about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2018-07-28 19:25:33 +0200
committerGitHub <noreply@github.com>2018-07-28 19:25:33 +0200
commitbb71538bb503159177d46d8956bd466973c0876b (patch)
tree41e39f53b365d91f83cfe393d75ddf8a1624ded9 /app
parente23b26178a71f90d64fe2a3e9e4468f265ecc71c (diff)
Redesign public profiles and toots (#8068)
Diffstat (limited to 'app')
-rw-r--r--app/controllers/concerns/account_controller_concern.rb5
-rw-r--r--app/controllers/statuses_controller.rb5
-rw-r--r--app/helpers/stream_entries_helper.rb46
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.js50
-rw-r--r--app/javascript/packs/public.js45
-rw-r--r--app/javascript/styles/application.scss2
-rw-r--r--app/javascript/styles/mastodon/accounts.scss559
-rw-r--r--app/javascript/styles/mastodon/basics.scss5
-rw-r--r--app/javascript/styles/mastodon/components.scss12
-rw-r--r--app/javascript/styles/mastodon/containers.scss573
-rw-r--r--app/javascript/styles/mastodon/footer.scss157
-rw-r--r--app/javascript/styles/mastodon/landing_strip.scss111
-rw-r--r--app/javascript/styles/mastodon/stream_entries.scss390
-rw-r--r--app/javascript/styles/mastodon/variables.scss2
-rw-r--r--app/javascript/styles/mastodon/widgets.scss123
-rw-r--r--app/models/concerns/account_header.rb5
-rw-r--r--app/views/accounts/_bio.html.haml15
-rw-r--r--app/views/accounts/_follow_button.html.haml28
-rw-r--r--app/views/accounts/_follow_grid.html.haml8
-rw-r--r--app/views/accounts/_follow_grid_hidden.html.haml3
-rw-r--r--app/views/accounts/_grid_card.html.haml12
-rw-r--r--app/views/accounts/_header.html.haml82
-rw-r--r--app/views/accounts/_moved.html.haml (renamed from app/views/accounts/_moved_strip.html.haml)11
-rw-r--r--app/views/accounts/_nothing_here.html.haml1
-rw-r--r--app/views/accounts/show.html.haml69
-rw-r--r--app/views/application/_card.html.haml16
-rw-r--r--app/views/application/_sidebar.html.haml6
-rw-r--r--app/views/auth/registrations/new.html.haml2
-rw-r--r--app/views/authorize_follows/_card.html.haml23
-rw-r--r--app/views/authorize_follows/show.html.haml2
-rw-r--r--app/views/authorize_follows/success.html.haml2
-rw-r--r--app/views/follower_accounts/index.html.haml9
-rw-r--r--app/views/following_accounts/index.html.haml9
-rw-r--r--app/views/layouts/public.html.haml55
-rw-r--r--app/views/remote_follow/new.html.haml2
-rw-r--r--app/views/remote_unfollows/success.html.haml2
-rw-r--r--app/views/settings/migrations/show.html.haml2
-rw-r--r--app/views/settings/profiles/show.html.haml3
-rw-r--r--app/views/shared/_landing_strip.html.haml6
-rw-r--r--app/views/stream_entries/_content_spoiler.html.haml7
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml34
-rw-r--r--app/views/stream_entries/_media.html.haml4
-rw-r--r--app/views/stream_entries/_more.html.haml2
-rw-r--r--app/views/stream_entries/_simple_status.html.haml34
-rw-r--r--app/views/stream_entries/_status.html.haml23
-rw-r--r--app/views/stream_entries/embed.html.haml2
-rw-r--r--app/views/stream_entries/show.html.haml11
47 files changed, 1361 insertions, 1214 deletions
diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb
index 5b9981aa2..6c27ef330 100644
--- a/app/controllers/concerns/account_controller_concern.rb
+++ b/app/controllers/concerns/account_controller_concern.rb
@@ -8,6 +8,7 @@ module AccountControllerConcern
   included do
     layout 'public'
     before_action :set_account
+    before_action :set_instance_presenter
     before_action :set_link_headers
     before_action :check_account_suspension
   end
@@ -18,6 +19,10 @@ module AccountControllerConcern
     @account = Account.find_local!(params[:account_username])
   end
 
+  def set_instance_presenter
+    @instance_presenter = InstancePresenter.new
+  end
+
   def set_link_headers
     response.headers['Link'] = LinkHeader.new(
       [
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index b85341822..755af1deb 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -12,6 +12,7 @@ class StatusesController < ApplicationController
 
   before_action :set_account
   before_action :set_status
+  before_action :set_instance_presenter
   before_action :set_link_headers
   before_action :check_account_suspension
   before_action :redirect_to_original, only: [:show]
@@ -148,6 +149,10 @@ class StatusesController < ApplicationController
     raise ActiveRecord::RecordNotFound
   end
 
+  def set_instance_presenter
+    @instance_presenter = InstancePresenter.new
+  end
+
   def check_account_suspension
     gone if @account.suspended?
   end
diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb
index 05cea73d7..121644263 100644
--- a/app/helpers/stream_entries_helper.rb
+++ b/app/helpers/stream_entries_helper.rb
@@ -12,6 +12,52 @@ module StreamEntriesHelper
     end
   end
 
+  def account_action_button(account)
+    if user_signed_in?
+      if account.id == current_user.account_id
+        link_to settings_profile_url, class: 'button logo-button' do
+          safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('settings.edit_profile')])
+        end
+      elsif current_account.following?(account) || current_account.requested?(account)
+        link_to account_unfollow_path(account), class: 'button logo-button', data: { method: :post } do
+          safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
+        end
+      else
+        link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do
+          safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
+        end
+      end
+    else
+      link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
+        safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
+      end
+    end
+  end
+
+  def account_badge(account)
+    if account.bot?
+      content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
+    elsif Setting.show_staff_badge && account.user_staff?
+      content_tag(:div, class: 'roles') do
+        if account.user_admin?
+          content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
+        elsif account.user_moderator?
+          content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
+        end
+      end
+    end
+  end
+
+  def link_to_more(url)
+    link_to t('statuses.show_more'), url, class: 'load-more load-gap'
+  end
+
+  def nothing_here(extra_classes = '')
+    content_tag(:div, class: "nothing-here #{extra_classes}") do
+      t('accounts.nothing_here')
+    end
+  end
+
   def account_description(account)
     prepend_str = [
       [
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
index 3c8db7092..9609714a1 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -60,6 +60,32 @@ const getUnitDelay = units => {
   }
 };
 
+export const timeAgoString = (intl, date, now, year) => {
+  const delta = now - date.getTime();
+
+  let relativeTime;
+
+  if (delta < 10 * SECOND) {
+    relativeTime = intl.formatMessage(messages.just_now);
+  } else if (delta < 7 * DAY) {
+    if (delta < MINUTE) {
+      relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
+    } else if (delta < HOUR) {
+      relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
+    } else if (delta < DAY) {
+      relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
+    } else {
+      relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
+    }
+  } else if (date.getFullYear() === year) {
+    relativeTime = intl.formatDate(date, shortDateFormatOptions);
+  } else {
+    relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
+  }
+
+  return relativeTime;
+};
+
 @injectIntl
 export default class RelativeTimestamp extends React.Component {
 
@@ -121,28 +147,8 @@ export default class RelativeTimestamp extends React.Component {
   render () {
     const { timestamp, intl, year } = this.props;
 
-    const date  = new Date(timestamp);
-    const delta = this.state.now - date.getTime();
-
-    let relativeTime;
-
-    if (delta < 10 * SECOND) {
-      relativeTime = intl.formatMessage(messages.just_now);
-    } else if (delta < 7 * DAY) {
-      if (delta < MINUTE) {
-        relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
-      } else if (delta < HOUR) {
-        relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
-      } else if (delta < DAY) {
-        relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
-      } else {
-        relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
-      }
-    } else if (date.getFullYear() === year) {
-      relativeTime = intl.formatDate(date, shortDateFormatOptions);
-    } else {
-      relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
-    }
+    const date         = new Date(timestamp);
+    const relativeTime = timeAgoString(intl, date, this.state.now, year);
 
     return (
       <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 4b1e87ae3..855e56ee6 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -22,15 +22,15 @@ window.addEventListener('message', e => {
 
 function main() {
   const { length } = require('stringz');
-  const IntlRelativeFormat = require('intl-relativeformat').default;
+  const IntlMessageFormat = require('intl-messageformat').default;
+  const { timeAgoString } = require('../mastodon/components/relative_timestamp');
   const { delegate } = require('rails-ujs');
   const emojify = require('../mastodon/features/emoji/emoji').default;
   const { getLocale } = require('../mastodon/locales');
-  const { localeData } = getLocale();
+  const { messages } = getLocale();
   const React = require('react');
   const ReactDOM = require('react-dom');
-
-  localeData.forEach(IntlRelativeFormat.__addLocaleData);
+  const Rellax = require('rellax');
 
   ready(() => {
     const locale = document.documentElement.lang;
@@ -43,8 +43,6 @@ function main() {
       minute: 'numeric',
     });
 
-    const relativeFormat = new IntlRelativeFormat(locale);
-
     [].forEach.call(document.querySelectorAll('.emojify'), (content) => {
       content.innerHTML = emojify(content.innerHTML);
     });
@@ -59,12 +57,16 @@ function main() {
 
     [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
       const datetime = new Date(content.getAttribute('datetime'));
+      const now      = new Date();
 
       content.title = dateTimeFormat.format(datetime);
-      content.textContent = relativeFormat.format(datetime);
+      content.textContent = timeAgoString({
+        formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
+        formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
+      }, datetime, now, datetime.getFullYear());
     });
 
-    [].forEach.call(document.querySelectorAll('.logo-button'), (content) => {
+    [].forEach.call(document.querySelectorAll('.modal-button'), (content) => {
       content.addEventListener('click', (e) => {
         e.preventDefault();
         window.open(e.target.href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
@@ -82,6 +84,8 @@ function main() {
         })
         .catch(error => console.error(error));
     }
+
+    new Rellax('.parallax', { speed: -1 });
   });
 
   delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
@@ -106,15 +110,20 @@ function main() {
     return false;
   });
 
-  delegate(document, '.account_display_name', 'input', ({ target }) => {
+  delegate(document, '#account_display_name', 'input', ({ target }) => {
     const nameCounter = document.querySelector('.name-counter');
+    const name        = document.querySelector('.card .display-name strong');
 
     if (nameCounter) {
       nameCounter.textContent = 30 - length(target.value);
     }
+
+    if (name) {
+      name.innerHTML = emojify(target.value);
+    }
   });
 
-  delegate(document, '.account_note', 'input', ({ target }) => {
+  delegate(document, '#account_note', 'input', ({ target }) => {
     const noteCounter = document.querySelector('.note-counter');
 
     if (noteCounter) {
@@ -123,7 +132,7 @@ function main() {
   });
 
   delegate(document, '#account_avatar', 'change', ({ target }) => {
-    const avatar = document.querySelector('.card.compact .avatar img');
+    const avatar = document.querySelector('.card .avatar img');
     const [file] = target.files || [];
     const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
 
@@ -131,11 +140,21 @@ function main() {
   });
 
   delegate(document, '#account_header', 'change', ({ target }) => {
-    const header = document.querySelector('.card.compact');
+    const header = document.querySelector('.card .card__img img');
     const [file] = target.files || [];
     const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
 
-    header.style.backgroundImage = `url(${url})`;
+    header.src = url;
+  });
+
+  delegate(document, '#account_locked', 'change', ({ target }) => {
+    const lock = document.querySelector('.card .display-name i');
+
+    if (target.checked) {
+      lock.style.display = 'inline';
+    } else {
+      lock.style.display = 'none';
+    }
   });
 }
 
diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss
index 7b3b10dfe..0990a4f25 100644
--- a/app/javascript/styles/application.scss
+++ b/app/javascript/styles/application.scss
@@ -10,7 +10,7 @@
 @import 'mastodon/lists';
 @import 'mastodon/footer';
 @import 'mastodon/compact_header';
-@import 'mastodon/landing_strip';
+@import 'mastodon/widgets';
 @import 'mastodon/forms';
 @import 'mastodon/accounts';
 @import 'mastodon/stream_entries';
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index b4612b063..c27bc0df3 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -1,243 +1,100 @@
 .card {
-  background-color: $base-shadow-color;
-  background-size: cover;
-  background-position: center;
-  border-radius: 4px 4px 0 0;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  overflow: hidden;
-  position: relative;
-  display: flex;
-
-  &::after {
-    background: rgba(darken($ui-base-color, 8%), 0.5);
+  & > a {
     display: block;
-    content: "";
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 1;
-  }
-
-  @media screen and (max-width: 740px) {
-    border-radius: 0;
-    box-shadow: none;
-  }
-
-  .card__illustration {
-    padding: 60px 0;
-    position: relative;
-    flex: 1 1 auto;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  .card__bio {
-    max-width: 260px;
-    flex: 1 1 auto;
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    background: rgba(darken($ui-base-color, 8%), 0.8);
-    position: relative;
-    z-index: 2;
-  }
-
-  &.compact {
-    padding: 30px 0;
-    border-radius: 4px;
-
-    .avatar {
-      margin-bottom: 0;
+    text-decoration: none;
+    color: inherit;
+    box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
 
-      img {
-        object-fit: cover;
-      }
+    @media screen and (max-width: $no-gap-breakpoint) {
+      box-shadow: none;
     }
-  }
-
-  .name {
-    display: block;
-    font-size: 20px;
-    line-height: 18px * 1.5;
-    color: $primary-text-color;
-    padding: 10px 15px;
-    padding-bottom: 0;
-    font-weight: 500;
-    position: relative;
-    z-index: 2;
-    margin-bottom: 30px;
-    overflow: hidden;
-    text-overflow: ellipsis;
 
-    small {
-      display: block;
-      font-size: 14px;
-      color: $highlight-text-color;
-      font-weight: 400;
-      overflow: hidden;
-      text-overflow: ellipsis;
-
-      .fa {
-        margin-left: 3px;
+    &:hover,
+    &:active,
+    &:focus {
+      .card__bar {
+        background: lighten($ui-base-color, 8%);
       }
     }
   }
 
-  .avatar {
-    width: 120px;
-    margin: 0 auto;
+  &__img {
+    height: 130px;
     position: relative;
-    z-index: 2;
+    background: darken($ui-base-color, 12%);
+    border-radius: 4px 4px 0 0;
 
     img {
-      width: 120px;
-      height: 120px;
-      display: block;
-      border-radius: 120px;
-      box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-    }
-  }
-
-  .roles {
-    margin-bottom: 30px;
-    padding: 0 15px;
-  }
-
-  .details-counters {
-    margin-top: 30px;
-    display: flex;
-    flex-direction: row;
-    width: 100%;
-  }
-
-  .counter {
-    width: 33.3%;
-    box-sizing: border-box;
-    flex: 0 0 auto;
-    color: $darker-text-color;
-    padding: 5px 10px 0;
-    margin-bottom: 10px;
-    border-right: 1px solid lighten($ui-base-color, 4%);
-    cursor: default;
-    text-align: center;
-    position: relative;
-
-    a {
-      display: block;
-    }
-
-    &:last-child {
-      border-right: 0;
-    }
-
-    &::after {
       display: block;
-      content: "";
-      position: absolute;
-      bottom: -10px;
-      left: 0;
       width: 100%;
-      border-bottom: 4px solid $ui-primary-color;
-      opacity: 0.5;
-      transition: all 400ms ease;
-    }
-
-    &.active {
-      &::after {
-        border-bottom: 4px solid $highlight-text-color;
-        opacity: 1;
-      }
-    }
-
-    &:hover {
-      &::after {
-        opacity: 1;
-        transition-duration: 100ms;
-      }
+      height: 100%;
+      margin: 0;
+      object-fit: cover;
+      border-radius: 4px 4px 0 0;
     }
 
-    a {
-      text-decoration: none;
-      color: inherit;
+    @media screen and (max-width: 600px) {
+      height: 200px;
     }
 
-    .counter-label {
-      font-size: 12px;
-      display: block;
-      margin-bottom: 5px;
-    }
-
-    .counter-number {
-      font-weight: 500;
-      font-size: 18px;
-      color: $primary-text-color;
-      font-family: 'mastodon-font-display', sans-serif;
+    @media screen and (max-width: $no-gap-breakpoint) {
+      display: none;
     }
   }
 
-  .bio {
-    font-size: 14px;
-    line-height: 18px;
-    padding: 0 15px;
-    color: $secondary-text-color;
-  }
-
-  @media screen and (max-width: 480px) {
-    display: block;
+  &__bar {
+    position: relative;
+    padding: 15px;
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    background: lighten($ui-base-color, 4%);
+    border-radius: 0 0 4px 4px;
 
-    .card__bio {
-      max-width: none;
+    @media screen and (max-width: $no-gap-breakpoint) {
+      border-radius: 0;
     }
 
-    .name,
-    .roles {
-      text-align: center;
-      margin-bottom: 15px;
-    }
+    .avatar {
+      flex: 0 0 auto;
+      width: 48px;
+      height: 48px;
+      padding-top: 2px;
 
-    .bio {
-      margin-bottom: 15px;
+      img {
+        width: 100%;
+        height: 100%;
+        display: block;
+        margin: 0;
+        border-radius: 4px;
+        background: darken($ui-base-color, 8%);
+      }
     }
-  }
-}
 
-.card,
-.account-grid-card {
-  .controls {
-    position: absolute;
-    top: 15px;
-    left: 15px;
-    z-index: 2;
-
-    .icon-button {
-      color: rgba($white, 0.8);
-      text-decoration: none;
-      font-size: 13px;
-      line-height: 13px;
-      font-weight: 500;
-
-      .fa {
-        font-weight: 400;
-        margin-right: 5px;
+    .display-name {
+      margin-left: 15px;
+      text-align: left;
+
+      strong {
+        font-size: 15px;
+        color: $primary-text-color;
+        font-weight: 500;
+        overflow: hidden;
+        text-overflow: ellipsis;
       }
 
-      &:hover,
-      &:active,
-      &:focus {
-        color: $white;
+      span {
+        display: block;
+        font-size: 14px;
+        color: $darker-text-color;
+        font-weight: 400;
+        overflow: hidden;
+        text-overflow: ellipsis;
       }
     }
   }
 }
 
-.account-grid-card .controls {
-  left: auto;
-  right: 15px;
-}
-
 .pagination {
   padding: 30px 0;
   text-align: center;
@@ -314,289 +171,23 @@
   }
 }
 
-.accounts-grid {
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  background: darken($simple-background-color, 8%);
-  border-radius: 0 0 4px 4px;
-  padding: 20px 5px;
-  padding-bottom: 10px;
-  overflow: hidden;
-  display: flex;
-  flex-wrap: wrap;
-  z-index: 2;
-  position: relative;
-
-  &.empty img {
-    position: absolute;
-    opacity: 0.2;
-    height: 200px;
-    left: 0;
-    bottom: 0;
-    pointer-events: none;
-  }
-
-  @media screen and (max-width: 740px) {
-    border-radius: 0;
-    box-shadow: none;
-  }
-
-  .account-grid-card {
-    box-sizing: border-box;
-    width: 335px;
-    background: $simple-background-color;
-    border-radius: 4px;
-    color: $inverted-text-color;
-    margin: 0 5px 10px;
-    position: relative;
-
-    @media screen and (max-width: 740px) {
-      width: calc(100% - 10px);
-    }
-
-    .account-grid-card__header {
-      overflow: hidden;
-      height: 100px;
-      border-radius: 4px 4px 0 0;
-      background-color: lighten($inverted-text-color, 4%);
-      background-size: cover;
-      background-position: center;
-      position: relative;
-
-      &::after {
-        background: rgba(darken($ui-base-color, 8%), 0.5);
-        display: block;
-        content: "";
-        position: absolute;
-        left: 0;
-        top: 0;
-        width: 100%;
-        height: 100%;
-        z-index: 1;
-      }
-    }
-
-    .account-grid-card__avatar {
-      box-sizing: border-box;
-      padding: 15px;
-      position: absolute;
-      z-index: 2;
-      top: 100px - (40px + 2px);
-      left: -2px;
-    }
-
-    .avatar {
-      width: 80px;
-      height: 80px;
-
-      img {
-        display: block;
-        width: 80px;
-        height: 80px;
-        border-radius: 80px;
-        border: 2px solid $simple-background-color;
-        background: $simple-background-color;
-      }
-    }
-
-    .name {
-      padding: 15px;
-      padding-top: 10px;
-      padding-left: 15px + 80px + 15px;
-
-      a {
-        display: block;
-        color: $inverted-text-color;
-        text-decoration: none;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        font-weight: 500;
-
-        &:hover {
-          .display_name {
-            text-decoration: underline;
-          }
-        }
-      }
-    }
-
-    .display_name {
-      font-size: 16px;
-      display: block;
-      text-overflow: ellipsis;
-      overflow: hidden;
-    }
-
-    .username {
-      color: $lighter-text-color;
-      font-size: 14px;
-      font-weight: 400;
-    }
-
-    .account__header__content {
-      padding: 10px 15px;
-      padding-top: 15px;
-      color: $lighter-text-color;
-      word-wrap: break-word;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      height: 5.5em;
-      position: relative;
-
-      &::after {
-        display: block;
-        content: "";
-        width: 100%;
-        height: 100px;
-        position: absolute;
-        bottom: 0;
-        background: linear-gradient(to bottom, rgba($simple-background-color, 0.01) 0%, rgba($simple-background-color, 1) 100%);
-        left: 0;
-        border-radius: 0 0 4px 4px;
-        pointer-events: none;
-      }
-    }
-  }
-}
-
 .nothing-here {
-  width: 100%;
-  display: block;
+  background: $ui-base-color;
+  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
   color: $light-text-color;
   font-size: 14px;
   font-weight: 500;
   text-align: center;
-  padding: 130px 0;
-  padding-top: 125px;
-  margin: 0 auto;
+  display: flex;
+  justify-content: center;
+  align-items: center;
   cursor: default;
-}
-
-.account-card {
   border-radius: 4px;
-  text-align: left;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  background: $simple-background-color;
-
-  &__header {
-    background: $base-shadow-color;
-    background-size: cover;
-    background-position: center center;
-    height: 90px;
-    border-radius: 4px 4px 0 0;
-  }
+  padding: 20px;
+  min-height: 30vh;
 
-  & > .detailed-status__display-name {
-    display: block;
-    overflow: hidden;
-    display: flex;
-    align-items: center;
-    padding: 10px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-
-    & > div:first-child {
-      flex: 0 0 auto;
-      margin-right: 10px;
-      width: 48px;
-      height: 48px;
-    }
-
-    .avatar {
-      display: block;
-      border-radius: 4px;
-      margin: 0;
-    }
-
-    .display-name {
-      flex: 1 0 auto;
-      display: block;
-      max-width: 100%;
-      overflow: hidden;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-      cursor: default;
-
-      & > .detailed-status__display-name {
-        margin-bottom: 0;
-      }
-
-      strong {
-        font-weight: 500;
-        color: $ui-base-color;
-
-        @each $lang in $cjk-langs {
-          &:lang(#{$lang}) {
-            font-weight: 700;
-          }
-        }
-      }
-
-      span {
-        font-size: 14px;
-        color: $light-text-color;
-      }
-    }
-
-    &:hover {
-      .display-name {
-        strong {
-          text-decoration: none;
-        }
-      }
-    }
-  }
-
-  .counter {
-    box-sizing: border-box;
-    flex: 0 0 auto;
-    color: $light-text-color;
-    padding: 0 10px;
-    cursor: default;
-    text-align: center;
-    position: relative;
-    line-height: 24px;
-
-    .counter-label {
-      font-size: 12px;
-      display: block;
-      text-transform: uppercase;
-    }
-
-    .counter-number {
-      font-weight: 500;
-      font-size: 16px;
-      color: $inverted-text-color;
-      font-family: 'mastodon-font-display', sans-serif;
-    }
-  }
-}
-
-.activity-stream-tabs {
-  background: $simple-background-color;
-  border-bottom: 1px solid $ui-secondary-color;
-  position: relative;
-  z-index: 2;
-
-  a {
-    display: inline-block;
-    padding: 15px;
-    text-decoration: none;
-    color: $highlight-text-color;
-    text-transform: uppercase;
-    font-weight: 500;
-
-    &:hover,
-    &:active,
-    &:focus {
-      color: lighten($highlight-text-color, 8%);
-    }
-
-    &.active {
-      color: $inverted-text-color;
-      cursor: default;
-    }
+  &--under-tabs {
+    border-radius: 0 0 4px 4px;
   }
 }
 
@@ -629,14 +220,14 @@
   padding: 0;
   margin: 15px -15px -15px;
   border: 0 none;
-  border-top: 1px solid lighten($ui-base-color, 4%);
-  border-bottom: 1px solid lighten($ui-base-color, 4%);
+  border-top: 1px solid lighten($ui-base-color, 12%);
+  border-bottom: 1px solid lighten($ui-base-color, 12%);
   font-size: 14px;
   line-height: 20px;
 
   dl {
     display: flex;
-    border-bottom: 1px solid lighten($ui-base-color, 4%);
+    border-bottom: 1px solid lighten($ui-base-color, 12%);
   }
 
   dt,
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index c52e069be..c45a50756 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -1,13 +1,12 @@
 body {
   font-family: 'mastodon-font-sans-serif', sans-serif;
-  background: $ui-base-color;
+  background: darken($ui-base-color, 8%);
   background-size: cover;
   background-attachment: fixed;
   font-size: 13px;
   line-height: 18px;
   font-weight: 400;
   color: $primary-text-color;
-  padding-bottom: 20px;
   text-rendering: optimizelegibility;
   font-feature-settings: "kern";
   text-size-adjust: none;
@@ -52,7 +51,7 @@ body {
   }
 
   &.embed {
-    background: transparent;
+    background: lighten($ui-base-color, 4%);
     margin: 0;
     padding-bottom: 0;
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 846bda893..814739dfc 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -946,6 +946,18 @@
   background: lighten($ui-base-color, 4%);
   padding: 14px 10px;
 
+  &--flex {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    align-items: flex-start;
+
+    .status__content,
+    .detailed-status__meta {
+      flex: 100%;
+    }
+  }
+
   .status__content {
     font-size: 19px;
     line-height: 24px;
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index ac648c868..c2ff77783 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -118,3 +118,576 @@
     margin-left: 8px;
   }
 }
+
+.public-layout {
+  @media screen and (max-width: $no-gap-breakpoint) {
+    padding-top: 48px;
+  }
+
+  .container {
+    max-width: 960px;
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      padding: 0;
+    }
+  }
+
+  .header {
+    background: lighten($ui-base-color, 8%);
+    box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+    border-radius: 4px;
+    height: 48px;
+    margin: 10px 0;
+    display: flex;
+    align-items: stretch;
+    justify-content: center;
+    flex-wrap: nowrap;
+    overflow: hidden;
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      position: fixed;
+      width: 100%;
+      top: 0;
+      left: 0;
+      margin: 0;
+      border-radius: 0;
+      box-shadow: none;
+      z-index: 110;
+    }
+
+    & > div {
+      flex: 1 1 33.3%;
+      min-height: 1px;
+    }
+
+    .nav-left {
+      display: flex;
+      align-items: stretch;
+      justify-content: flex-start;
+      flex-wrap: nowrap;
+    }
+
+    .nav-center {
+      display: flex;
+      align-items: stretch;
+      justify-content: center;
+      flex-wrap: nowrap;
+    }
+
+    .nav-right {
+      display: flex;
+      align-items: stretch;
+      justify-content: flex-end;
+      flex-wrap: nowrap;
+    }
+
+    .brand {
+      display: block;
+      padding: 15px;
+
+      img {
+        display: block;
+        height: 18px;
+        width: auto;
+        position: relative;
+        bottom: -2px;
+
+        @media screen and (max-width: $no-gap-breakpoint) {
+          height: 20px;
+        }
+      }
+
+      &:hover,
+      &:focus,
+      &:active {
+        background: lighten($ui-base-color, 12%);
+      }
+    }
+
+    .nav-link {
+      display: flex;
+      align-items: center;
+      padding: 0 1rem;
+      font-size: 12px;
+      font-weight: 500;
+      text-decoration: none;
+      color: $darker-text-color;
+      white-space: nowrap;
+      text-align: center;
+
+      &:hover,
+      &:focus,
+      &:active {
+        text-decoration: underline;
+        color: $primary-text-color;
+      }
+    }
+
+    .nav-button {
+      background: lighten($ui-base-color, 16%);
+      margin: 8px;
+      margin-left: 0;
+      border-radius: 4px;
+
+      &:hover,
+      &:focus,
+      &:active {
+        text-decoration: none;
+        background: lighten($ui-base-color, 20%);
+      }
+    }
+  }
+
+  $no-columns-breakpoint: 600px;
+
+  .grid {
+    display: grid;
+    grid-gap: 10px;
+    grid-template-columns: minmax(300px, 3fr) minmax(298px, 1fr);
+    grid-auto-columns: 25%;
+    grid-auto-rows: max-content;
+
+    .column-0 {
+      grid-row: 1;
+      grid-column: 1;
+    }
+
+    .column-1 {
+      grid-row: 1;
+      grid-column: 2;
+    }
+
+    @media screen and (max-width: $no-columns-breakpoint) {
+      grid-template-columns: 100%;
+      grid-gap: 0;
+
+      .column-1 {
+        display: none;
+      }
+    }
+  }
+
+  .public-account-header {
+    overflow: hidden;
+    margin-bottom: 10px;
+    box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+
+    &__image {
+      border-radius: 4px 4px 0 0;
+      overflow: hidden;
+      height: 300px;
+      position: relative;
+      background: darken($ui-base-color, 12%);
+
+      &::after {
+        content: "";
+        display: block;
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        box-shadow: inset 0 -1px 1px 1px rgba($base-shadow-color, 0.15);
+        top: 0;
+        left: 0;
+      }
+
+      img {
+        object-fit: cover;
+        display: block;
+        width: 100%;
+        height: 100%;
+        margin: 0;
+        border-radius: 4px 4px 0 0;
+      }
+
+      @media screen and (max-width: 600px) {
+        height: 200px;
+      }
+    }
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      margin-bottom: 0;
+      box-shadow: none;
+
+      &__image::after {
+        display: none;
+      }
+
+      &__image,
+      &__image img {
+        border-radius: 0;
+      }
+    }
+
+    &__bar {
+      position: relative;
+      margin-top: -80px;
+      display: flex;
+      justify-content: flex-start;
+
+      &::before {
+        content: "";
+        display: block;
+        background: lighten($ui-base-color, 4%);
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        height: 60px;
+        border-radius: 0 0 4px 4px;
+        z-index: -1;
+      }
+
+      .avatar {
+        display: block;
+        width: 120px;
+        height: 120px;
+        padding-left: 20px - 4px;
+        flex: 0 0 auto;
+
+        img {
+          display: block;
+          width: 100%;
+          height: 100%;
+          margin: 0;
+          border-radius: 50%;
+          border: 4px solid lighten($ui-base-color, 4%);
+          background: darken($ui-base-color, 8%);
+        }
+      }
+
+      @media screen and (max-width: 600px) {
+        margin-top: 0;
+        background: lighten($ui-base-color, 4%);
+        border-radius: 0 0 4px 4px;
+        padding: 5px;
+
+        &::before {
+          display: none;
+        }
+
+        .avatar {
+          width: 48px;
+          height: 48px;
+          padding: 7px 0;
+          padding-left: 10px;
+
+          img {
+            border: 0;
+            border-radius: 4px;
+          }
+
+          @media screen and (max-width: 360px) {
+            display: none;
+          }
+        }
+      }
+
+      @media screen and (max-width: $no-gap-breakpoint) {
+        border-radius: 0;
+      }
+
+      @media screen and (max-width: $no-columns-breakpoint) {
+        flex-wrap: wrap;
+      }
+    }
+
+    &__tabs {
+      flex: 1 1 auto;
+      margin-left: 20px;
+
+      &__name {
+        padding-top: 20px;
+        padding-bottom: 8px;
+
+        h1 {
+          font-size: 20px;
+          line-height: 18px * 1.5;
+          color: $primary-text-color;
+          font-weight: 500;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          text-shadow: 1px 1px 1px $base-shadow-color;
+
+          small {
+            display: block;
+            font-size: 14px;
+            color: $primary-text-color;
+            font-weight: 400;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
+      }
+
+      @media screen and (max-width: 600px) {
+        margin-left: 15px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        &__name {
+          padding-top: 0;
+          padding-bottom: 0;
+
+          h1 {
+            font-size: 16px;
+            line-height: 24px;
+            text-shadow: none;
+
+            small {
+              color: $darker-text-color;
+            }
+          }
+        }
+      }
+
+      &__tabs {
+        display: flex;
+        justify-content: flex-start;
+        align-items: stretch;
+        height: 58px;
+
+        .details-counters {
+          display: flex;
+          flex-direction: row;
+          min-width: 300px;
+        }
+
+        @media screen and (max-width: $no-columns-breakpoint) {
+          .details-counters {
+            display: none;
+          }
+        }
+
+        .counter {
+          width: 33.3%;
+          box-sizing: border-box;
+          flex: 0 0 auto;
+          color: $darker-text-color;
+          padding: 10px;
+          border-right: 1px solid lighten($ui-base-color, 4%);
+          cursor: default;
+          text-align: center;
+          position: relative;
+
+          a {
+            display: block;
+          }
+
+          &:last-child {
+            border-right: 0;
+          }
+
+          &::after {
+            display: block;
+            content: "";
+            position: absolute;
+            bottom: 0;
+            left: 0;
+            width: 100%;
+            border-bottom: 4px solid $ui-primary-color;
+            opacity: 0.5;
+            transition: all 400ms ease;
+          }
+
+          &.active {
+            &::after {
+              border-bottom: 4px solid $highlight-text-color;
+              opacity: 1;
+            }
+          }
+
+          &:hover {
+            &::after {
+              opacity: 1;
+              transition-duration: 100ms;
+            }
+          }
+
+          a {
+            text-decoration: none;
+            color: inherit;
+          }
+
+          .counter-label {
+            font-size: 12px;
+            display: block;
+          }
+
+          .counter-number {
+            font-weight: 500;
+            font-size: 18px;
+            margin-bottom: 5px;
+            color: $primary-text-color;
+            font-family: 'mastodon-font-display', sans-serif;
+          }
+        }
+
+        .spacer {
+          flex: 1 1 auto;
+          height: 1px;
+        }
+
+        &__buttons {
+          padding: 7px 8px;
+        }
+      }
+    }
+
+    &__extra {
+      display: none;
+      margin-top: 4px;
+
+      .public-account-bio {
+        border-radius: 0;
+        box-shadow: none;
+        background: transparent;
+        margin: 0 -5px;
+
+        .account__header__fields {
+          border-top: 1px solid lighten($ui-base-color, 12%);
+        }
+
+        .roles {
+          display: none;
+        }
+      }
+
+      &__links {
+        margin-top: -15px;
+        font-size: 14px;
+        color: $darker-text-color;
+
+        a {
+          display: inline-block;
+          color: $darker-text-color;
+          text-decoration: none;
+          padding: 15px;
+
+          strong {
+            font-weight: 700;
+            color: $primary-text-color;
+          }
+        }
+      }
+
+      @media screen and (max-width: $no-columns-breakpoint) {
+        display: block;
+        flex: 100%;
+      }
+    }
+  }
+
+  .account__section-headline {
+    border-radius: 4px 4px 0 0;
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      border-radius: 0;
+    }
+  }
+
+  .detailed-status__meta {
+    margin-top: 25px;
+  }
+
+  .public-account-bio {
+    background: lighten($ui-base-color, 8%);
+    box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+    border-radius: 4px;
+    overflow: hidden;
+    margin-bottom: 10px;
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      box-shadow: none;
+      margin-bottom: 0;
+      border-radius: 0;
+    }
+
+    .account__header__fields {
+      margin: 0;
+      border-top: 0;
+
+      a {
+        color: lighten($ui-highlight-color, 8%);
+      }
+    }
+
+    .account__header__content {
+      padding: 20px;
+      padding-bottom: 0;
+      color: $primary-text-color;
+    }
+
+    &__extra,
+    .roles {
+      padding: 20px;
+      font-size: 14px;
+      color: $darker-text-color;
+    }
+
+    .roles {
+      padding-bottom: 0;
+    }
+  }
+
+  .static-icon-button {
+    color: $action-button-color;
+    font-size: 18px;
+
+    & > span {
+      font-size: 14px;
+      font-weight: 500;
+    }
+  }
+
+  .card-grid {
+    display: flex;
+    flex-wrap: wrap;
+    min-width: 100%;
+    margin: 0 -5px;
+
+    & > div {
+      box-sizing: border-box;
+      flex: 1 0 auto;
+      width: 300px;
+      padding: 0 5px;
+      margin-bottom: 10px;
+      max-width: 33.333%;
+
+      @media screen and (max-width: 900px) {
+        max-width: 50%;
+      }
+
+      @media screen and (max-width: 600px) {
+        max-width: 100%;
+      }
+    }
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      margin: 0;
+      border-top: 1px solid lighten($ui-base-color, 8%);
+
+      & > div {
+        width: 100%;
+        padding: 0;
+        margin-bottom: 0;
+        border-bottom: 1px solid lighten($ui-base-color, 8%);
+
+        &:last-child {
+          border-bottom: 0;
+        }
+
+        .card__bar {
+          background: $ui-base-color;
+
+          &:hover,
+          &:active,
+          &:focus {
+            background: lighten($ui-base-color, 4%);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss
index 81eb1ce2d..4d75477e0 100644
--- a/app/javascript/styles/mastodon/footer.scss
+++ b/app/javascript/styles/mastodon/footer.scss
@@ -1,39 +1,140 @@
-.footer {
-  text-align: center;
-  margin-top: 30px;
-  padding-bottom: 60px;
-  font-size: 12px;
-  color: $darker-text-color;
-
-  .footer__domain {
-    font-weight: 500;
-
-    a {
-      color: inherit;
-      text-decoration: none;
+.public-layout {
+  .footer {
+    text-align: left;
+    padding-top: 20px;
+    padding-bottom: 60px;
+    font-size: 12px;
+    color: lighten($ui-base-color, 34%);
+
+    @media screen and (max-width: $no-gap-breakpoint) {
+      padding-left: 20px;
+      padding-right: 20px;
     }
-  }
 
-  .powered-by,
-  .single-user-login {
-    font-weight: 400;
+    .grid {
+      display: grid;
+      grid-gap: 10px;
+      grid-template-columns: 1fr 1fr 2fr 1fr 1fr;
+
+      .column-0 {
+        grid-column: 1;
+        grid-row: 1;
+        min-width: 0;
+      }
+
+      .column-1 {
+        grid-column: 2;
+        grid-row: 1;
+        min-width: 0;
+      }
+
+      .column-2 {
+        grid-column: 3;
+        grid-row: 1;
+        min-width: 0;
+        text-align: center;
+
+        h4 a {
+          color: lighten($ui-base-color, 34%);
+        }
+      }
+
+      .column-3 {
+        grid-column: 4;
+        grid-row: 1;
+        min-width: 0;
+      }
+
+      .column-4 {
+        grid-column: 5;
+        grid-row: 1;
+        min-width: 0;
+      }
+
+      @media screen and (max-width: 690px) {
+        grid-template-columns: 1fr 2fr 1fr;
+
+        .column-0,
+        .column-1 {
+          grid-column: 1;
+        }
+
+        .column-1 {
+          grid-row: 2;
+        }
+
+        .column-2 {
+          grid-column: 2;
+        }
 
-    a {
-      color: inherit;
-      text-decoration: underline;
-      font-weight: 500;
+        .column-3,
+        .column-4 {
+          grid-column: 3;
+        }
 
-      &:hover {
+        .column-4 {
+          grid-row: 2;
+        }
+      }
+
+      @media screen and (max-width: 600px) {
+        .column-1 {
+          display: block;
+        }
+      }
+
+      @media screen and (max-width: $no-gap-breakpoint) {
+        .column-0,
+        .column-1,
+        .column-3,
+        .column-4 {
+          display: none;
+        }
+      }
+    }
+
+    h4 {
+      text-transform: uppercase;
+      font-weight: 700;
+      margin-bottom: 8px;
+      color: $darker-text-color;
+
+      a {
+        color: inherit;
         text-decoration: none;
       }
     }
 
-    img {
-      margin: 0 4px;
-      position: relative;
-      bottom: -1px;
-      height: 18px;
-      vertical-align: top;
+    ul a {
+      text-decoration: none;
+      color: lighten($ui-base-color, 34%);
+
+      &:hover,
+      &:active,
+      &:focus {
+        text-decoration: underline;
+      }
+    }
+
+    .brand {
+      svg {
+        display: block;
+        height: 36px;
+        width: auto;
+        margin: 0 auto;
+
+        path {
+          fill: lighten($ui-base-color, 34%);
+        }
+      }
+
+      &:hover,
+      &:focus,
+      &:active {
+        svg path {
+          fill: lighten($ui-base-color, 38%);
+        }
+      }
     }
   }
 }
diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss
deleted file mode 100644
index 86614b89b..000000000
--- a/app/javascript/styles/mastodon/landing_strip.scss
+++ /dev/null
@@ -1,111 +0,0 @@
-.landing-strip,
-.memoriam-strip {
-  background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $darker-text-color;
-  font-weight: 400;
-  padding: 14px;
-  border-radius: 4px;
-  margin-bottom: 20px;
-  display: flex;
-  align-items: center;
-
-  strong,
-  a {
-    font-weight: 500;
-
-    @each $lang in $cjk-langs {
-      &:lang(#{$lang}) {
-        font-weight: 700;
-      }
-    }
-  }
-
-  a {
-    color: inherit;
-    text-decoration: underline;
-  }
-
-  .logo {
-    width: 30px;
-    height: 30px;
-    flex: 0 0 auto;
-    margin-right: 15px;
-  }
-
-  @media screen and (max-width: 740px) {
-    margin-bottom: 0;
-  }
-}
-
-.memoriam-strip {
-  background: rgba($base-shadow-color, 0.7);
-}
-
-.moved-strip {
-  padding: 14px;
-  border-radius: 4px;
-  background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $secondary-text-color;
-  font-weight: 400;
-  margin-bottom: 20px;
-
-  strong,
-  a {
-    font-weight: 500;
-
-    @each $lang in $cjk-langs {
-      &:lang(#{$lang}) {
-        font-weight: 700;
-      }
-    }
-  }
-
-  a {
-    color: inherit;
-    text-decoration: underline;
-
-    &.mention {
-      text-decoration: none;
-
-      span {
-        text-decoration: none;
-      }
-
-      &:focus,
-      &:hover,
-      &:active {
-        text-decoration: none;
-
-        span {
-          text-decoration: underline;
-        }
-      }
-    }
-  }
-
-  &__message {
-    margin-bottom: 15px;
-
-    .fa {
-      margin-right: 5px;
-      color: $darker-text-color;
-    }
-  }
-
-  &__card {
-    .detailed-status__display-avatar {
-      position: relative;
-      cursor: pointer;
-    }
-
-    .detailed-status__display-name {
-      margin-bottom: 0;
-      text-decoration: none;
-
-      span {
-        color: $highlight-text-color;
-        font-weight: 400;
-      }
-    }
-  }
-}
diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss
index f4d6e237f..9e2aa720c 100644
--- a/app/javascript/styles/mastodon/stream_entries.scss
+++ b/app/javascript/styles/mastodon/stream_entries.scss
@@ -1,367 +1,145 @@
 .activity-stream {
-  clear: both;
   box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+  border-radius: 4px;
+  overflow: hidden;
+  margin-bottom: 10px;
+
+  @media screen and (max-width: $no-gap-breakpoint) {
+    margin-bottom: 0;
+    border-radius: 0;
+    box-shadow: none;
+  }
+
+  &--headless {
+    border-radius: 0;
+    margin: 0;
+    box-shadow: none;
+
+    .detailed-status,
+    .status {
+      border-radius: 0 !important;
+    }
+  }
 
   div[data-component] {
     width: 100%;
   }
 
   .entry {
-    background: $simple-background-color;
+    background: $ui-base-color;
 
-    .detailed-status.light,
-    .status.light,
-    .more.light {
-      border-bottom: 1px solid $ui-secondary-color;
+    .detailed-status,
+    .status,
+    .load-more {
       animation: none;
     }
 
     &:last-child {
-      &,
-      .detailed-status.light,
-      .status.light {
+      .detailed-status,
+      .status {
         border-bottom: 0;
         border-radius: 0 0 4px 4px;
       }
     }
 
     &:first-child {
-      &,
-      .detailed-status.light,
-      .status.light {
+      .detailed-status,
+      .status {
         border-radius: 4px 4px 0 0;
       }
 
       &:last-child {
-        &,
-        .detailed-status.light,
-        .status.light {
+        .detailed-status,
+        .status {
           border-radius: 4px;
         }
       }
     }
 
     @media screen and (max-width: 740px) {
-      &,
-      .detailed-status.light,
-      .status.light {
+      .detailed-status,
+      .status {
         border-radius: 0 !important;
       }
     }
   }
+}
 
-  &.with-header {
-    .entry {
-      &:first-child {
-        &,
-        .detailed-status.light,
-        .status.light {
-          border-radius: 0;
-        }
-
-        &:last-child {
-          &,
-          .detailed-status.light,
-          .status.light {
-            border-radius: 0 0 4px 4px;
-          }
-        }
-      }
-    }
-  }
-
-  .media-gallery__gifv__label {
-    bottom: 9px;
-  }
-
-  .status.light {
-    padding: 14px 14px 14px (48px + 14px * 2);
-    position: relative;
-    min-height: 48px;
-    cursor: default;
-
-    .status__header {
-      font-size: 15px;
-
-      .status__meta {
-        float: right;
-        font-size: 14px;
-
-        .status__relative-time {
-          color: $lighter-text-color;
-        }
-      }
-    }
-
-    .status__display-name {
-      display: block;
-      max-width: 100%;
-      padding-right: 25px;
-      color: $inverted-text-color;
-    }
-
-    .status__avatar {
-      position: absolute;
-      left: 14px;
-      top: 14px;
-      width: 48px;
-      height: 48px;
-
-      & > div {
-        width: 48px;
-        height: 48px;
-      }
-
-      img {
-        display: block;
-        border-radius: 4px;
-      }
-    }
-
-    .display-name {
-      display: block;
-      max-width: 100%;
-      overflow: hidden;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-
-      strong {
-        font-weight: 500;
-        color: $inverted-text-color;
-
-        @each $lang in $cjk-langs {
-          &:lang(#{$lang}) {
-            font-weight: 700;
-          }
-        }
-      }
-
-      span {
-        font-size: 14px;
-        color: $light-text-color;
-      }
-    }
-
-    .status__content {
-      color: $inverted-text-color;
-
-      a {
-        color: $highlight-text-color;
-      }
-
-      a.status__content__spoiler-link {
-        color: $primary-text-color;
-        background: $ui-base-color;
-
-        &:hover {
-          background: lighten($ui-base-color, 8%);
-        }
-      }
-    }
-  }
-
-  .detailed-status.light {
-    padding: 14px;
-    background: $simple-background-color;
-    cursor: default;
-
-    .detailed-status__display-name {
-      display: block;
-      overflow: hidden;
-      margin-bottom: 15px;
-
-      & > div {
-        float: left;
-        margin-right: 10px;
-      }
-
-      .display-name {
-        display: block;
-        max-width: 100%;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-
-        strong {
-          font-weight: 500;
-          color: $inverted-text-color;
-
-          @each $lang in $cjk-langs {
-            &:lang(#{$lang}) {
-              font-weight: 700;
-            }
-          }
-        }
-
-        span {
-          font-size: 14px;
-          color: $light-text-color;
-        }
-      }
-    }
-
-    .avatar {
-      width: 48px;
-      height: 48px;
-
-      img {
-        display: block;
-        border-radius: 4px;
-      }
-    }
-
-    .status__content {
-      color: $inverted-text-color;
-
-      a {
-        color: $highlight-text-color;
-      }
-
-      a.status__content__spoiler-link {
-        color: $primary-text-color;
-        background: $ui-base-color;
-
-        &:hover {
-          background: lighten($ui-base-color, 8%);
-        }
-      }
-    }
-
-    .detailed-status__meta {
-      margin-top: 15px;
-      color: $light-text-color;
-      font-size: 14px;
-      line-height: 18px;
-
-      a {
-        color: inherit;
-      }
-
-      span > span {
-        font-weight: 500;
-        font-size: 12px;
-        margin-left: 6px;
-        display: inline-block;
-      }
-    }
-
-    .status-card {
-      border-color: lighten($ui-secondary-color, 4%);
-      color: $lighter-text-color;
-
-      &:hover {
-        background: lighten($ui-secondary-color, 4%);
-      }
-    }
-
-    .status-card__title,
-    .status-card__description {
-      color: $inverted-text-color;
-    }
-
-    .status-card__image {
-      background: $ui-secondary-color;
-    }
-  }
-
-  .media-spoiler {
-    background: $ui-base-color;
-    color: $darker-text-color;
-  }
+.button.logo-button {
+  flex: 0 auto;
+  font-size: 14px;
+  background: $ui-highlight-color;
+  color: $primary-text-color;
+  text-transform: none;
+  line-height: 36px;
+  height: auto;
+  padding: 3px 15px;
+  border: 0;
 
-  .pre-header {
-    padding: 14px 0;
-    padding-left: (48px + 14px * 2);
-    padding-bottom: 0;
-    margin-bottom: -4px;
-    color: $light-text-color;
-    font-size: 14px;
-    position: relative;
+  svg {
+    width: 20px;
+    height: auto;
+    vertical-align: middle;
+    margin-right: 5px;
 
-    .pre-header__icon {
-      position: absolute;
-      left: (48px + 14px * 2 - 30px);
+    path:first-child {
+      fill: $primary-text-color;
     }
 
-    .status__display-name.muted strong {
-      color: $light-text-color;
+    path:last-child {
+      fill: $ui-highlight-color;
     }
   }
 
-  .open-in-web-link {
-    text-decoration: none;
+  &:active,
+  &:focus,
+  &:hover {
+    background: lighten($ui-highlight-color, 10%);
 
-    &:hover {
-      text-decoration: underline;
+    svg path:last-child {
+      fill: lighten($ui-highlight-color, 10%);
     }
   }
 
-  .more {
-    color: $darker-text-color;
-    display: block;
-    padding: 14px;
-    text-align: center;
-
-    &:not(:hover) {
-      text-decoration: none;
+  @media screen and (max-width: $no-gap-breakpoint) {
+    svg {
+      display: none;
     }
   }
 }
 
-.embed {
-  .activity-stream {
-    box-shadow: none;
+.embed,
+.public-layout {
+  .detailed-status {
+    padding: 15px;
   }
-}
 
-.entry {
-  .detailed-status.light {
-    display: flex;
-    flex-wrap: wrap;
-    justify-content: space-between;
-    align-items: flex-start;
+  .status {
+    padding: 15px 15px 15px (48px + 15px * 2);
+    min-height: 48px + 2px;
 
-    .detailed-status__display-name {
-      flex: 1;
-      margin: 0 5px 15px 0;
+    &__avatar {
+      left: 15px;
+      top: 17px;
     }
 
-    .button.button-secondary.logo-button {
-      flex: 0 auto;
-      font-size: 14px;
-      background: $ui-highlight-color;
-      color: $primary-text-color;
-      border: 0;
-
-      svg {
-        width: 20px;
-        height: auto;
-        vertical-align: middle;
-        margin-right: 5px;
-
-        path:first-child {
-          fill: $primary-text-color;
-        }
-
-        path:last-child {
-          fill: $ui-highlight-color;
-        }
-      }
+    &__content {
+      padding-top: 5px;
+    }
 
-      &:active,
-      &:focus,
-      &:hover {
-        background: lighten($ui-highlight-color, 10%);
+    &__prepend {
+      margin-left: 48px + 15px * 2;
+      padding-top: 15px;
+    }
 
-        svg path:last-child {
-          fill: lighten($ui-highlight-color, 10%);
-        }
-      }
+    &__prepend-icon-wrapper {
+      left: -32px;
     }
 
-    .status__content,
-    .detailed-status__meta {
-      flex: 100%;
+    .media-gallery,
+    &__action-bar,
+    .video-player {
+      margin-top: 10px;
     }
   }
 }
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index 40aeb4afc..009f0a3c9 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -46,3 +46,5 @@ $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
 $media-modal-media-max-width: 100%;
 // put margins on top and bottom of image to avoid the screen covered by image.
 $media-modal-media-max-height: 80%;
+
+$no-gap-breakpoint: 415px;
diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss
new file mode 100644
index 000000000..9324533bd
--- /dev/null
+++ b/app/javascript/styles/mastodon/widgets.scss
@@ -0,0 +1,123 @@
+.hero-widget {
+  margin-bottom: 10px;
+  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+
+  &__img {
+    width: 100%;
+    height: 167px;
+    position: relative;
+    overflow: hidden;
+    border-radius: 4px 4px 0 0;
+    background: $base-shadow-color;
+
+    img {
+      object-fit: cover;
+      display: block;
+      width: 100%;
+      height: 100%;
+      margin: 0;
+      border-radius: 4px 4px 0 0;
+    }
+  }
+
+  &__text {
+    background: $ui-base-color;
+    padding: 20px;
+    border-radius: 0 0 4px 4px;
+    font-size: 14px;
+    color: $darker-text-color;
+  }
+
+  @media screen and (max-width: $no-gap-breakpoint) {
+    display: none;
+  }
+}
+
+.moved-account-widget {
+  padding: 15px;
+  padding-bottom: 20px;
+  border-radius: 4px;
+  background: $ui-base-color;
+  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+  color: $secondary-text-color;
+  font-weight: 400;
+  margin-bottom: 10px;
+
+  strong,
+  a {
+    font-weight: 500;
+
+    @each $lang in $cjk-langs {
+      &:lang(#{$lang}) {
+        font-weight: 700;
+      }
+    }
+  }
+
+  a {
+    color: inherit;
+    text-decoration: underline;
+
+    &.mention {
+      text-decoration: none;
+
+      span {
+        text-decoration: none;
+      }
+
+      &:focus,
+      &:hover,
+      &:active {
+        text-decoration: none;
+
+        span {
+          text-decoration: underline;
+        }
+      }
+    }
+  }
+
+  &__message {
+    margin-bottom: 15px;
+
+    .fa {
+      margin-right: 5px;
+      color: $darker-text-color;
+    }
+  }
+
+  &__card {
+    .detailed-status__display-avatar {
+      position: relative;
+      cursor: pointer;
+    }
+
+    .detailed-status__display-name {
+      margin-bottom: 0;
+      text-decoration: none;
+
+      span {
+        font-weight: 400;
+      }
+    }
+  }
+}
+
+.memoriam-widget {
+  padding: 20px;
+  border-radius: 4px;
+  background: $base-shadow-color;
+  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
+  font-size: 14px;
+  color: $darker-text-color;
+  margin-bottom: 10px;
+}
+
+.moved-account-widget,
+.memoriam-widget {
+  @media screen and (max-width: $no-gap-breakpoint) {
+    margin-bottom: 0;
+    box-shadow: none;
+    border-radius: 0;
+  }
+}
diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb
index ef40b8126..067e166eb 100644
--- a/app/models/concerns/account_header.rb
+++ b/app/models/concerns/account_header.rb
@@ -5,11 +5,12 @@ module AccountHeader
 
   IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
   LIMIT = 2.megabytes
+  MAX_PIXELS = 750_000 # 1500x500px
 
   class_methods do
     def header_styles(file)
-      styles = { original: { geometry: '700x335#', file_geometry_parser: FastGeometryParser } }
-      styles[:static] = { geometry: '700x335#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
+      styles = { original: { pixels: MAX_PIXELS, file_geometry_parser: FastGeometryParser } }
+      styles[:static] = { format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
       styles
     end
 
diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml
new file mode 100644
index 000000000..c9a4d8f1b
--- /dev/null
+++ b/app/views/accounts/_bio.html.haml
@@ -0,0 +1,15 @@
+.public-account-bio
+  - unless account.fields.empty?
+    .account__header__fields
+      - account.fields.each do |field|
+        %dl
+          %dt.emojify{ title: field.name }= field.name
+          %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true)
+
+  = account_badge(account)
+
+  - if account.note.present?
+    .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
+
+  .public-account-bio__extra
+    = t 'accounts.joined', date: l(account.created_at, format: :month)
diff --git a/app/views/accounts/_follow_button.html.haml b/app/views/accounts/_follow_button.html.haml
deleted file mode 100644
index 558ced010..000000000
--- a/app/views/accounts/_follow_button.html.haml
+++ /dev/null
@@ -1,28 +0,0 @@
-- relationships ||= nil
-
-- unless account.memorial? || account.moved?
-  - if user_signed_in?
-    - requested = relationships ? relationships.requested[account.id].present? : current_account.requested?(account)
-    - following = relationships ? relationships.following[account.id].present? : current_account.following?(account)
-
-  - if user_signed_in? && current_account.id != account.id && !requested
-    .controls
-      - if following
-        = link_to (account.local? ? account_unfollow_path(account) : remote_unfollow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do
-          = fa_icon 'user-times'
-          = t('accounts.unfollow')
-      - else
-        = link_to (account.local? ? account_follow_path(account) : authorize_follow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do
-          = fa_icon 'user-plus'
-          = t('accounts.follow')
-  - elsif user_signed_in? && current_account.id == account.id
-    .controls
-      = link_to settings_profile_url, class: 'icon-button' do
-        = fa_icon 'pencil'
-        = t('settings.edit_profile')
-  - elsif !user_signed_in?
-    .controls
-      .remote-follow
-        = link_to (account.local? ? account_remote_follow_path(account) : "web+mastodon://follow?uri=#{account.uri}"), class: 'icon-button' do
-          = fa_icon 'user-plus'
-          = t('accounts.remote_follow')
diff --git a/app/views/accounts/_follow_grid.html.haml b/app/views/accounts/_follow_grid.html.haml
deleted file mode 100644
index fdcef84be..000000000
--- a/app/views/accounts/_follow_grid.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.accounts-grid{ class: accounts.empty? ? 'empty' : '' }
-  - if accounts.empty?
-    = image_tag asset_pack_path('elephant_ui_greeting.svg'), alt: '', role: 'presentational'
-    = render partial: 'accounts/nothing_here'
-  - else
-    = render partial: 'accounts/grid_card', collection: accounts, as: :account, cached: !user_signed_in?
-
-= paginate follows
diff --git a/app/views/accounts/_follow_grid_hidden.html.haml b/app/views/accounts/_follow_grid_hidden.html.haml
deleted file mode 100644
index e970350e6..000000000
--- a/app/views/accounts/_follow_grid_hidden.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-.accounts-grid.empty
-  = image_tag asset_pack_path('elephant_ui_greeting.svg'), alt: '', role: 'presentational'
-  %p.nothing-here= t('accounts.network_hidden')
diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml
deleted file mode 100644
index a59ed128e..000000000
--- a/app/views/accounts/_grid_card.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-.account-grid-card
-  .account-grid-card__header{ style: "background-image: url(#{account.header.url(:original)})" }
-    = render 'accounts/follow_button', account: account, relationships: @relationships
-  .account-grid-card__avatar
-    .avatar= image_tag account.avatar.url(:original)
-  .name
-    = link_to TagManager.instance.url_for(account) do
-      %span.display_name.emojify= display_name(account, custom_emojify: true)
-      %span.username
-        @#{account.local? ? account.local_username_and_domain : account.acct}
-        = fa_icon('lock') if account.locked?
-  .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account)
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 4098d6778..e343be820 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -1,51 +1,43 @@
-.card.h-card.p-author{ style: "background-image: url(#{account.header.url(:original)})" }
-  .card__illustration
-    = render 'accounts/follow_button', account: account
-    .avatar= image_tag account.avatar.url(:original), class: 'u-photo'
+.public-account-header
+  .public-account-header__image
+    = image_tag account.header.url, class: 'parallax'
+  .public-account-header__bar
+    = link_to short_account_url(account), class: 'avatar' do
+      = image_tag account.avatar.url
+    .public-account-header__tabs
+      .public-account-header__tabs__name
+        %h1
+          = display_name(account)
+          %small
+            = acct(account)
+            = fa_icon('lock') if account.locked?
+      .public-account-header__tabs__tabs
+        .details-counters
+          .counter{ class: active_nav_class(short_account_url(account)) }
+            = link_to short_account_url(account), class: 'u-url u-uid' do
+              %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
+              %span.counter-label= t('accounts.posts')
 
-  .card__bio
-    %h1.name
-      %span.p-name.emojify= display_name(account, custom_emojify: true)
-      %small<
-        %span>< @#{account.local_username_and_domain}
-        = fa_icon('lock') if account.locked?
+          .counter{ class: active_nav_class(account_following_index_url(account)) }
+            = link_to account_following_index_url(account) do
+              %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true
+              %span.counter-label= t('accounts.following')
 
-    - if account.bot?
-      .roles
-        .account-role.bot
-          = t 'accounts.roles.bot'
-    - elsif Setting.show_staff_badge
-      - if account.user_admin?
-        .roles
-          .account-role.admin
-            = t 'accounts.roles.admin'
-      - elsif account.user_moderator?
-        .roles
-          .account-role.moderator
-            = t 'accounts.roles.moderator'
+          .counter{ class: active_nav_class(account_followers_url(account)) }
+            = link_to account_followers_url(account) do
+              %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true
+              %span.counter-label= t('accounts.followers')
+        .spacer
+        .public-account-header__tabs__tabs__buttons
+          = account_action_button(account)
 
-    .bio
-      .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
+    .public-account-header__extra
+      = render 'accounts/bio', account: account
 
-      - unless account.fields.empty?
-        .account__header__fields
-          - account.fields.each do |field|
-            %dl
-              %dt.emojify{ title: field.name }= field.name
-              %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true)
-
-    .details-counters
-      .counter{ class: active_nav_class(short_account_url(account)) }
-        = link_to short_account_url(account), class: 'u-url u-uid' do
-          %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
-          %span.counter-label= t('accounts.posts')
-
-      .counter{ class: active_nav_class(account_following_index_url(account)) }
+      .public-account-header__extra__links
         = link_to account_following_index_url(account) do
-          %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true
-          %span.counter-label= t('accounts.following')
-
-      .counter{ class: active_nav_class(account_followers_url(account)) }
+          %strong= number_to_human account.following_count, strip_insignificant_zeros: true
+          = t('accounts.following')
         = link_to account_followers_url(account) do
-          %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true
-          %span.counter-label= t('accounts.followers')
+          %strong= number_to_human account.followers_count, strip_insignificant_zeros: true
+          = t('accounts.followers')
diff --git a/app/views/accounts/_moved_strip.html.haml b/app/views/accounts/_moved.html.haml
index ae18c6dc7..f99328dbd 100644
--- a/app/views/accounts/_moved_strip.html.haml
+++ b/app/views/accounts/_moved.html.haml
@@ -1,11 +1,11 @@
 - moved_to_account = account.moved_to_account
 
-.moved-strip
-  .moved-strip__message
+.moved-account-widget
+  .moved-account-widget__message
     = fa_icon 'suitcase'
-    = t('accounts.moved_html', name: content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention'))
+    = t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention'))
 
-  .moved-strip__card
+  .moved-account-widget__card
     = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
       .detailed-status__display-avatar
         .account__avatar-overlay
@@ -13,5 +13,6 @@
           .account__avatar-overlay-overlay{ style: "background-image: url('#{account.avatar.url(:original)}')" }
 
       %span.display-name
-        %strong.emojify= display_name(moved_to_account, custom_emojify: true)
+        %bdi
+          %strong.emojify= display_name(moved_to_account, custom_emojify: true)
         %span @#{moved_to_account.acct}
diff --git a/app/views/accounts/_nothing_here.html.haml b/app/views/accounts/_nothing_here.html.haml
deleted file mode 100644
index 0c6dc1168..000000000
--- a/app/views/accounts/_nothing_here.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-%p.nothing-here= t('accounts.nothing_here')
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index cfdd3a945..b30755d94 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -20,36 +20,39 @@
   = opengraph 'og:type', 'profile'
   = render 'og', account: @account, url: short_account_url(@account, only_path: false)
 
-- if @account.memorial?
-  .memoriam-strip= t('in_memoriam_html')
-- elsif @account.moved?
-  = render partial: 'moved_strip', locals: { account: @account }
-- elsif show_landing_strip?
-  = render partial: 'shared/landing_strip', locals: { account: @account }
-
-.h-feed
-  %data.p-name{ value: "#{@account.username} on #{site_hostname}" }/
-
-  = render 'header', account: @account
-
-  .activity-stream-tabs
-    = active_link_to t('accounts.posts'), short_account_url(@account)
-    = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
-    = active_link_to t('accounts.media'), short_account_media_url(@account)
-
-  - if @statuses.empty?
-    .accounts-grid
-      = render 'nothing_here'
-  - else
-    .activity-stream.with-header
-      - if params[:page].to_i.zero?
-        = render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true }
-
-      = render partial: 'stream_entries/status', collection: @statuses, as: :status
-
-  - if @newer_url || @older_url
-    .pagination
-      - if @older_url
-        = link_to safe_join([fa_icon('chevron-left'), t('pagination.older')], ' '), @older_url, class: 'older', rel: 'next'
-      - if @newer_url
-        = link_to safe_join([t('pagination.newer'), fa_icon('chevron-right')], ' '), @newer_url, class: 'newer', rel: 'prev'
+
+= render 'header', account: @account, with_bio: true
+
+.grid
+  .column-0
+    .h-feed
+      %data.p-name{ value: "#{@account.username} on #{site_hostname}" }/
+
+      .account__section-headline
+        = active_link_to t('accounts.posts'), short_account_url(@account)
+        = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
+        = active_link_to t('accounts.media'), short_account_media_url(@account)
+
+      - if @statuses.empty?
+        = nothing_here 'nothing-here--under-tabs'
+      - else
+        .activity-stream
+          - if params[:page].to_i.zero?
+            = render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true }
+
+          - if @newer_url
+            .entry= link_to_more @newer_url
+
+          = render partial: 'stream_entries/status', collection: @statuses, as: :status
+
+          - if @older_url
+            .entry= link_to_more @older_url
+
+  .column-1
+    - if @account.memorial?
+      .memoriam-widget= t('in_memoriam_html')
+    - elsif @account.moved?
+      = render 'moved', account: @account
+
+    = render 'bio', account: @account
+    = render 'application/sidebar'
diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml
new file mode 100644
index 000000000..9cf8f8ff2
--- /dev/null
+++ b/app/views/application/_card.html.haml
@@ -0,0 +1,16 @@
+- account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account)
+
+.card.h-card
+  = link_to account_url, target: '_blank', rel: 'noopener' do
+    .card__img
+      = image_tag account.header.url, alt: ''
+    .card__bar
+      .avatar
+        = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo'
+
+      .display-name
+        %bdi
+          %strong.emojify.p-name= display_name(account, custom_emojify: true)
+        %span
+          = acct(account)
+          = fa_icon('lock') if account.locked?
diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml
new file mode 100644
index 000000000..3d8832bb4
--- /dev/null
+++ b/app/views/application/_sidebar.html.haml
@@ -0,0 +1,6 @@
+.hero-widget
+  .hero-widget__img
+    = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('preview.jpg'), alt: @instance_presenter.site_title
+
+  .hero-widget__text
+    %p= @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname)
diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml
index 0fac8e10d..200ed42de 100644
--- a/app/views/auth/registrations/new.html.haml
+++ b/app/views/auth/registrations/new.html.haml
@@ -10,7 +10,7 @@
   - if @invite.present? && @invite.autofollow?
     .fields-group{ style: 'margin-bottom: 30px' }
       %p.hint{ style: 'text-align: center' }= t('invites.invited_by')
-      = render 'authorize_follows/card', account: @invite.user.account
+      = render 'application/card', account: @invite.user.account
 
   = f.simple_fields_for :account do |ff|
     .input-with-append
diff --git a/app/views/authorize_follows/_card.html.haml b/app/views/authorize_follows/_card.html.haml
deleted file mode 100644
index edc03131f..000000000
--- a/app/views/authorize_follows/_card.html.haml
+++ /dev/null
@@ -1,23 +0,0 @@
-.account-card
-  .account-card__header{ style: "background-image: url(#{account.header.url(:original)})" }
-  .detailed-status__display-name
-    %div
-      = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar'
-
-    %span.display-name
-      - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account)
-      = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
-        %strong.emojify= display_name(account, custom_emojify: true)
-        %span @#{account.acct}
-
-    .counter
-      %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
-      %span.counter-label= t('accounts.posts')
-
-    .counter
-      %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true
-      %span.counter-label= t('accounts.following')
-
-    .counter
-      %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true
-      %span.counter-label= t('accounts.followers')
diff --git a/app/views/authorize_follows/show.html.haml b/app/views/authorize_follows/show.html.haml
index a1fd01dd6..90e65b34f 100644
--- a/app/views/authorize_follows/show.html.haml
+++ b/app/views/authorize_follows/show.html.haml
@@ -3,7 +3,7 @@
 
 .form-container
   .follow-prompt
-    = render 'card', account: @account
+    = render 'application/card', account: @account
 
   - if current_account.following?(@account)
     .flash-message
diff --git a/app/views/authorize_follows/success.html.haml b/app/views/authorize_follows/success.html.haml
index fa59b24b8..cf9cb50ea 100644
--- a/app/views/authorize_follows/success.html.haml
+++ b/app/views/authorize_follows/success.html.haml
@@ -8,6 +8,6 @@
     - else
       %h2= t('authorize_follow.following')
 
-    = render 'card', account: @account
+    = render 'application/card', account: @account
 
   = render 'post_follow_actions'
diff --git a/app/views/follower_accounts/index.html.haml b/app/views/follower_accounts/index.html.haml
index 65af81a5b..31dab68bf 100644
--- a/app/views/follower_accounts/index.html.haml
+++ b/app/views/follower_accounts/index.html.haml
@@ -8,6 +8,11 @@
 = render 'accounts/header', account: @account
 
 - if @account.user_hides_network?
-  = render 'accounts/follow_grid_hidden'
+  .nothing-here= t('accounts.network_hidden')
+- elsif @follows.empty?
+  = nothing_here
 - else
-  = render 'accounts/follow_grid', follows: @follows, accounts: @follows.map(&:account)
+  .card-grid
+    = render partial: 'application/card', collection: @follows.map(&:account), as: :account
+
+  = paginate @follows
diff --git a/app/views/following_accounts/index.html.haml b/app/views/following_accounts/index.html.haml
index 8fd95a0b4..8b49b529b 100644
--- a/app/views/following_accounts/index.html.haml
+++ b/app/views/following_accounts/index.html.haml
@@ -8,6 +8,11 @@
 = render 'accounts/header', account: @account
 
 - if @account.user_hides_network?
-  = render 'accounts/follow_grid_hidden'
+  .nothing-here= t('accounts.network_hidden')
+- elsif @follows.empty?
+  = nothing_here
 - else
-  = render 'accounts/follow_grid', follows: @follows, accounts: @follows.map(&:target_account)
+  .card-grid
+    = render partial: 'application/card', collection: @follows.map(&:target_account), as: :account
+
+  = paginate @follows
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index 600290297..f9d808bed 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -2,16 +2,49 @@
   = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
 
 - content_for :content do
-  .container-alt= yield
-  .footer
-    - if !user_signed_in? && single_user_mode?
-      %span.single-user-login
-        = link_to t('auth.login'), new_user_session_path
-        &mdash;
-      %span.footer__domain= link_to site_hostname, about_path
-    - else
-      %span.footer__domain= link_to site_hostname, root_path
-    %span.powered-by
-      != t('generic.powered_by', link: link_to('https://joinmastodon.org') { image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon' })
+  .public-layout
+    .container
+      %nav.header
+        .nav-left
+          = link_to root_url, class: 'brand' do
+            = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
+        .nav-center
+        .nav-right
+          - if user_signed_in?
+            = link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
+          - else
+            = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button'
+            = link_to t('auth.register'), new_user_registration_path, class: 'webapp-btn nav-link nav-button'
+
+    .container= yield
+
+    .container
+      .footer
+        .grid
+          .column-0
+            %h4= t 'footer.resources'
+            %ul
+              %li= link_to t('about.terms'), terms_path
+              %li= link_to t('about.privacy_policy'), terms_path
+          .column-1
+            %h4= t 'footer.developers'
+            %ul
+              %li= link_to t('about.documentation'), 'https://github.com/tootsuite/documentation'
+              %li= link_to t('about.api'), 'https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md'
+          .column-2
+            %h4= link_to t('about.what_is_mastodon'), 'https://joinmastodon.org/'
+
+            = link_to root_url, class: 'brand' do
+              = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
+          .column-3
+            %h4= site_hostname
+            %ul
+              %li= link_to t('about.about_this'), about_more_path
+              %li= "v#{Mastodon::Version.to_s}"
+          .column-4
+            %h4= t 'footer.more'
+            %ul
+              %li= link_to t('about.source_code'), Mastodon::Version.source_url
+              %li= link_to 'joinmastodon.org', 'https://joinmastodon.org'
 
 = render template: 'layouts/application'
diff --git a/app/views/remote_follow/new.html.haml b/app/views/remote_follow/new.html.haml
index 9b22fda5f..9b679015f 100644
--- a/app/views/remote_follow/new.html.haml
+++ b/app/views/remote_follow/new.html.haml
@@ -6,7 +6,7 @@
   .follow-prompt
     %h2= t('remote_follow.prompt')
 
-    = render partial: 'authorize_follows/card', locals: { account: @account }
+    = render partial: 'application/card', locals: { account: @account }
 
   = simple_form_for @remote_follow, as: :remote_follow, url: account_remote_follow_path(@account) do |f|
     = render 'shared/error_messages', object: @remote_follow
diff --git a/app/views/remote_unfollows/success.html.haml b/app/views/remote_unfollows/success.html.haml
index aa3c838a0..b007eedc7 100644
--- a/app/views/remote_unfollows/success.html.haml
+++ b/app/views/remote_unfollows/success.html.haml
@@ -5,6 +5,6 @@
   .follow-prompt
     %h2= t('remote_unfollow.unfollowed')
 
-    = render 'card', account: @account
+    = render 'application/card', account: @account
 
   = render 'post_follow_actions'
diff --git a/app/views/settings/migrations/show.html.haml b/app/views/settings/migrations/show.html.haml
index b7c34761f..c69061d50 100644
--- a/app/views/settings/migrations/show.html.haml
+++ b/app/views/settings/migrations/show.html.haml
@@ -6,7 +6,7 @@
     %p.hint= t('migrations.currently_redirecting')
 
     .fields-group
-      = render partial: 'authorize_follows/card', locals: { account: @migration.account }
+      = render partial: 'application/card', locals: { account: @migration.account }
 
   = render 'shared/error_messages', object: @migration
 
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index a84f8a7da..3940eb855 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -8,8 +8,7 @@
     = f.input :display_name, placeholder: t('simple_form.labels.defaults.display_name'), hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe
     = f.input :note, placeholder: t('simple_form.labels.defaults.note'), hint: t('simple_form.hints.defaults.note', count: 160 - @account.note.size).html_safe
 
-  .card.compact{ style: "background-image: url(#{@account.header.url(:original)})", data: { original_src: @account.header.url(:original) } }
-    .avatar= image_tag @account.avatar.url(:original), data: { original_src: @account.avatar.url(:original) }
+  = render 'application/card', account: @account
 
   .fields-group
     = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar')
diff --git a/app/views/shared/_landing_strip.html.haml b/app/views/shared/_landing_strip.html.haml
deleted file mode 100644
index 9a4144723..000000000
--- a/app/views/shared/_landing_strip.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-.landing-strip
-  = image_tag asset_pack_path('logo.svg'), class: 'logo'
-
-  %div
-    = t('landing_strip_html', name: content_tag(:span, display_name(account, custom_emojify: true), class: :emojify), link_to_root_path: link_to(content_tag(:strong, site_hostname), root_path))
-    = t('landing_strip_signup_html', sign_up_path: open_registrations? ? new_user_registration_path : 'https://joinmastodon.org/#getting-started')
diff --git a/app/views/stream_entries/_content_spoiler.html.haml b/app/views/stream_entries/_content_spoiler.html.haml
deleted file mode 100644
index 798dfce67..000000000
--- a/app/views/stream_entries/_content_spoiler.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.media-spoiler-wrapper{ class: sensitive == false && 'media-spoiler-wrapper__visible' }
-  .spoiler-button
-    .icon-button.overlayed
-      %i.fa.fa-fw.fa-eye
-  .media-spoiler
-    %span= t('stream_entries.sensitive_content')
-    %span= t('stream_entries.click_to_show')
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index 85e90a237..aa160b979 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -1,16 +1,15 @@
-.detailed-status.light
+.detailed-status.detailed-status--flex
   = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
-    %div
-      .avatar
-        = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo'
+    .detailed-status__display-avatar
+      = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'account__avatar u-photo'
     %span.display-name
-      %strong.p-name.emojify= display_name(status.account, custom_emojify: true)
-      %span= acct(status.account)
+      %bdi
+        %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true)
+      %span.display-name__account
+        = acct(status.account)
+        = fa_icon('lock') if status.account.locked?
 
-  - if !user_signed_in? || embedded_view?
-    = link_to account_remote_follow_path(status.account), class: 'button button-secondary logo-button', target: '_new' do
-      = render file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')
-      = t('accounts.follow')
+  = account_action_button(status.account)
 
   .status__content.emojify<
     - if status.spoiler_text?
@@ -30,6 +29,7 @@
 
   .detailed-status__meta
     %data.dt-published{ value: status.created_at.to_time.iso8601 }
+
     = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener' do
       %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
     ·
@@ -40,20 +40,20 @@
         = link_to status.application.name, status.application.website, class: 'detailed-status__application', target: '_blank', rel: 'noopener'
       ·
     - if status.direct_visibility?
-      %span<
+      %span.detailed-status__link<
         = fa_icon('envelope')
     - elsif status.private_visibility?
-      %span<
+      %span.detailed-status__link<
         = fa_icon('lock')
     - else
-      %span<
+      %span.detailed-status__link<
         = fa_icon('retweet')
-        %span= status.reblogs_count
+        %span.detailed-status__reblogs= number_to_human status.reblogs_count, strip_insignificant_zeros: true
     ·
-    %span<
+    %span.detailed-status__link<
       = fa_icon('star')
-      %span= status.favourites_count
+      %span.detailed-status__favorites= number_to_human status.favourites_count, strip_insignificant_zeros: true
 
     - if user_signed_in?
       ·
-      = link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'open-in-web-link', target: '_blank'
+      = link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'detailed-status__application', target: '_blank'
diff --git a/app/views/stream_entries/_media.html.haml b/app/views/stream_entries/_media.html.haml
deleted file mode 100644
index 779f02c8d..000000000
--- a/app/views/stream_entries/_media.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-.media-item
-  = link_to media.remote_url.blank? ? media.file.url(:original) : media.remote_url, style: media.image? ? "background-image: url(#{media.file.url(:original)})" : '', target: '_blank', rel: 'noopener', class: "u-#{media.video? || media.gifv? ? 'video' : 'photo'}" do
-    - unless media.image?
-      %video{ src: media.file.url(:original), autoplay: true, loop: true }/
diff --git a/app/views/stream_entries/_more.html.haml b/app/views/stream_entries/_more.html.haml
deleted file mode 100644
index 9b1dfe4a7..000000000
--- a/app/views/stream_entries/_more.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-= link_to url, class: 'more light'  do
-  = t('statuses.show_more')
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 0f27585a1..676d367ca 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -1,18 +1,19 @@
-.status.light
-  .status__header
-    .status__meta
-      = link_to TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do
-        %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
-      %data.dt-published{ value: status.created_at.to_time.iso8601 }
+.status
+  .status__info
+    = link_to TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do
+      %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
+    %data.dt-published{ value: status.created_at.to_time.iso8601 }
 
     = link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
       .status__avatar
         %div
-          = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo'
+          = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo account__avatar'
       %span.display-name
-        %strong.p-name.emojify= display_name(status.account, custom_emojify: true)
-        %span= acct(status.account)
-
+        %bdi
+          %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true)
+        %span.display-name__account
+          = acct(status.account)
+          = fa_icon('lock') if status.account.locked?
   .status__content.emojify<
     - if status.spoiler_text?
       %p{ style: 'margin-bottom: 0' }<
@@ -26,3 +27,16 @@
       = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true
     - else
       = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+
+  .status__action-bar
+    .status__action-bar-button.static-icon-button<
+      - if status.public_visibility? || status.unlisted_visibility?
+        = fa_icon 'retweet fw'
+        %span.detailed-status__reblogs= number_to_human status.reblogs_count, strip_insignificant_zeros: true
+      - elsif status.private_visibility?
+        = fa_icon 'lock fw'
+      - else
+        = fa_icon 'envelope fw'
+    .status__action-bar-button.static-icon-button<
+      = fa_icon 'star fw'
+      %span.detailed-status__favorites= number_to_human status.favourites_count, strip_insignificant_zeros: true
diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml
index b87ca2177..320c9bc4f 100644
--- a/app/views/stream_entries/_status.html.haml
+++ b/app/views/stream_entries/_status.html.haml
@@ -16,24 +16,25 @@
 - if status.reply? && include_threads
   - if @next_ancestor
     .entry{ class: entry_classes }
-      = render 'stream_entries/more', url: TagManager.instance.url_for(@next_ancestor)
+      = link_to_more TagManager.instance.url_for(@next_ancestor)
 
   = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }
 
 .entry{ class: entry_classes }
 
   - if status.reblog?
-    .pre-header
-      .pre-header__icon
-        = fa_icon('retweet fw')
+    .status__prepend
+      .status__prepend-icon-wrapper
+        %i.status__prepend-icon.fa.fa-fw.fa-retweet
       %span
         = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do
-          %strong.emojify= display_name(status.account, custom_emojify: true)
+          %bdi
+            %strong.emojify= display_name(status.account, custom_emojify: true)
         = t('stream_entries.reblogged')
   - elsif pinned
-    .pre-header
-      .pre-header__icon
-        = fa_icon('thumb-tack fw')
+    .status__prepend
+      .status__prepend-icon-wrapper
+        %i.status__prepend-icon.fa.fa-fw.fa-thumb-tack
       %span
         = t('stream_entries.pinned')
 
@@ -42,13 +43,13 @@
 - if include_threads
   - if @since_descendant_thread_id
     .entry{ class: entry_classes }
-      = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1)
+      = link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1)
   - @descendant_threads.each do |thread|
     = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }
 
     - if thread[:next_status]
       .entry{ class: entry_classes }
-        = render 'stream_entries/more', url: TagManager.instance.url_for(thread[:next_status])
+        = link_to_more TagManager.instance.url_for(thread[:next_status])
   - if @next_descendant_thread
     .entry{ class: entry_classes }
-      = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1)
+      = link_to_more short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1)
diff --git a/app/views/stream_entries/embed.html.haml b/app/views/stream_entries/embed.html.haml
index b703c15d2..d20c1e93e 100644
--- a/app/views/stream_entries/embed.html.haml
+++ b/app/views/stream_entries/embed.html.haml
@@ -1,3 +1,3 @@
 - cache @stream_entry.activity do
-  .activity-stream.activity-stream-headless
+  .activity-stream.activity-stream--headless
     = render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
index dfb83e747..9da6245dc 100644
--- a/app/views/stream_entries/show.html.haml
+++ b/app/views/stream_entries/show.html.haml
@@ -17,8 +17,9 @@
   = render 'stream_entries/og_description', activity: @stream_entry.activity
   = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account
 
-- if show_landing_strip?
-  = render partial: 'shared/landing_strip', locals: { account: @stream_entry.account }
-
-.activity-stream.activity-stream-headless.h-entry
-  = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
+.grid
+  .column-0
+    .activity-stream.activity-stream-headless.h-entry
+      = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
+  .column-1
+    = render 'application/sidebar'