about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/api/web/base_controller.rb9
-rw-r--r--app/controllers/api/web/embeds_controller.rb2
-rw-r--r--app/controllers/api/web/push_subscriptions_controller.rb3
-rw-r--r--app/controllers/api/web/settings_controller.rb2
-rw-r--r--app/controllers/home_controller.rb6
-rw-r--r--app/controllers/settings/profiles_controller.rb6
-rw-r--r--app/controllers/statuses_controller.rb6
-rw-r--r--app/controllers/stream_entries_controller.rb3
-rw-r--r--app/javascript/mastodon/actions/compose.js5
-rw-r--r--app/javascript/mastodon/actions/importer/normalizer.js8
-rw-r--r--app/javascript/mastodon/actions/notifications.js37
-rw-r--r--app/javascript/mastodon/actions/push_notifications/registerer.js10
-rw-r--r--app/javascript/mastodon/actions/settings.js2
-rw-r--r--app/javascript/mastodon/actions/streaming.js1
-rw-r--r--app/javascript/mastodon/actions/timelines.js1
-rw-r--r--app/javascript/mastodon/compare_id.js10
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js32
-rw-r--r--app/javascript/mastodon/components/status.js5
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js4
-rw-r--r--app/javascript/mastodon/features/account/components/header.js14
-rw-r--r--app/javascript/mastodon/features/compose/components/compose_form.js9
-rw-r--r--app/javascript/mastodon/features/compose/containers/compose_form_container.js4
-rw-r--r--app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/mastodon/features/direct_timeline/index.js104
-rw-r--r--app/javascript/mastodon/features/domain_blocks/index.js2
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js17
-rw-r--r--app/javascript/mastodon/features/keyboard_shortcuts/index.js4
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js4
-rw-r--r--app/javascript/mastodon/features/status/index.js5
-rw-r--r--app/javascript/mastodon/features/ui/components/actions_modal.js2
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.js3
-rw-r--r--app/javascript/mastodon/features/ui/index.js9
-rw-r--r--app/javascript/mastodon/features/ui/util/async-components.js4
-rw-r--r--app/javascript/mastodon/locales/ar.json7
-rw-r--r--app/javascript/mastodon/locales/bg.json7
-rw-r--r--app/javascript/mastodon/locales/ca.json7
-rw-r--r--app/javascript/mastodon/locales/de.json7
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json59
-rw-r--r--app/javascript/mastodon/locales/en.json2
-rw-r--r--app/javascript/mastodon/locales/eo.json19
-rw-r--r--app/javascript/mastodon/locales/es.json7
-rw-r--r--app/javascript/mastodon/locales/fa.json7
-rw-r--r--app/javascript/mastodon/locales/fi.json233
-rw-r--r--app/javascript/mastodon/locales/fr.json11
-rw-r--r--app/javascript/mastodon/locales/gl.json11
-rw-r--r--app/javascript/mastodon/locales/he.json7
-rw-r--r--app/javascript/mastodon/locales/hr.json7
-rw-r--r--app/javascript/mastodon/locales/hu.json7
-rw-r--r--app/javascript/mastodon/locales/hy.json7
-rw-r--r--app/javascript/mastodon/locales/id.json7
-rw-r--r--app/javascript/mastodon/locales/io.json7
-rw-r--r--app/javascript/mastodon/locales/it.json7
-rw-r--r--app/javascript/mastodon/locales/ja.json7
-rw-r--r--app/javascript/mastodon/locales/ko.json7
-rw-r--r--app/javascript/mastodon/locales/nl.json7
-rw-r--r--app/javascript/mastodon/locales/no.json7
-rw-r--r--app/javascript/mastodon/locales/oc.json7
-rw-r--r--app/javascript/mastodon/locales/pl.json6
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json7
-rw-r--r--app/javascript/mastodon/locales/pt.json7
-rw-r--r--app/javascript/mastodon/locales/ru.json7
-rw-r--r--app/javascript/mastodon/locales/sk.json33
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json7
-rw-r--r--app/javascript/mastodon/locales/sr.json7
-rw-r--r--app/javascript/mastodon/locales/sv.json7
-rw-r--r--app/javascript/mastodon/locales/th.json7
-rw-r--r--app/javascript/mastodon/locales/tr.json7
-rw-r--r--app/javascript/mastodon/locales/uk.json7
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json7
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json13
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json131
-rw-r--r--app/javascript/mastodon/reducers/compose.js7
-rw-r--r--app/javascript/mastodon/reducers/contexts.js39
-rw-r--r--app/javascript/mastodon/reducers/notifications.js12
-rw-r--r--app/javascript/mastodon/reducers/settings.js6
-rw-r--r--app/javascript/mastodon/reducers/timelines.js5
-rw-r--r--app/javascript/styles/mastodon/about.scss64
-rw-r--r--app/javascript/styles/mastodon/accounts.scss90
-rw-r--r--app/javascript/styles/mastodon/admin.scss42
-rw-r--r--app/javascript/styles/mastodon/basics.scss2
-rw-r--r--app/javascript/styles/mastodon/boost.scss6
-rw-r--r--app/javascript/styles/mastodon/compact_header.scss4
-rw-r--r--app/javascript/styles/mastodon/components.scss388
-rw-r--r--app/javascript/styles/mastodon/containers.scss2
-rw-r--r--app/javascript/styles/mastodon/emoji_picker.scss16
-rw-r--r--app/javascript/styles/mastodon/footer.scss2
-rw-r--r--app/javascript/styles/mastodon/forms.scss51
-rw-r--r--app/javascript/styles/mastodon/landing_strip.scss8
-rw-r--r--app/javascript/styles/mastodon/reset.scss5
-rw-r--r--app/javascript/styles/mastodon/stream_entries.scss32
-rw-r--r--app/javascript/styles/mastodon/tables.scss4
-rw-r--r--app/javascript/styles/mastodon/variables.scss7
-rw-r--r--app/lib/activitypub/adapter.rb3
-rw-r--r--app/lib/formatter.rb18
-rw-r--r--app/models/account.rb38
-rw-r--r--app/models/status.rb1
-rw-r--r--app/policies/status_policy.rb2
-rw-r--r--app/serializers/activitypub/actor_serializer.rb17
-rw-r--r--app/serializers/rest/account_serializer.rb10
-rw-r--r--app/services/activitypub/process_account_service.rb19
-rw-r--r--app/services/process_mentions_service.rb4
-rw-r--r--app/services/search_service.rb2
-rw-r--r--app/validators/status_pin_validator.rb2
-rw-r--r--app/views/about/_administration.html.haml19
-rw-r--r--app/views/about/show.html.haml30
-rw-r--r--app/views/accounts/_header.html.haml12
-rw-r--r--app/views/admin/accounts/_card.html.haml17
-rw-r--r--app/views/admin/reports/_account_details.html.haml20
-rw-r--r--app/views/admin/reports/show.html.haml4
-rw-r--r--app/views/invites/_invite.html.haml2
-rw-r--r--app/views/settings/profiles/show.html.haml10
-rw-r--r--app/workers/activitypub/synchronize_featured_collection_worker.rb2
112 files changed, 1379 insertions, 709 deletions
diff --git a/app/controllers/api/web/base_controller.rb b/app/controllers/api/web/base_controller.rb
new file mode 100644
index 000000000..8da549b3a
--- /dev/null
+++ b/app/controllers/api/web/base_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Api::Web::BaseController < Api::BaseController
+  protect_from_forgery with: :exception
+
+  rescue_from ActionController::InvalidAuthenticityToken do
+    render json: { error: "Can't verify CSRF token authenticity." }, status: 422
+  end
+end
diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb
index 2ed516161..f2fe74b17 100644
--- a/app/controllers/api/web/embeds_controller.rb
+++ b/app/controllers/api/web/embeds_controller.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-class Api::Web::EmbedsController < Api::BaseController
+class Api::Web::EmbedsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb
index c611031ab..249e7c186 100644
--- a/app/controllers/api/web/push_subscriptions_controller.rb
+++ b/app/controllers/api/web/push_subscriptions_controller.rb
@@ -1,10 +1,9 @@
 # frozen_string_literal: true
 
-class Api::Web::PushSubscriptionsController < Api::BaseController
+class Api::Web::PushSubscriptionsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
-  protect_from_forgery with: :exception
 
   def create
     active_session = current_session
diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb
index f6739d506..e3178bf48 100644
--- a/app/controllers/api/web/settings_controller.rb
+++ b/app/controllers/api/web/settings_controller.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-class Api::Web::SettingsController < Api::BaseController
+class Api::Web::SettingsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index a8ec0dcc9..6e331dd2d 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -2,7 +2,9 @@
 
 class HomeController < ApplicationController
   before_action :authenticate_user!
+
   before_action :set_pack
+  before_action :set_referrer_policy_header
   before_action :set_initial_state_json
 
   def index
@@ -67,4 +69,8 @@ class HomeController < ApplicationController
       about_path
     end
   end
+
+  def set_referrer_policy_header
+    response.headers['Referrer-Policy'] = 'origin'
+  end
 end
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index dadc3d911..2b8330f2e 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -8,7 +8,9 @@ class Settings::ProfilesController < Settings::BaseController
   obfuscate_filename [:account, :avatar]
   obfuscate_filename [:account, :header]
 
-  def show; end
+  def show
+    @account.build_fields
+  end
 
   def update
     if UpdateAccountService.new.call(@account, account_params)
@@ -22,7 +24,7 @@ class Settings::ProfilesController < Settings::BaseController
   private
 
   def account_params
-    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked)
+    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
   end
 
   def set_account
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index 17fbaa62c..3237a15b9 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -13,6 +13,7 @@ class StatusesController < ApplicationController
   before_action :set_link_headers
   before_action :check_account_suspension
   before_action :redirect_to_original, only: [:show]
+  before_action :set_referrer_policy_header, only: [:show]
   before_action :set_cache_headers
 
   def show
@@ -83,4 +84,9 @@ class StatusesController < ApplicationController
   def redirect_to_original
     redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog?
   end
+
+  def set_referrer_policy_header
+    return if @status.public_visibility? || @status.unlisted_visibility?
+    response.headers['Referrer-Policy'] = 'origin'
+  end
 end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
index e2ea45c83..44e9c0bb8 100644
--- a/app/controllers/stream_entries_controller.rb
+++ b/app/controllers/stream_entries_controller.rb
@@ -16,8 +16,7 @@ class StreamEntriesController < ApplicationController
     respond_to do |format|
       format.html do
         use_pack 'public'
-        @ancestors   = @stream_entry.activity.reply? ? cache_collection(@stream_entry.activity.ancestors(current_account), Status) : []
-        @descendants = cache_collection(@stream_entry.activity.descendants(current_account), Status)
+        redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status'
       end
 
       format.atom do
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 59aa6f98d..eee9c6928 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -145,6 +145,8 @@ export function submitCompose() {
       if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
         insertIfOnline('community');
         insertIfOnline('public');
+      } else if (response.data.visibility === 'direct') {
+        insertIfOnline('direct');
       }
     }).catch(function (error) {
       dispatch(submitComposeFail(error));
@@ -446,11 +448,12 @@ export function changeComposeVisibility(value) {
   };
 };
 
-export function insertEmojiCompose(position, emoji) {
+export function insertEmojiCompose(position, emoji, needsSpace) {
   return {
     type: COMPOSE_EMOJI_INSERT,
     position,
     emoji,
+    needsSpace,
   };
 };
 
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 1b09f319f..5f1274fab 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -10,6 +10,14 @@ export function normalizeAccount(account) {
   account.display_name_html = emojify(escapeTextContentForBrowser(displayName));
   account.note_emojified = emojify(account.note);
 
+  if (account.fields) {
+    account.fields = account.fields.map(pair => ({
+      ...pair,
+      name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
+      value_emojified: emojify(pair.value),
+    }));
+  }
+
   if (account.moved) {
     account.moved = account.moved.id;
   }
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index da77afbe0..7aa070f56 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -9,7 +9,8 @@ import {
 } from './importer';
 import { defineMessages } from 'react-intl';
 
-export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
+export const NOTIFICATIONS_UPDATE      = 'NOTIFICATIONS_UPDATE';
+export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
 
 export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
 export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
@@ -39,21 +40,30 @@ const unescapeHTML = (html) => {
 
 export function updateNotifications(notification, intlMessages, intlLocale) {
   return (dispatch, getState) => {
-    const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
-    const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
+    const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
+    const showAlert    = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
+    const playSound    = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
 
-    dispatch(importFetchedAccount(notification.account));
-    if (notification.status) {
-      dispatch(importFetchedStatus(notification.status));
-    }
+    if (showInColumn) {
+      dispatch(importFetchedAccount(notification.account));
 
-    dispatch({
-      type: NOTIFICATIONS_UPDATE,
-      notification,
-      meta: playSound ? { sound: 'boop' } : undefined,
-    });
+      if (notification.status) {
+        dispatch(importFetchedStatus(notification.status));
+      }
+
+      dispatch({
+        type: NOTIFICATIONS_UPDATE,
+        notification,
+        meta: playSound ? { sound: 'boop' } : undefined,
+      });
 
-    fetchRelatedRelationships(dispatch, [notification]);
+      fetchRelatedRelationships(dispatch, [notification]);
+    } else if (playSound) {
+      dispatch({
+        type: NOTIFICATIONS_UPDATE_NOOP,
+        meta: { sound: 'boop' },
+      });
+    }
 
     // Desktop notifications
     if (typeof window.Notification !== 'undefined' && showAlert) {
@@ -61,6 +71,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
       const body  = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
 
       const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
+
       notify.addEventListener('click', () => {
         window.focus();
         notify.close();
diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js
index f17d929a6..60b215f02 100644
--- a/app/javascript/mastodon/actions/push_notifications/registerer.js
+++ b/app/javascript/mastodon/actions/push_notifications/registerer.js
@@ -36,7 +36,7 @@ const subscribe = (registration) =>
 const unsubscribe = ({ registration, subscription }) =>
   subscription ? subscription.unsubscribe().then(() => registration) : registration;
 
-const sendSubscriptionToBackend = (getState, subscription) => {
+const sendSubscriptionToBackend = (subscription) => {
   const params = { subscription };
 
   if (me) {
@@ -46,7 +46,7 @@ const sendSubscriptionToBackend = (getState, subscription) => {
     }
   }
 
-  return api(getState).post('/api/web/push_subscriptions', params).then(response => response.data);
+  return api().post('/api/web/push_subscriptions', params).then(response => response.data);
 };
 
 // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
@@ -85,13 +85,13 @@ export function register () {
             } else {
               // Something went wrong, try to subscribe again
               return unsubscribe({ registration, subscription }).then(subscribe).then(
-                subscription => sendSubscriptionToBackend(getState, subscription));
+                subscription => sendSubscriptionToBackend(subscription));
             }
           }
 
           // No subscription, try to subscribe
           return subscribe(registration).then(
-            subscription => sendSubscriptionToBackend(getState, subscription));
+            subscription => sendSubscriptionToBackend(subscription));
         })
         .then(subscription => {
           // If we got a PushSubscription (and not a subscription object from the backend)
@@ -134,7 +134,7 @@ export function saveSettings() {
     const alerts = state.get('alerts');
     const data = { alerts };
 
-    api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
+    api().put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
       data,
     }).then(() => {
       if (me) {
diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js
index 5634a11ef..6bf85e464 100644
--- a/app/javascript/mastodon/actions/settings.js
+++ b/app/javascript/mastodon/actions/settings.js
@@ -24,7 +24,7 @@ const debouncedSave = debounce((dispatch, getState) => {
 
   const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
 
-  api(getState).put('/api/web/settings', { data })
+  api().put('/api/web/settings', { data })
     .then(() => dispatch({ type: SETTING_SAVE }))
     .catch(error => dispatch(showAlertForError(error)));
 }, 5000, { trailing: true });
diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index f76510cdb..14215ab6d 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -46,4 +46,5 @@ export const connectCommunityStream = () => connectTimelineStream('community', '
 export const connectMediaStream = () => connectTimelineStream('community', 'public:local');
 export const connectPublicStream = () => connectTimelineStream('public', 'public');
 export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
+export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
 export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`);
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index 5be07126d..eca847ee7 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -87,6 +87,7 @@ export function expandTimeline(timelineId, path, params = {}) {
 export const expandHomeTimeline         = ({ maxId } = {}) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId });
 export const expandPublicTimeline       = ({ maxId } = {}) => expandTimeline('public', '/api/v1/timelines/public', { max_id: maxId });
 export const expandCommunityTimeline    = ({ maxId } = {}) => expandTimeline('community', '/api/v1/timelines/public', { local: true, max_id: maxId });
+export const expandDirectTimeline       = ({ maxId } = {}) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId });
 export const expandAccountTimeline      = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
 export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
 export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
diff --git a/app/javascript/mastodon/compare_id.js b/app/javascript/mastodon/compare_id.js
new file mode 100644
index 000000000..aaff66481
--- /dev/null
+++ b/app/javascript/mastodon/compare_id.js
@@ -0,0 +1,10 @@
+export default function compareId(id1, id2) {
+  if (id1 === id2) {
+    return 0;
+  }
+  if (id1.length === id2.length) {
+    return id1 > id2 ? 1 : -1;
+  } else {
+    return id1.length > id2.length ? 1 : -1;
+  }
+}
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index ee07106f7..fd6858d05 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -34,7 +34,7 @@ export default class ScrollableList extends PureComponent {
   };
 
   state = {
-    lastMouseMove: null,
+    fullscreen: null,
   };
 
   intersectionObserverWrapper = new IntersectionObserverWrapper();
@@ -43,7 +43,6 @@ export default class ScrollableList extends PureComponent {
     if (this.node) {
       const { scrollTop, scrollHeight, clientHeight } = this.node;
       const offset = scrollHeight - scrollTop - clientHeight;
-      this._oldScrollPosition = scrollHeight - scrollTop;
 
       if (400 > offset && this.props.onLoadMore && !this.props.isLoading) {
         this.props.onLoadMore();
@@ -59,14 +58,6 @@ export default class ScrollableList extends PureComponent {
     trailing: true,
   });
 
-  handleMouseMove = throttle(() => {
-    this._lastMouseMove = new Date();
-  }, 300);
-
-  handleMouseLeave = () => {
-    this._lastMouseMove = null;
-  }
-
   componentDidMount () {
     this.attachScrollListener();
     this.attachIntersectionObserver();
@@ -76,21 +67,26 @@ export default class ScrollableList extends PureComponent {
     this.handleScroll();
   }
 
-  componentDidUpdate (prevProps) {
+  getSnapshotBeforeUpdate (prevProps) {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
       React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
       this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
+    if (someItemInserted && this.node.scrollTop > 0) {
+      return this.node.scrollHeight - this.node.scrollTop;
+    } else {
+      return null;
+    }
+  }
 
+  componentDidUpdate (prevProps, prevState, snapshot) {
     // Reset the scroll position when a new child comes in in order not to
     // jerk the scrollbar around if you're already scrolled down the page.
-    if (someItemInserted && this._oldScrollPosition && this.node.scrollTop > 0) {
-      const newScrollTop = this.node.scrollHeight - this._oldScrollPosition;
+    if (snapshot !== null) {
+      const newScrollTop = this.node.scrollHeight - snapshot;
 
       if (this.node.scrollTop !== newScrollTop) {
         this.node.scrollTop = newScrollTop;
       }
-    } else {
-      this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop;
     }
   }
 
@@ -143,10 +139,6 @@ export default class ScrollableList extends PureComponent {
     this.props.onLoadMore();
   }
 
-  _recentlyMoved () {
-    return this._lastMouseMove !== null && ((new Date()) - this._lastMouseMove < 600);
-  }
-
   render () {
     const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
@@ -157,7 +149,7 @@ export default class ScrollableList extends PureComponent {
 
     if (isLoading || childrenCount > 0 || !emptyMessage) {
       scrollableArea = (
-        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove} onMouseLeave={this.handleMouseLeave}>
+        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
           <div role='feed' className='item-list'>
             {prepend}
 
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 6129b3f1e..e5f7c9399 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -122,6 +122,10 @@ export default class Status extends ImmutablePureComponent {
     this.props.onMoveDown(this.props.status.get('id'));
   }
 
+  handleHotkeyToggleHidden = () => {
+    this.props.onToggleHidden(this._properStatus());
+  }
+
   _properStatus () {
     const { status } = this.props;
 
@@ -224,6 +228,7 @@ export default class Status extends ImmutablePureComponent {
       openProfile: this.handleHotkeyOpenProfile,
       moveUp: this.handleHotkeyMoveUp,
       moveDown: this.handleHotkeyMoveDown,
+      toggleHidden: this.handleHotkeyToggleHidden,
     };
 
     return (
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index 10f34b0c7..e58625582 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -18,6 +18,8 @@ const messages = defineMessages({
   more: { id: 'status.more', defaultMessage: 'More' },
   replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
+  reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
+  cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   open: { id: 'status.open', defaultMessage: 'Expand this status' },
@@ -150,6 +152,8 @@ export default class StatusActionBar extends ImmutablePureComponent {
     if (status.getIn(['account', 'id']) === me) {
       if (publicStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
+      } else {
+        menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick });
       }
 
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index bb7b3b632..bbf886dca 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -130,6 +130,7 @@ export default class Header extends ImmutablePureComponent {
 
     const content         = { __html: account.get('note_emojified') };
     const displayNameHtml = { __html: account.get('display_name_html') };
+    const fields          = account.get('fields');
 
     return (
       <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
@@ -140,6 +141,19 @@ export default class Header extends ImmutablePureComponent {
           <span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
           <div className='account__header__content' dangerouslySetInnerHTML={content} />
 
+          {fields.size > 0 && (
+            <table className='account__header__fields'>
+              <tbody>
+                {fields.map((pair, i) => (
+                  <tr key={i}>
+                    <th dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} />
+                    <td dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          )}
+
           {info}
           {mutingInfo}
           {actionBtn}
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index fe7bb1cb3..39eb02362 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -19,6 +19,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import { length } from 'stringz';
 import { countableText } from '../util/counter';
 
+const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
+
 const messages = defineMessages({
   placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
   spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
@@ -144,10 +146,13 @@ export default class ComposeForm extends ImmutablePureComponent {
   }
 
   handleEmojiPick = (data) => {
+    const { text }     = this.props;
     const position     = this.autosuggestTextarea.textarea.selectionStart;
     const emojiChar    = data.native;
-    this._restoreCaret = position + emojiChar.length + 1;
-    this.props.onPickEmoji(position, data);
+    const needsSpace   = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
+
+    this._restoreCaret = position + emojiChar.length + 1 + (needsSpace ? 1 : 0);
+    this.props.onPickEmoji(position, data, needsSpace);
   }
 
   render () {
diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
index ede23d361..c3aa580ee 100644
--- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
@@ -56,8 +56,8 @@ const mapDispatchToProps = (dispatch) => ({
     dispatch(uploadCompose(files));
   },
 
-  onPickEmoji (position, data) {
-    dispatch(insertEmojiCompose(position, data));
+  onPickEmoji (position, data, needsSpace) {
+    dispatch(insertEmojiCompose(position, data, needsSpace));
   },
 
 });
diff --git a/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js
new file mode 100644
index 000000000..1833f69e5
--- /dev/null
+++ b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import ColumnSettings from '../../community_timeline/components/column_settings';
+import { changeSetting } from '../../../actions/settings';
+
+const mapStateToProps = state => ({
+  settings: state.getIn(['settings', 'direct']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (key, checked) {
+    dispatch(changeSetting(['direct', ...key], checked));
+  },
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js
new file mode 100644
index 000000000..fda57f69a
--- /dev/null
+++ b/app/javascript/mastodon/features/direct_timeline/index.js
@@ -0,0 +1,104 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import StatusListContainer from '../ui/containers/status_list_container';
+import Column from '../../components/column';
+import ColumnHeader from '../../components/column_header';
+import { expandDirectTimeline } from '../../actions/timelines';
+import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import ColumnSettingsContainer from './containers/column_settings_container';
+import { connectDirectStream } from '../../actions/streaming';
+
+const messages = defineMessages({
+  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
+});
+
+const mapStateToProps = state => ({
+  hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
+});
+
+@connect(mapStateToProps)
+@injectIntl
+export default class DirectTimeline extends React.PureComponent {
+
+  static propTypes = {
+    dispatch: PropTypes.func.isRequired,
+    columnId: PropTypes.string,
+    intl: PropTypes.object.isRequired,
+    hasUnread: PropTypes.bool,
+    multiColumn: PropTypes.bool,
+  };
+
+  handlePin = () => {
+    const { columnId, dispatch } = this.props;
+
+    if (columnId) {
+      dispatch(removeColumn(columnId));
+    } else {
+      dispatch(addColumn('DIRECT', {}));
+    }
+  }
+
+  handleMove = (dir) => {
+    const { columnId, dispatch } = this.props;
+    dispatch(moveColumn(columnId, dir));
+  }
+
+  handleHeaderClick = () => {
+    this.column.scrollTop();
+  }
+
+  componentDidMount () {
+    const { dispatch } = this.props;
+
+    dispatch(expandDirectTimeline());
+    this.disconnect = dispatch(connectDirectStream());
+  }
+
+  componentWillUnmount () {
+    if (this.disconnect) {
+      this.disconnect();
+      this.disconnect = null;
+    }
+  }
+
+  setRef = c => {
+    this.column = c;
+  }
+
+  handleLoadMore = maxId => {
+    this.props.dispatch(expandDirectTimeline({ maxId }));
+  }
+
+  render () {
+    const { intl, hasUnread, columnId, multiColumn } = this.props;
+    const pinned = !!columnId;
+
+    return (
+      <Column ref={this.setRef}>
+        <ColumnHeader
+          icon='envelope'
+          active={hasUnread}
+          title={intl.formatMessage(messages.title)}
+          onPin={this.handlePin}
+          onMove={this.handleMove}
+          onClick={this.handleHeaderClick}
+          pinned={pinned}
+          multiColumn={multiColumn}
+        >
+          <ColumnSettingsContainer />
+        </ColumnHeader>
+
+        <StatusListContainer
+          trackScroll={!pinned}
+          scrollKey={`direct_timeline-${columnId}`}
+          timelineId='direct'
+          onLoadMore={this.handleLoadMore}
+          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
+        />
+      </Column>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js
index b17c47e91..3b29e2a26 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.js
@@ -52,7 +52,7 @@ export default class Blocks extends ImmutablePureComponent {
     }
 
     return (
-      <Column icon='ban' heading={intl.formatMessage(messages.heading)}>
+      <Column icon='minus-circle' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
         <ScrollableList scrollKey='domain_blocks' onLoadMore={this.handleLoadMore}>
           {domains.map(domain =>
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
index 1a71cff94..4a249f301 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
   navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' },
   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
@@ -98,20 +99,24 @@ export default class GettingStarted extends ImmutablePureComponent {
       }
     }
 
+    if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) {
+      navItems.push(<ColumnLink key='4' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />);
+    }
+
     navItems.push(
-      <ColumnLink key='4' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
-      <ColumnLink key='5' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
+      <ColumnLink key='5' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
+      <ColumnLink key='6' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
     );
 
     if (myAccount.get('locked')) {
-      navItems.push(<ColumnLink key='6' icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
+      navItems.push(<ColumnLink key='7' icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
     }
 
     if (multiColumn) {
-      navItems.push(<ColumnLink key='7' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />);
+      navItems.push(<ColumnLink key='8' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />);
     }
 
-    navItems.push(<ColumnLink key='8' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />);
+    navItems.push(<ColumnLink key='9' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />);
 
     return (
       <Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile>
@@ -122,7 +127,7 @@ export default class GettingStarted extends ImmutablePureComponent {
           <ColumnLink icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
           <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
           <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
-          <ColumnLink icon='ban' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
+          <ColumnLink icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
           <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
           <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
         </div>
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
index 8531e03c0..5ae7b34a2 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
@@ -53,6 +53,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent {
                 <td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td>
               </tr>
               <tr>
+                <td><kbd>x</kbd></td>
+                <td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
+              </tr>
+              <tr>
                 <td><kbd>up</kbd></td>
                 <td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
               </tr>
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index 4aa6b08f2..fc34c8cdc 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -12,6 +12,8 @@ const messages = defineMessages({
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
+  reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
+  cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
@@ -120,6 +122,8 @@ export default class ActionBar extends React.PureComponent {
     if (me === status.getIn(['account', 'id'])) {
       if (publicStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
+      } else {
+        menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick });
       }
 
       menu.push(null);
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 55eff0823..d5af2a459 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -244,6 +244,10 @@ export default class Status extends ImmutablePureComponent {
     this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
   }
 
+  handleHotkeyToggleHidden = () => {
+    this.handleToggleHidden(this.props.status);
+  }
+
   handleMoveUp = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
 
@@ -354,6 +358,7 @@ export default class Status extends ImmutablePureComponent {
       boost: this.handleHotkeyBoost,
       mention: this.handleHotkeyMention,
       openProfile: this.handleHotkeyOpenProfile,
+      toggleHidden: this.handleHotkeyToggleHidden,
     };
 
     return (
diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.js b/app/javascript/mastodon/features/ui/components/actions_modal.js
index 79a5a20ef..9792eba5f 100644
--- a/app/javascript/mastodon/features/ui/components/actions_modal.js
+++ b/app/javascript/mastodon/features/ui/components/actions_modal.js
@@ -27,7 +27,7 @@ export default class ActionsModal extends ImmutablePureComponent {
     return (
       <li key={`${text}-${i}`}>
         <a href={href} target='_blank' rel='noopener' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' />}
+          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
           <div>
             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
             <div>{meta}</div>
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 05cdb4e3b..0a62cbbeb 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -12,7 +12,7 @@ import BundleContainer from '../containers/bundle_container';
 import ColumnLoading from './column_loading';
 import DrawerLoading from './drawer_loading';
 import BundleColumnError from './bundle_column_error';
-import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
+import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
 
 import detectPassiveEvents from 'detect-passive-events';
 import { scrollRight } from '../../../scroll';
@@ -24,6 +24,7 @@ const componentMap = {
   'PUBLIC': PublicTimeline,
   'COMMUNITY': CommunityTimeline,
   'HASHTAG': HashtagTimeline,
+  'DIRECT': DirectTimeline,
   'FAVOURITES': FavouritedStatuses,
   'LIST': ListTimeline,
 };
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 8b905fa1d..adca0d617 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -30,6 +30,7 @@ import {
   Following,
   Reblogs,
   Favourites,
+  DirectTimeline,
   HashtagTimeline,
   Notifications,
   FollowRequests,
@@ -79,12 +80,14 @@ const keyMap = {
   goToNotifications: 'g n',
   goToLocal: 'g l',
   goToFederated: 'g t',
+  goToDirect: 'g d',
   goToStart: 'g s',
   goToFavourites: 'g f',
   goToPinned: 'g p',
   goToProfile: 'g u',
   goToBlocked: 'g b',
   goToMuted: 'g m',
+  toggleHidden: 'x',
 };
 
 class SwitchingColumnsArea extends React.PureComponent {
@@ -139,6 +142,7 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
           <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
           <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
+          <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
           <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
           <WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
 
@@ -385,6 +389,10 @@ export default class UI extends React.PureComponent {
     this.context.router.history.push('/timelines/public');
   }
 
+  handleHotkeyGoToDirect = () => {
+    this.context.router.history.push('/timelines/direct');
+  }
+
   handleHotkeyGoToStart = () => {
     this.context.router.history.push('/getting-started');
   }
@@ -424,6 +432,7 @@ export default class UI extends React.PureComponent {
       goToNotifications: this.handleHotkeyGoToNotifications,
       goToLocal: this.handleHotkeyGoToLocal,
       goToFederated: this.handleHotkeyGoToFederated,
+      goToDirect: this.handleHotkeyGoToDirect,
       goToStart: this.handleHotkeyGoToStart,
       goToFavourites: this.handleHotkeyGoToFavourites,
       goToPinned: this.handleHotkeyGoToPinned,
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js
index 19957208f..8cf2a6e7d 100644
--- a/app/javascript/mastodon/features/ui/util/async-components.js
+++ b/app/javascript/mastodon/features/ui/util/async-components.js
@@ -26,6 +26,10 @@ export function HashtagTimeline () {
   return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline');
 }
 
+export function DirectTimeline() {
+  return import(/* webpackChunkName: "features/direct_timeline" */'../../direct_timeline');
+}
+
 export function ListTimeline () {
   return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline');
 }
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 34e34411f..24c8a5b54 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "إعادة المحاولة",
   "column.blocks": "الحسابات المحجوبة",
   "column.community": "الخيط العام المحلي",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "المفضلة",
   "column.follow_requests": "طلبات المتابعة",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "رموز",
   "emoji_button.travel": "أماكن و أسفار",
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
   "empty_column.home.public_timeline": "الخيط العام",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "تعديل الملف الشخصي",
   "navigation_bar.favourites": "المفضلة",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} و {results}}",
   "standalone.public_title": "نظرة على ...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "تعذرت ترقية هذا المنشور",
   "status.delete": "إحذف",
+  "status.direct": "Direct message @{name}",
   "status.embed": "إدماج",
   "status.favourite": "أضف إلى المفضلة",
   "status.load_more": "حمّل المزيد",
@@ -252,6 +257,7 @@
   "status.pin": "تدبيس على الملف الشخصي",
   "status.pinned": "تبويق مثبَّت",
   "status.reblog": "رَقِّي",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} رقى",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "الرئيسية",
   "tabs_bar.local_timeline": "المحلي",
   "tabs_bar.notifications": "الإخطارات",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
   "upload_area.title": "إسحب ثم أفلت للرفع",
   "upload_button.label": "إضافة وسائط",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 9aaff0ddf..25ef6db65 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
   "column.community": "Local timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favourites",
   "column.follow_requests": "Follow requests",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Редактирай профил",
   "navigation_bar.favourites": "Favourites",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Изтриване",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Предпочитани",
   "status.load_more": "Load more",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Споделяне",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} сподели",
   "status.reply": "Отговор",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Начало",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Известия",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Добави медия",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index b7f95a664..6a44808e0 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Torna-ho a provar",
   "column.blocks": "Usuaris blocats",
   "column.community": "Línia de temps local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorits",
   "column.follow_requests": "Peticions per seguir-te",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.",
   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
   "empty_column.home.public_timeline": "la línia de temps pública",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?",
   "navigation_bar.blocks": "Usuaris bloquejats",
   "navigation_bar.community_timeline": "Línia de temps Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favorits",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, un {result} altres {results}}",
   "standalone.public_title": "Una mirada a l'interior ...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Aquesta publicació no pot ser retootejada",
   "status.delete": "Esborrar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorit",
   "status.load_more": "Carrega més",
@@ -252,6 +257,7 @@
   "status.pin": "Fixat en el perfil",
   "status.pinned": "Toot fixat",
   "status.reblog": "Impuls",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} ha retootejat",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre al tema",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inici",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacions",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.",
   "upload_area.title": "Arrossega i deixa anar per carregar",
   "upload_button.label": "Afegir multimèdia",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index a618b853e..69c2ae8d8 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Erneut versuchen",
   "column.blocks": "Blockierte Profile",
   "column.community": "Lokale Zeitleiste",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriten",
   "column.follow_requests": "Folgeanfragen",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Reisen und Orte",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
   "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
   "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profil bearbeiten",
   "navigation_bar.favourites": "Favoriten",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
   "standalone.public_title": "Ein kleiner Einblick …",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
   "status.delete": "Löschen",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Einbetten",
   "status.favourite": "Favorisieren",
   "status.load_more": "Weitere laden",
@@ -252,6 +257,7 @@
   "status.pin": "Im Profil anheften",
   "status.pinned": "Pinned toot",
   "status.reblog": "Teilen",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} teilte",
   "status.reply": "Antworten",
   "status.replyAll": "Auf Thread antworten",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Startseite",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Mitteilungen",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Zum Hochladen hereinziehen",
   "upload_button.label": "Mediendatei hinzufügen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 6f81db13e..b400349d2 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -121,6 +121,15 @@
         "id": "status.load_more"
       }
     ],
+    "path": "app/javascript/mastodon/components/load_gap.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Load more",
+        "id": "status.load_more"
+      }
+    ],
     "path": "app/javascript/mastodon/components/load_more.json"
   },
   {
@@ -234,6 +243,14 @@
         "id": "status.reblog"
       },
       {
+        "defaultMessage": "Boost to original audience",
+        "id": "status.reblog_private"
+      },
+      {
+        "defaultMessage": "Unboost",
+        "id": "status.cancel_reblog_private"
+      },
+      {
         "defaultMessage": "This post cannot be boosted",
         "id": "status.cannot_reblog"
       },
@@ -891,6 +908,19 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Direct messages",
+        "id": "column.direct"
+      },
+      {
+        "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+        "id": "empty_column.direct"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/direct_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Hidden domains",
         "id": "column.domain_blocks"
       },
@@ -963,6 +993,10 @@
         "id": "navigation_bar.community_timeline"
       },
       {
+        "defaultMessage": "Direct messages",
+        "id": "navigation_bar.direct"
+      },
+      {
         "defaultMessage": "Preferences",
         "id": "navigation_bar.preferences"
       },
@@ -1115,6 +1149,10 @@
         "id": "keyboard_shortcuts.enter"
       },
       {
+        "defaultMessage": "to show/hide text behind CW",
+        "id": "keyboard_shortcuts.toggle_hidden"
+      },
+      {
         "defaultMessage": "to move up in the list",
         "id": "keyboard_shortcuts.up"
       },
@@ -1391,6 +1429,14 @@
         "id": "status.reblog"
       },
       {
+        "defaultMessage": "Boost to original audience",
+        "id": "status.reblog_private"
+      },
+      {
+        "defaultMessage": "Unboost",
+        "id": "status.cancel_reblog_private"
+      },
+      {
         "defaultMessage": "This post cannot be boosted",
         "id": "status.cannot_reblog"
       },
@@ -1819,18 +1865,5 @@
       }
     ],
     "path": "app/javascript/mastodon/features/video/index.json"
-  },
-  {
-    "descriptors": [
-      {
-        "defaultMessage": "Oops!",
-        "id": "alert.unexpected.title"
-      },
-      {
-        "defaultMessage": "An unexpected error occurred.",
-        "id": "alert.unexpected.message"
-      }
-    ],
-    "path": "app/javascript/mastodon/middleware/errors.json"
   }
 ]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 4802ddfd1..ad6f3b712 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -245,6 +245,7 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
   "status.direct": "Direct message @{name}",
@@ -260,6 +261,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boosted",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 6dee6e544..e51163971 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Bonvolu reprovi",
   "column.blocks": "Blokitaj uzantoj",
   "column.community": "Loka tempolinio",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Stelumoj",
   "column.follow_requests": "Petoj de sekvado",
@@ -60,7 +61,7 @@
   "column_subheading.settings": "Agordado",
   "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
   "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
-  "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.",
+  "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn, kiuj estas nur por sekvantoj.",
   "compose_form.lock_disclaimer.lock": "ŝlosita",
   "compose_form.placeholder": "Pri kio vi pensas?",
   "compose_form.publish": "Hup",
@@ -100,9 +101,10 @@
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
   "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
-  "empty_column.home.public_timeline": "la publika tempolinio",
+  "empty_column.home.public_timeline": "la publikan tempolinion",
   "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn mesaĝojn, ili aperos ĉi tie.",
   "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.",
   "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj nodoj por plenigi la publikan tempolinion",
@@ -150,10 +152,11 @@
   "loading_indicator.label": "Ŝargado…",
   "media_gallery.toggle_visible": "Baskuligi videblecon",
   "missing_indicator.label": "Ne trovita",
-  "missing_indicator.sublabel": "Ĉi tiu rimedo ne estis trovita",
-  "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el ĉi tiu uzanto?",
+  "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita",
+  "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Redakti profilon",
   "navigation_bar.favourites": "Stelumoj",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
   "standalone.public_title": "Enrigardo…",
   "status.block": "Bloki @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
   "status.delete": "Forigi",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
   "status.load_more": "Ŝargi pli",
@@ -248,10 +253,11 @@
   "status.more": "Pli",
   "status.mute": "Silentigi @{name}",
   "status.mute_conversation": "Silentigi konversacion",
-  "status.open": "Grandigi ĉi tiun mesaĝon",
-  "status.pin": "Alpingli en la profilo",
+  "status.open": "Grandigi",
+  "status.pin": "Alpingli profile",
   "status.pinned": "Alpinglita mesaĝo",
   "status.reblog": "Diskonigi",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} diskonigis",
   "status.reply": "Respondi",
   "status.replyAll": "Respondi al la fadeno",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hejmo",
   "tabs_bar.local_timeline": "Loka tempolinio",
   "tabs_bar.notifications": "Sciigoj",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
   "upload_area.title": "Altreni kaj lasi por alŝuti",
   "upload_button.label": "Aldoni aŭdovidaĵon",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 6f9c06c5f..61ea0588d 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Inténtalo de nuevo",
   "column.blocks": "Usuarios bloqueados",
   "column.community": "Línea de tiempo local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Solicitudes de seguimiento",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viajes y lugares",
   "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "No hay nada en este hashtag aún.",
   "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
   "empty_column.home.public_timeline": "la línea de tiempo pública",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Un pequeño vistazo...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.delete": "Borrar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustado",
   "status.favourite": "Favorito",
   "status.load_more": "Cargar más",
@@ -252,6 +257,7 @@
   "status.pin": "Fijar",
   "status.pinned": "Toot fijado",
   "status.reblog": "Retootear",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "Retooteado por {name}",
   "status.reply": "Responder",
   "status.replyAll": "Responder al hilo",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificaciones",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
   "upload_area.title": "Arrastra y suelta para subir",
   "upload_button.label": "Subir multimedia",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 61cdcd00a..cfe93007d 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "تلاش دوباره",
   "column.blocks": "کاربران مسدودشده",
   "column.community": "نوشته‌های محلی",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "پسندیده‌ها",
   "column.follow_requests": "درخواست‌های پیگیری",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "نمادها",
   "emoji_button.travel": "سفر و مکان",
   "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
   "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "ویرایش نمایه",
   "navigation_bar.favourites": "پسندیده‌ها",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
   "standalone.public_title": "نگاهی به کاربران این سرور...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.delete": "پاک‌کردن",
+  "status.direct": "Direct message @{name}",
   "status.embed": "جاگذاری",
   "status.favourite": "پسندیدن",
   "status.load_more": "بیشتر نشان بده",
@@ -252,6 +257,7 @@
   "status.pin": "نوشتهٔ ثابت نمایه",
   "status.pinned": "Pinned toot",
   "status.reblog": "بازبوقیدن",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "‫{name}‬ بازبوقید",
   "status.reply": "پاسخ",
   "status.replyAll": "به نوشته پاسخ دهید",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "خانه",
   "tabs_bar.local_timeline": "محلی",
   "tabs_bar.notifications": "اعلان‌ها",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
   "upload_button.label": "افزودن تصویر",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index f4be80514..1677c3c6c 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -2,7 +2,7 @@
   "account.block": "Estä @{name}",
   "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
   "account.blocked": "Estetty",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direct message @{name}",
   "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.",
   "account.domain_blocked": "Verkko-osoite piilotettu",
   "account.edit_profile": "Muokkaa",
@@ -17,37 +17,38 @@
   "account.mute": "Mykistä @{name}",
   "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}",
   "account.muted": "Mykistetty",
-  "account.posts": "Töötit",
-  "account.posts_with_replies": "Töötit ja vastaukset",
-  "account.report": "Report @{name}",
-  "account.requested": "Odottaa hyväksyntää. Klikkaa peruuttaaksesi seurauspyynnön",
+  "account.posts": "Tuuttaukset",
+  "account.posts_with_replies": "Tuuttaukset ja vastaukset",
+  "account.report": "Raportoi @{name}",
+  "account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla",
   "account.share": "Jaa käyttäjän @{name} profiili",
-  "account.show_reblogs": "Näytä boostaukset käyttäjältä @{name}",
+  "account.show_reblogs": "Näytä buustaukset käyttäjältä @{name}",
   "account.unblock": "Salli @{name}",
   "account.unblock_domain": "Näytä {domain}",
   "account.unfollow": "Lakkaa seuraamasta",
-  "account.unmute": "Poista mykistys käyttäjältä @{name}",
+  "account.unmute": "Poista käyttäjän @{name} mykistys",
   "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta",
   "account.view_full_profile": "Näytä koko profiili",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
-  "boost_modal.combo": "Voit painaa näppäimiä {combo} ohittaaksesi tämän ensi kerralla",
-  "bundle_column_error.body": "Jokin meni vikaan tätä komponenttia ladatessa.",
+  "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
+  "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.",
   "bundle_column_error.retry": "Yritä uudestaan",
-  "bundle_column_error.title": "Network error",
+  "bundle_column_error.title": "Verkkovirhe",
   "bundle_modal_error.close": "Sulje",
-  "bundle_modal_error.message": "Jokin meni vikaan tätä komponenttia ladatessa.",
+  "bundle_modal_error.message": "Jokin meni vikaan komponenttia ladattaessa.",
   "bundle_modal_error.retry": "Yritä uudestaan",
   "column.blocks": "Estetyt käyttäjät",
   "column.community": "Paikallinen aikajana",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Suosikit",
-  "column.follow_requests": "Seurauspyynnöt",
+  "column.follow_requests": "Seuraamispyynnöt",
   "column.home": "Koti",
   "column.lists": "Listat",
   "column.mutes": "Mykistetyt käyttäjät",
   "column.notifications": "Ilmoitukset",
-  "column.pins": "Pinned toot",
+  "column.pins": "Kiinnitetty tuuttaus",
   "column.public": "Yleinen aikajana",
   "column_back_button.label": "Takaisin",
   "column_header.hide_settings": "Piilota asetukset",
@@ -59,32 +60,32 @@
   "column_subheading.navigation": "Navigaatio",
   "column_subheading.settings": "Asetukset",
   "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.hashtag_warning": "Tämä töötti ei tule näkymään hashtag-hauissa, koska se ei näy julkisilla aikajanoilla. Vain julkisia tööttejä voi hakea hashtageilla.",
-  "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille -postauksesi.",
+  "compose_form.hashtag_warning": "Tämä tuuttaus ei näy hashtag-hauissa, koska se on listaamaton. Hashtagien avulla voi hakea vain julkisia tuuttauksia.",
+  "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.",
   "compose_form.lock_disclaimer.lock": "lukittu",
-  "compose_form.placeholder": "Mitä sinulla on mielessä?",
-  "compose_form.publish": "Toot",
+  "compose_form.placeholder": "Mitä mietit?",
+  "compose_form.publish": "Tuuttaa",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
   "compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
   "compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
   "compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
-  "compose_form.spoiler_placeholder": "Content warning",
+  "compose_form.spoiler_placeholder": "Sisältövaroitus",
   "confirmation_modal.cancel": "Peruuta",
   "confirmations.block.confirm": "Estä",
-  "confirmations.block.message": "Oletko varma, että haluat estää käyttäjän {name}?",
-  "confirmations.delete.confirm": "Delete",
-  "confirmations.delete.message": "Oletko varma, että haluat poistaa tämän statuspäivityksen?",
-  "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Oletko varma, että haluat poistaa tämän listan pysyvästi?",
+  "confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?",
+  "confirmations.delete.confirm": "Poista",
+  "confirmations.delete.message": "Haluatko varmasti poistaa tämän tilapäivityksen?",
+  "confirmations.delete_list.confirm": "Poista",
+  "confirmations.delete_list.message": "Haluatko varmasti poistaa tämän listan kokonaan?",
   "confirmations.domain_block.confirm": "Piilota koko verkko-osoite",
-  "confirmations.domain_block.message": "Oletko aivan oikeasti varma että haluat estää koko verkko-osoitteen {domain}? Useimmissa tapauksissa muutamat kohdistetut estot ja mykistykset ovat riittäviä ja suositeltavampia.",
+  "confirmations.domain_block.message": "Haluatko aivan varmasti estää koko verkko-osoitteen {domain}? Useimmiten jokunen kohdistettu esto ja mykistys riittää, ja se on suositeltavampi tapa toimia.",
   "confirmations.mute.confirm": "Mykistä",
-  "confirmations.mute.message": "Oletko varma että haluat mykistää käyttäjän {name}?",
+  "confirmations.mute.message": "Haluatko varmasti mykistää käyttäjän {name}?",
   "confirmations.unfollow.confirm": "Lakkaa seuraamasta",
-  "confirmations.unfollow.message": "Oletko varma, että haluat lakata seuraamasta käyttäjää {name}?",
-  "embed.instructions": "Upota tämä statuspäivitys sivullesi kopioimalla alla oleva koodi.",
-  "embed.preview": "Tältä se tulee näyttämään:",
+  "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta käyttäjää {name}?",
+  "embed.instructions": "Upota statuspäivitys sivullesi kopioimalla alla oleva koodi.",
+  "embed.preview": "Se tulee näyttämään tältä:",
   "emoji_button.activity": "Aktiviteetit",
   "emoji_button.custom": "Mukautetut",
   "emoji_button.flags": "Liput",
@@ -92,154 +93,158 @@
   "emoji_button.label": "Lisää emoji",
   "emoji_button.nature": "Luonto",
   "emoji_button.not_found": "Ei emojeja!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Objektit",
+  "emoji_button.objects": "Esineet",
   "emoji_button.people": "Ihmiset",
   "emoji_button.recent": "Usein käytetyt",
   "emoji_button.search": "Etsi...",
   "emoji_button.search_results": "Hakutulokset",
   "emoji_button.symbols": "Symbolit",
   "emoji_button.travel": "Matkailu",
-  "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista saadaksesi pyörät pyörimään!",
-  "empty_column.hashtag": "Tässä hashtagissa ei ole vielä mitään.",
-  "empty_column.home": "Kotiaikajanasi on tyhjä! Käy vierailemassa {public}ssa tai käytä hakutoimintoa aloittaaksesi ja tavataksesi muita käyttäjiä.",
+  "empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.hashtag": "Tällä hashtagilla ei ole vielä mitään.",
+  "empty_column.home": "Kotiaikajanasi on tyhjä! {public} ja hakutoiminto auttavat alkuun ja kohtaamaan muita käyttäjiä.",
   "empty_column.home.public_timeline": "yleinen aikajana",
-  "empty_column.list": "Tämä lista on vielä tyhjä. Kun listan jäsenet julkaisevat statuspäivityksiä, ne näkyvät tässä.",
-  "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Juttele muille aloittaaksesi keskustelun.",
-  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti, tai käy manuaalisesti seuraamassa käyttäjiä muista instansseista saadaksesi sisältöä",
+  "empty_column.list": "Lista on vielä tyhjä. Listan jäsenten julkaisemat tilapäivitykset tulevat tähän näkyviin.",
+  "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Aloita keskustelu juttelemalla muille.",
+  "empty_column.public": "Täällä ei ole mitään! Saat sisältöä, kun kirjoitat jotain julkisesti tai käyt manuaalisesti seuraamassa muiden instanssien käyttäjiä",
   "follow_request.authorize": "Valtuuta",
   "follow_request.reject": "Hylkää",
   "getting_started.appsshort": "Sovellukset",
   "getting_started.faq": "FAQ",
   "getting_started.heading": "Aloitus",
-  "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}.",
+  "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.",
   "getting_started.userguide": "Käyttöopas",
-  "home.column_settings.advanced": "Tarkemmat asetukset",
+  "home.column_settings.advanced": "Lisäasetukset",
   "home.column_settings.basic": "Perusasetukset",
-  "home.column_settings.filter_regex": "Suodata säännöllisten lauseiden avulla",
+  "home.column_settings.filter_regex": "Suodata säännöllisillä lausekkeilla",
   "home.column_settings.show_reblogs": "Näytä buustaukset",
   "home.column_settings.show_replies": "Näytä vastaukset",
   "home.settings": "Sarakeasetukset",
-  "keyboard_shortcuts.back": "liikkuaksesi taaksepäin",
-  "keyboard_shortcuts.boost": "buustataksesi",
-  "keyboard_shortcuts.column": "keskittääksesi statuspäivitykseen yhdessä sarakkeista",
-  "keyboard_shortcuts.compose": "aktivoidaksesi tekstinkirjoitusalueen",
-  "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.down": "liikkuaksesi listassa alaspäin",
-  "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "tykätäksesi",
-  "keyboard_shortcuts.heading": "Näppäinoikotiet",
+  "keyboard_shortcuts.back": "liiku taaksepäin",
+  "keyboard_shortcuts.boost": "buustaa",
+  "keyboard_shortcuts.column": "siirrä fokus tietyn sarakkeen tilapäivitykseen",
+  "keyboard_shortcuts.compose": "siirry tekstinsyöttöön",
+  "keyboard_shortcuts.description": "Kuvaus",
+  "keyboard_shortcuts.down": "siirry listassa alaspäin",
+  "keyboard_shortcuts.enter": "avaa tilapäivitys",
+  "keyboard_shortcuts.favourite": "tykkää",
+  "keyboard_shortcuts.heading": "Näppäinkomennot",
   "keyboard_shortcuts.hotkey": "Pikanäppäin",
-  "keyboard_shortcuts.legend": "näyttääksesi tämän selitteen",
-  "keyboard_shortcuts.mention": "mainitaksesi julkaisijan",
-  "keyboard_shortcuts.reply": "vastataksesi",
-  "keyboard_shortcuts.search": "aktivoidaksesi hakukentän",
-  "keyboard_shortcuts.toot": "aloittaaksesi uuden töötin kirjoittamisen",
-  "keyboard_shortcuts.unfocus": "poistaaksesi aktivoinnin tekstikentästä/hakukentästä",
-  "keyboard_shortcuts.up": "liikkuaksesi listassa ylöspäin",
+  "keyboard_shortcuts.legend": "näytä tämä selite",
+  "keyboard_shortcuts.mention": "mainitse julkaisija",
+  "keyboard_shortcuts.reply": "vastaa",
+  "keyboard_shortcuts.search": "siirry hakukenttään",
+  "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta",
+  "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä",
+  "keyboard_shortcuts.up": "siirry listassa ylöspäin",
   "lightbox.close": "Sulje",
   "lightbox.next": "Seuraava",
   "lightbox.previous": "Edellinen",
   "lists.account.add": "Lisää listaan",
-  "lists.account.remove": "Poista listalta",
-  "lists.delete": "Delete list",
+  "lists.account.remove": "Poista listasta",
+  "lists.delete": "Poista lista",
   "lists.edit": "Muokkaa listaa",
   "lists.new.create": "Lisää lista",
-  "lists.new.title_placeholder": "Uuden listan otsikko",
-  "lists.search": "Etsi seuraamiesi henkilöiden joukosta",
+  "lists.new.title_placeholder": "Uuden listan nimi",
+  "lists.search": "Etsi seuraamistasi henkilöistä",
   "lists.subheading": "Omat listat",
   "loading_indicator.label": "Ladataan...",
   "media_gallery.toggle_visible": "Säädä näkyvyyttä",
-  "missing_indicator.label": "Ei löydetty",
+  "missing_indicator.label": "Ei löytynyt",
   "missing_indicator.sublabel": "Tätä resurssia ei löytynyt",
-  "mute_modal.hide_notifications": "Piilota ilmoitukset tältä käyttäjältä?",
+  "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?",
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Muokkaa profiilia",
   "navigation_bar.favourites": "Suosikit",
-  "navigation_bar.follow_requests": "Seurauspyynnöt",
+  "navigation_bar.follow_requests": "Seuraamispyynnöt",
   "navigation_bar.info": "Tietoa tästä instanssista",
-  "navigation_bar.keyboard_shortcuts": "Näppäinoikotiet",
+  "navigation_bar.keyboard_shortcuts": "Näppäinkomennot",
   "navigation_bar.lists": "Listat",
   "navigation_bar.logout": "Kirjaudu ulos",
   "navigation_bar.mutes": "Mykistetyt käyttäjät",
-  "navigation_bar.pins": "Kiinnitetyt töötit",
-  "navigation_bar.preferences": "Ominaisuudet",
+  "navigation_bar.pins": "Kiinnitetyt tuuttaukset",
+  "navigation_bar.preferences": "Asetukset",
   "navigation_bar.public_timeline": "Yleinen aikajana",
-  "notification.favourite": "{name} tykkäsi statuksestasi",
+  "notification.favourite": "{name} tykkäsi tilastasi",
   "notification.follow": "{name} seurasi sinua",
   "notification.mention": "{name} mainitsi sinut",
-  "notification.reblog": "{name} buustasi statustasi",
+  "notification.reblog": "{name} buustasi tilaasi",
   "notifications.clear": "Tyhjennä ilmoitukset",
-  "notifications.clear_confirmation": "Oletko varma, että haluat lopullisesti tyhjentää kaikki ilmoituksesi?",
-  "notifications.column_settings.alert": "Työpöytä ilmoitukset",
-  "notifications.column_settings.favourite": "Tykkäyksiä:",
-  "notifications.column_settings.follow": "Uusia seuraajia:",
-  "notifications.column_settings.mention": "Mainintoja:",
+  "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?",
+  "notifications.column_settings.alert": "Työpöytäilmoitukset",
+  "notifications.column_settings.favourite": "Tykkäykset:",
+  "notifications.column_settings.follow": "Uudet seuraajat:",
+  "notifications.column_settings.mention": "Maininnat:",
   "notifications.column_settings.push": "Push-ilmoitukset",
   "notifications.column_settings.push_meta": "Tämä laite",
-  "notifications.column_settings.reblog": "Buusteja:",
+  "notifications.column_settings.reblog": "Buustit:",
   "notifications.column_settings.show": "Näytä sarakkeessa",
-  "notifications.column_settings.sound": "Soita ääni",
+  "notifications.column_settings.sound": "Äänimerkki",
   "onboarding.done": "Valmis",
   "onboarding.next": "Seuraava",
-  "onboarding.page_five.public_timelines": "Paikallinen aikajana näyttää kaikki julkiset julkaisut kaikilta, jotka ovat verkko-osoitteessa {domain}. Yleinen aikajana näyttää julkiset julkaisut kaikilta niiltä, joita käyttäjät verkko-osoitteessa {domain} seuraavat. Nämä ovat julkiset aikajanat, ja ne ovat hyviä tapoja löytää uusia ihmisiä.",
-  "onboarding.page_four.home": "Kotiaikajana näyttää julkaisut ihmisiltä joita seuraat.",
-  "onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.",
-  "onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.",
+  "onboarding.page_five.public_timelines": "Paikallisella aikajanalla näytetään instanssin {domain} kaikkien käyttäjien julkiset julkaisut. Yleisellä aikajanalla näytetään kaikkien instanssin {domain} käyttäjien seuraamien käyttäjien julkiset julkaisut. Nämä julkiset aikajanat ovat loistavia paikkoja löytää uusia ihmisiä.",
+  "onboarding.page_four.home": "Kotiaikajanalla näytetään seuraamiesi ihmisten julkaisut.",
+  "onboarding.page_four.notifications": "Ilmoitukset-sarakkeessa näytetään muiden sinuun liittyvä toiminta.",
+  "onboarding.page_one.federation": "Mastodon on usean itsenäisen palvelimen muodostama yhteisöpalvelu. Näitä palvelimia kutsutaan instansseiksi.",
   "onboarding.page_one.full_handle": "Koko käyttäjänimesi",
-  "onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.",
+  "onboarding.page_one.handle_hint": "Tällä nimellä ystäväsi löytävät sinut.",
   "onboarding.page_one.welcome": "Tervetuloa Mastodoniin!",
-  "onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.",
+  "onboarding.page_six.admin": "Instanssin ylläpitäjä on {admin}.",
   "onboarding.page_six.almost_done": "Melkein valmista...",
-  "onboarding.page_six.appetoot": "Bon Appetööt!",
+  "onboarding.page_six.appetoot": "Tuuttailun iloa!",
   "onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.",
-  "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, pyytää ominaisuuksia tai osallistua kehittämiseen GitHub-palvelussa: {github}.",
+  "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, ehdottaa ominaisuuksia tai osallistua kehittämiseen GitHubissa: {github}.",
   "onboarding.page_six.guidelines": "yhteisön säännöt",
   "onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!",
   "onboarding.page_six.various_app": "mobiilisovellukset",
-  "onboarding.page_three.profile": "Muokkaa profiiliasi muuttaaksesi kuvakettasi, esittelyäsi ja nimimerkkiäsi. Löydät sieltä myös muita henkilökohtaisia asetuksia.",
-  "onboarding.page_three.search": "Käytä hakukenttää löytääksesi ihmisiä ja etsiäksesi hashtageja, kuten {illustration} tai {introductions}. Hakeaksesi henkilöä joka on toisessa instanssissa, käytä hänen käyttäjänimeään kokonaisuudessaan.",
-  "onboarding.page_two.compose": "Kirjoita postauksia kirjoita-sarakkeessa. Voit ladata kuvia, vaihtaa yksityisyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.",
+  "onboarding.page_three.profile": "Voit muuttaa profiilikuvaasi, esittelyäsi ja nimimerkkiäsi sekä muita asetuksia muokkaamalla profiiliasi.",
+  "onboarding.page_three.search": "Etsi ihmisiä ja hashtageja (esimerkiksi {illustration} tai {introductions}) hakukentän avulla. Jos haet toista instanssia käyttävää henkilöä, käytä hänen koko käyttäjänimeään.",
+  "onboarding.page_two.compose": "Kirjoita julkaisuja kirjoitussarakkeessa. Voit ladata kuvia, vaihtaa näkyvyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.",
   "onboarding.skip": "Ohita",
-  "privacy.change": "Säädä töötin yksityisyysasetuksia",
+  "privacy.change": "Säädä tuuttauksen näkyvyyttä",
   "privacy.direct.long": "Julkaise vain mainituille käyttäjille",
-  "privacy.direct.short": "Yksityisviesti",
+  "privacy.direct.short": "Suora viesti",
   "privacy.private.long": "Julkaise vain seuraajille",
   "privacy.private.short": "Vain seuraajat",
   "privacy.public.long": "Julkaise julkisille aikajanoille",
   "privacy.public.short": "Julkinen",
-  "privacy.unlisted.long": "Älä julkaise yleisillä aikajanoilla",
-  "privacy.unlisted.short": "Julkinen, mutta älä näytä julkisella aikajanalla",
+  "privacy.unlisted.long": "Älä julkaise julkisilla aikajanoilla",
+  "privacy.unlisted.short": "Listaamaton julkinen",
   "regeneration_indicator.label": "Ladataan…",
   "regeneration_indicator.sublabel": "Kotinäkymääsi valmistellaan!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
+  "relative_time.days": "{number} pv",
+  "relative_time.hours": "{number} h",
   "relative_time.just_now": "nyt",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
+  "relative_time.minutes": "{number} m",
+  "relative_time.seconds": "{number} s",
   "reply_indicator.cancel": "Peruuta",
-  "report.forward": "Uudelleenohjaa kohteeseen {target}",
-  "report.forward_hint": "Tämä tili on toiselta serveriltä. Haluatko, että myös sinne lähetetään anonymisoitu kopio ilmiantoraportista?",
-  "report.hint": "Ilmianto lähetetään instanssisi moderaattoreille. Voit antaa kuvauksen käyttäjän ilmiantamisen syystä alle:",
+  "report.forward": "Välitä kohteeseen {target}",
+  "report.forward_hint": "Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?",
+  "report.hint": "Raportti lähetetään oman instanssisi moderaattoreille. Seuraavassa voit kertoa, miksi raportoit tästä tilistä:",
   "report.placeholder": "Lisäkommentit",
-  "report.submit": "Submit",
-  "report.target": "Reporting",
+  "report.submit": "Lähetä",
+  "report.target": "Raportoidaan {target}",
   "search.placeholder": "Hae",
   "search_popout.search_format": "Tarkennettu haku",
-  "search_popout.tips.full_text": "Tekstihaku palauttaa statuspäivitykset jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä käyttäjänimet, nimimerkit ja hastagit jotka sisältävät tekstin.",
-  "search_popout.tips.hashtag": "hashtagi",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Pelkkä tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
+  "search_popout.tips.full_text": "Tekstihaku palauttaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja hastagit.",
+  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.status": "tila",
+  "search_popout.tips.text": "Tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
   "search_popout.tips.user": "käyttäjä",
   "search_results.accounts": "Ihmiset",
   "search_results.hashtags": "Hashtagit",
-  "search_results.statuses": "Töötit",
+  "search_results.statuses": "Tuuttaukset",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Kurkistus sisälle...",
-  "status.block": "Block @{name}",
-  "status.cannot_reblog": "Tätä postausta ei voi buustata",
+  "status.block": "Estä @{name}",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "Tätä julkaisua ei voi buustata",
   "status.delete": "Poista",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Upota",
   "status.favourite": "Tykkää",
   "status.load_more": "Lataa lisää",
@@ -248,29 +253,31 @@
   "status.more": "Lisää",
   "status.mute": "Mykistä @{name}",
   "status.mute_conversation": "Mykistä keskustelu",
-  "status.open": "Laajenna statuspäivitys",
+  "status.open": "Laajenna tilapäivitys",
   "status.pin": "Kiinnitä profiiliin",
-  "status.pinned": "Kiinnitetty töötti",
+  "status.pinned": "Kiinnitetty tuuttaus",
   "status.reblog": "Buustaa",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} buustasi",
   "status.reply": "Vastaa",
   "status.replyAll": "Vastaa ketjuun",
-  "status.report": "Report @{name}",
+  "status.report": "Raportoi @{name}",
   "status.sensitive_toggle": "Klikkaa nähdäksesi",
   "status.sensitive_warning": "Arkaluontoista sisältöä",
   "status.share": "Jaa",
   "status.show_less": "Näytä vähemmän",
   "status.show_less_all": "Näytä vähemmän kaikista",
   "status.show_more": "Näytä lisää",
-  "status.show_more_all": "Näytä enemmän kaikista",
-  "status.unmute_conversation": "Poista mykistys keskustelulta",
+  "status.show_more_all": "Näytä lisää kaikista",
+  "status.unmute_conversation": "Poista keskustelun mykistys",
   "status.unpin": "Irrota profiilista",
   "tabs_bar.federated_timeline": "Yleinen",
   "tabs_bar.home": "Koti",
   "tabs_bar.local_timeline": "Paikallinen",
   "tabs_bar.notifications": "Ilmoitukset",
-  "ui.beforeunload": "Luonnoksesi menetetään, jos poistut Mastodonista.",
-  "upload_area.title": "Raahaa ja pudota tähän ladataksesi",
+  "tabs_bar.search": "Search",
+  "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
+  "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän",
   "upload_button.label": "Lisää mediaa",
   "upload_form.description": "Anna kuvaus näkörajoitteisia varten",
   "upload_form.focus": "Rajaa",
@@ -279,10 +286,10 @@
   "video.close": "Sulje video",
   "video.exit_fullscreen": "Poistu koko näytön tilasta",
   "video.expand": "Laajenna video",
-  "video.fullscreen": "Full screen",
+  "video.fullscreen": "Koko näyttö",
   "video.hide": "Piilota video",
   "video.mute": "Mykistä ääni",
   "video.pause": "Keskeytä",
   "video.play": "Toista",
-  "video.unmute": "Poista mykistys ääneltä"
+  "video.unmute": "Poista äänen mykistys"
 }
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 57c55c9bd..98c1c43d2 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Réessayer",
   "column.blocks": "Comptes bloqués",
   "column.community": "Fil public local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoris",
   "column.follow_requests": "Demandes de suivi",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux & Voyages",
   "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.",
   "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.",
   "empty_column.home.public_timeline": "le fil public",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifier le profil",
   "navigation_bar.favourites": "Favoris",
@@ -205,8 +208,8 @@
   "privacy.change": "Ajuster la confidentialité du message",
   "privacy.direct.long": "N'envoyer qu'aux personnes mentionnées",
   "privacy.direct.short": "Direct",
-  "privacy.private.long": "N'envoyer qu'à vos abonné⋅e⋅s",
-  "privacy.private.short": "Privé",
+  "privacy.private.long": "Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts",
+  "privacy.private.short": "Abonné⋅e⋅s uniquement",
   "privacy.public.long": "Afficher dans les fils publics",
   "privacy.public.short": "Public",
   "privacy.unlisted.long": "Ne pas afficher dans les fils publics",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}",
   "standalone.public_title": "Un aperçu …",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Cette publication ne peut être boostée",
   "status.delete": "Effacer",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Intégrer",
   "status.favourite": "Ajouter aux favoris",
   "status.load_more": "Charger plus",
@@ -252,6 +257,7 @@
   "status.pin": "Épingler sur le profil",
   "status.pinned": "Pouet épinglé",
   "status.reblog": "Partager",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} a partagé :",
   "status.reply": "Répondre",
   "status.replyAll": "Répondre au fil",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Accueil",
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 8d586404d..fca42374d 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -2,7 +2,7 @@
   "account.block": "Bloquear @{name}",
   "account.block_domain": "Ocultar calquer contido de {domain}",
   "account.blocked": "Blocked",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direct message @{name}",
   "account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "Editar perfil",
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Inténteo de novo",
   "column.blocks": "Usuarias bloqueadas",
   "column.community": "Liña temporal local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritas",
   "column.follow_requests": "Peticións de seguimento",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes e Lugares",
   "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.",
   "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.",
   "empty_column.home.public_timeline": "a liña temporal pública",
@@ -148,12 +150,13 @@
   "lists.search": "Procurar entre a xente que segues",
   "lists.subheading": "As túas listas",
   "loading_indicator.label": "Cargando...",
-  "media_gallery.toggle_visible": "Dar visibilidade",
+  "media_gallery.toggle_visible": "Ocultar",
   "missing_indicator.label": "Non atopado",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Esconder notificacións deste usuario?",
   "navigation_bar.blocks": "Usuarias bloqueadas",
   "navigation_bar.community_timeline": "Liña temporal local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritas",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count,plural,one {result} outros {results}}",
   "standalone.public_title": "Ollada dentro...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Esta mensaxe non pode ser promocionada",
   "status.delete": "Eliminar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorita",
   "status.load_more": "Cargar máis",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Promover",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} promoveu",
   "status.reply": "Resposta",
   "status.replyAll": "Resposta a conversa",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacións",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
   "upload_area.title": "Arrastre e solte para subir",
   "upload_button.label": "Engadir medios",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 6bec26fd8..e3e87f1d0 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "לנסות שוב",
   "column.blocks": "חסימות",
   "column.community": "ציר זמן מקומי",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "חיבובים",
   "column.follow_requests": "בקשות מעקב",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "סמלים",
   "emoji_button.travel": "טיולים ואתרים",
   "empty_column.community": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.",
   "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.",
   "empty_column.home.public_timeline": "ציר זמן בין-קהילתי",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?",
   "navigation_bar.blocks": "חסימות",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "עריכת פרופיל",
   "navigation_bar.favourites": "חיבובים",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}",
   "standalone.public_title": "הצצה פנימה...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "לא ניתן להדהד הודעה זו",
   "status.delete": "מחיקה",
+  "status.direct": "Direct message @{name}",
   "status.embed": "הטמעה",
   "status.favourite": "חיבוב",
   "status.load_more": "עוד",
@@ -252,6 +257,7 @@
   "status.pin": "לקבע באודות",
   "status.pinned": "Pinned toot",
   "status.reblog": "הדהוד",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "הודהד על ידי {name}",
   "status.reply": "תגובה",
   "status.replyAll": "תגובה לכולם",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "בבית",
   "tabs_bar.local_timeline": "ציר זמן מקומי",
   "tabs_bar.notifications": "התראות",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.",
   "upload_area.title": "ניתן להעלות על ידי Drag & drop",
   "upload_button.label": "הוספת מדיה",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index f7a5d0a3f..b41c98394 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokirani korisnici",
   "column.community": "Lokalni timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriti",
   "column.follow_requests": "Zahtjevi za slijeđenje",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja & Mjesta",
   "empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
   "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.",
   "empty_column.home.public_timeline": "javni timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalni timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Uredi profil",
   "navigation_bar.favourites": "Favoriti",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj post ne može biti boostan",
   "status.delete": "Obriši",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Označi omiljenim",
   "status.load_more": "Učitaj više",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Podigni",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} je podigao",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na temu",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Dom",
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Notifikacije",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Povuci i spusti kako bi uploadao",
   "upload_button.label": "Dodaj media",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 8b9c14993..956accc67 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Próbálja újra",
   "column.blocks": "Letiltott felhasználók",
   "column.community": "Helyi idővonal",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Kedvencek",
   "column.follow_requests": "Követési kérések",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Szimbólumok",
   "emoji_button.travel": "Utazás és Helyek",
   "empty_column.community": "A helyi idővonal üres. Írj egy publikus stástuszt, hogy elindítsd a labdát!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Jelenleg nem található semmi ezen hashtaggel.",
   "empty_column.home": "A hazai idővonala üres! Látogasd meg a {public} vagy használd a keresőt, hogy ismerj meg más felhasználókat.",
   "empty_column.home.public_timeline": "publikus idővonal",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Értesítések elrejtése ezen felhasználótól?",
   "navigation_bar.blocks": "Tiltott felhasználók",
   "navigation_bar.community_timeline": "Helyi idővonal",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profil szerkesztése",
   "navigation_bar.favourites": "Kedvencek",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Betekintés...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ezen státusz nem rebloggolható",
   "status.delete": "Törlés",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Beágyaz",
   "status.favourite": "Kedvenc",
   "status.load_more": "Többet",
@@ -252,6 +257,7 @@
   "status.pin": "Kitűzés a profilra",
   "status.pinned": "Pinned toot",
   "status.reblog": "Reblog",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} reblogolta",
   "status.reply": "Válasz",
   "status.replyAll": "Válaszolj a beszélgetésre",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Kezdőlap",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Értesítések",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.",
   "upload_area.title": "Húzza ide a feltöltéshez",
   "upload_button.label": "Média hozzáadása",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 22ba89a43..33e079201 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Կրկին փորձել",
   "column.blocks": "Արգելափակված օգտատերեր",
   "column.community": "Տեղական հոսք",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Հավանածներ",
   "column.follow_requests": "Հետեւելու հայցեր",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Նշաններ",
   "emoji_button.travel": "Ուղեւորություն եւ տեղանքներ",
   "empty_column.community": "Տեղական հոսքը դատա՛րկ է։ Հրապարակային մի բան գրիր շարժիչը խոդ տալու համար։",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Այս պիտակով դեռ ոչինչ չկա։",
   "empty_column.home": "Քո հիմնական հոսքը դատա՛րկ է։ Այցելի՛ր {public}ը կամ օգտվիր որոնումից՝ այլ մարդկանց հանդիպելու համար։",
   "empty_column.home.public_timeline": "հրապարակային հոսք",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։",
   "navigation_bar.blocks": "Արգելափակված օգտատերեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Խմբագրել անձնական էջը",
   "navigation_bar.favourites": "Հավանածներ",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Այս պահին…",
   "status.block": "Արգելափակել @{name}֊ին",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Այս թութը չի կարող տարածվել",
   "status.delete": "Ջնջել",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Ներդնել",
   "status.favourite": "Հավանել",
   "status.load_more": "Բեռնել ավելին",
@@ -252,6 +257,7 @@
   "status.pin": "Ամրացնել անձնական էջում",
   "status.pinned": "Pinned toot",
   "status.reblog": "Տարածել",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} տարածել է",
   "status.reply": "Պատասխանել",
   "status.replyAll": "Պատասխանել թելին",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Հիմնական",
   "tabs_bar.local_timeline": "Տեղական",
   "tabs_bar.notifications": "Ծանուցումներ",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։",
   "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
   "upload_button.label": "Ավելացնել մեդիա",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 1ef610fba..412ffd3a0 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Coba lagi",
   "column.blocks": "Pengguna diblokir",
   "column.community": "Linimasa Lokal",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorit",
   "column.follow_requests": "Permintaan mengikuti",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simbol",
   "emoji_button.travel": "Tempat Wisata",
   "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
   "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
   "empty_column.home.public_timeline": "linimasa publik",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.community_timeline": "Linimasa lokal",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Ubah profil",
   "navigation_bar.favourites": "Favorit",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Hapus",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Difavoritkan",
   "status.load_more": "Tampilkan semua",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "di-boost {name}",
   "status.reply": "Balas",
   "status.replyAll": "Balas ke semua",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Beranda",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Notifikasi",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
   "upload_area.title": "Seret & lepaskan untuk mengunggah",
   "upload_button.label": "Tambahkan media",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 1435178a8..9730bf934 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokusita uzeri",
   "column.community": "Lokala tempolineo",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorati",
   "column.follow_requests": "Demandi di sequado",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.",
   "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.",
   "empty_column.home.public_timeline": "la publika tempolineo",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokusita uzeri",
   "navigation_bar.community_timeline": "Lokala tempolineo",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifikar profilo",
   "navigation_bar.favourites": "Favorati",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Efacar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorizar",
   "status.load_more": "Kargar pluse",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Repetar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} repetita",
   "status.reply": "Respondar",
   "status.replyAll": "Respondar a filo",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hemo",
   "tabs_bar.local_timeline": "Lokala",
   "tabs_bar.notifications": "Savigi",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Tranar faligar por kargar",
   "upload_button.label": "Adjuntar kontenajo",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 226127e6b..5146d7ca2 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Utenti bloccati",
   "column.community": "Timeline locale",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Apprezzati",
   "column.follow_requests": "Richieste di amicizia",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.",
   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.",
   "empty_column.home.public_timeline": "la timeline pubblica",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifica profilo",
   "navigation_bar.favourites": "Apprezzati",
@@ -238,8 +241,10 @@
   "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Elimina",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Apprezzato",
   "status.load_more": "Mostra di più",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Condividi",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} ha condiviso",
   "status.reply": "Rispondi",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Locale",
   "tabs_bar.notifications": "Notifiche",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Trascina per caricare",
   "upload_button.label": "Aggiungi file multimediale",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 0efd367e9..abd18742a 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -159,6 +159,7 @@
   "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "非表示にしたドメイン",
   "navigation_bar.edit_profile": "プロフィールを編集",
   "navigation_bar.favourites": "お気に入り",
@@ -192,7 +193,7 @@
   "onboarding.page_five.public_timelines": "連合タイムラインでは{domain}の人がフォローしているMastodon全体での公開投稿を表示します。同じくローカルタイムラインでは{domain}のみの公開投稿を表示します。",
   "onboarding.page_four.home": "「ホーム」タイムラインではあなたがフォローしている人の投稿を表示します。",
   "onboarding.page_four.notifications": "「通知」ではあなたへの他の人からの関わりを表示します。",
-  "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。",
+  "onboarding.page_one.federation": "Mastodonは独立したインスタンス(サーバー)の集合体です。",
   "onboarding.page_one.full_handle": "あなたのフルハンドル",
   "onboarding.page_one.handle_hint": "あなたを探している友達に伝えるといいでしょう。",
   "onboarding.page_one.welcome": "Mastodonへようこそ!",
@@ -244,8 +245,10 @@
   "search_results.total": "{count, number}件の結果",
   "standalone.public_title": "今こんな話をしています...",
   "status.block": "@{name}さんをブロック",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.delete": "削除",
+  "status.direct": "@{name}さんにダイレクトメッセージ",
   "status.embed": "埋め込み",
   "status.favourite": "お気に入り",
   "status.load_more": "もっと見る",
@@ -258,6 +261,7 @@
   "status.pin": "プロフィールに固定表示",
   "status.pinned": "固定されたトゥート",
   "status.reblog": "ブースト",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name}さんがブースト",
   "status.reply": "返信",
   "status.replyAll": "全員に返信",
@@ -275,6 +279,7 @@
   "tabs_bar.home": "ホーム",
   "tabs_bar.local_timeline": "ローカル",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "検索",
   "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
   "upload_area.title": "ドラッグ&ドロップでアップロード",
   "upload_button.label": "メディアを追加",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 89fde8966..92367dc95 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "다시 시도",
   "column.blocks": "차단 중인 사용자",
   "column.community": "로컬 타임라인",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "즐겨찾기",
   "column.follow_requests": "팔로우 요청",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "기호",
   "emoji_button.travel": "여행과 장소",
   "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
   "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.",
   "empty_column.home.public_timeline": "연합 타임라인",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "프로필 편집",
   "navigation_bar.favourites": "즐겨찾기",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number}건의 결과",
   "standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
   "status.block": "@{name} 차단",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
   "status.delete": "삭제",
+  "status.direct": "Direct message @{name}",
   "status.embed": "공유하기",
   "status.favourite": "즐겨찾기",
   "status.load_more": "더 보기",
@@ -252,6 +257,7 @@
   "status.pin": "고정",
   "status.pinned": "고정 된 툿",
   "status.reblog": "부스트",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name}님이 부스트 했습니다",
   "status.reply": "답장",
   "status.replyAll": "전원에게 답장",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "홈",
   "tabs_bar.local_timeline": "로컬",
   "tabs_bar.notifications": "알림",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
   "upload_area.title": "드래그 & 드롭으로 업로드",
   "upload_button.label": "미디어 추가",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 7bfb74a5c..c18ddbd01 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Opnieuw proberen",
   "column.blocks": "Geblokkeerde gebruikers",
   "column.community": "Lokale tijdlijn",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorieten",
   "column.follow_requests": "Volgverzoeken",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
   "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
   "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.",
   "empty_column.home.public_timeline": "de globale tijdlijn",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profiel bewerken",
   "navigation_bar.favourites": "Favorieten",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
   "standalone.public_title": "Een kijkje binnenin...",
   "status.block": "Blokkeer @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Deze toot kan niet geboost worden",
   "status.delete": "Verwijderen",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favoriet",
   "status.load_more": "Meer laden",
@@ -252,6 +257,7 @@
   "status.pin": "Aan profielpagina vastmaken",
   "status.pinned": "Vastgemaakte toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boostte",
   "status.reply": "Reageren",
   "status.replyAll": "Reageer op iedereen",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Start",
   "tabs_bar.local_timeline": "Lokaal",
   "tabs_bar.notifications": "Meldingen",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
   "upload_area.title": "Hierin slepen om te uploaden",
   "upload_button.label": "Media toevoegen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index b79277ce3..282a72acb 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Prøv igjen",
   "column.blocks": "Blokkerte brukere",
   "column.community": "Lokal tidslinje",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Likt",
   "column.follow_requests": "Følgeforespørsler",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Reise & steder",
   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.",
   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.",
   "empty_column.home.public_timeline": "en offentlig tidslinje",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?",
   "navigation_bar.blocks": "Blokkerte brukere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Rediger profil",
   "navigation_bar.favourites": "Favoritter",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
   "standalone.public_title": "En titt inni...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Denne posten kan ikke fremheves",
   "status.delete": "Slett",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Bygge inn",
   "status.favourite": "Lik",
   "status.load_more": "Last mer",
@@ -252,6 +257,7 @@
   "status.pin": "Fest på profilen",
   "status.pinned": "Pinned toot",
   "status.reblog": "Fremhev",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "Fremhevd av {name}",
   "status.reply": "Svar",
   "status.replyAll": "Svar til samtale",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hjem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Varslinger",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
   "upload_area.title": "Dra og slipp for å laste opp",
   "upload_button.label": "Legg til media",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 5b12f8811..7170aefb8 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tornar ensajar",
   "column.blocks": "Personas blocadas",
   "column.community": "Flux public local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorits",
   "column.follow_requests": "Demandas d’abonament",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simbòls",
   "emoji_button.travel": "Viatges & lòcs",
   "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.",
   "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.public_timeline": "lo flux public",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modificar lo perfil",
   "navigation_bar.favourites": "Favorits",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
   "standalone.public_title": "Una ulhada dedins…",
   "status.block": "Blocar @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
   "status.delete": "Escafar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embarcar",
   "status.favourite": "Apondre als favorits",
   "status.load_more": "Cargar mai",
@@ -252,6 +257,7 @@
   "status.pin": "Penjar al perfil",
   "status.pinned": "Tut penjat",
   "status.reblog": "Partejar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} a partejat",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre a la conversacion",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Acuèlh",
   "tabs_bar.local_timeline": "Flux public local",
   "tabs_bar.notifications": "Notificacions",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.",
   "upload_area.title": "Lisatz e depausatz per mandar",
   "upload_button.label": "Ajustar un mèdia",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 82b7070b8..08aea797d 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -2,7 +2,7 @@
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Blokuj wszystko z {domain}",
   "account.blocked": "Zablokowany",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Wyślij wiadomość bezpośrednią do @{name}",
   "account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.",
   "account.domain_blocked": "Ukryto domenę",
   "account.edit_profile": "Edytuj profil",
@@ -138,6 +138,7 @@
   "keyboard_shortcuts.mention": "aby wspomnieć o autorze",
   "keyboard_shortcuts.reply": "aby odpowiedzieć",
   "keyboard_shortcuts.search": "aby przejść do pola wyszukiwania",
+  "keyboard_shortcuts.toggle_hidden": "aby wyświetlić lub ukryć wpis spod CW",
   "keyboard_shortcuts.toot": "aby utworzyć nowy wpis",
   "keyboard_shortcuts.unfocus": "aby opuścić pole wyszukiwania/pisania",
   "keyboard_shortcuts.up": "aby przejść na górę listy",
@@ -245,8 +246,10 @@
   "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}",
   "standalone.public_title": "Spojrzenie w głąb…",
   "status.block": "Zablokuj @{name}",
+  "status.cancel_reblog_private": "Cofnij podbicie",
   "status.cannot_reblog": "Ten wpis nie może zostać podbity",
   "status.delete": "Usuń",
+  "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
   "status.embed": "Osadź",
   "status.favourite": "Ulubione",
   "status.load_more": "Załaduj więcej",
@@ -259,6 +262,7 @@
   "status.pin": "Przypnij do profilu",
   "status.pinned": "Przypięty wpis",
   "status.reblog": "Podbij",
+  "status.reblog_private": "Podbij dla odbiorców oryginalnego wpisu",
   "status.reblogged_by": "{name} podbił",
   "status.reply": "Odpowiedz",
   "status.replyAll": "Odpowiedz na wątek",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 4cd2e0643..c604476c7 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tente novamente",
   "column.blocks": "Usuários bloqueados",
   "column.community": "Local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Seguidores pendentes",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
   "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.",
   "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
   "empty_column.home.public_timeline": "global",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Esconder notificações deste usuário?",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Dê uma espiada...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Esta postagem não pode ser compartilhada",
   "status.delete": "Excluir",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Toot fixado",
   "status.reblog": "Compartilhar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} compartilhou",
   "status.reply": "Responder",
   "status.replyAll": "Responder à sequência",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 7a404eaba..826785aad 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tente de novo",
   "column.blocks": "Utilizadores Bloqueados",
   "column.community": "Local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Seguidores Pendentes",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
   "empty_column.community": "Ainda não existe conteúdo local para mostrar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
   "empty_column.home.public_timeline": "global",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Esconder notificações deste utilizador?",
   "navigation_bar.blocks": "Utilizadores bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Espreitar lá dentro...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este post não pode ser partilhado",
   "status.delete": "Eliminar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Partilhar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} partilhou",
   "status.reply": "Responder",
   "status.replyAll": "Responder à conversa",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar media",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 8616ef98f..bb3cc1794 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Попробовать снова",
   "column.blocks": "Список блокировки",
   "column.community": "Локальная лента",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Скрытые домены",
   "column.favourites": "Понравившееся",
   "column.follow_requests": "Запросы на подписку",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Символы",
   "emoji_button.travel": "Путешествия",
   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.",
   "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.",
   "empty_column.home.public_timeline": "публичные ленты",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Скрытые домены",
   "navigation_bar.edit_profile": "Изменить профиль",
   "navigation_bar.favourites": "Понравившееся",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
   "standalone.public_title": "Прямо сейчас",
   "status.block": "Заблокировать @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Этот статус не может быть продвинут",
   "status.delete": "Удалить",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Встроить",
   "status.favourite": "Нравится",
   "status.load_more": "Показать еще",
@@ -252,6 +257,7 @@
   "status.pin": "Закрепить в профиле",
   "status.pinned": "Pinned toot",
   "status.reblog": "Продвинуть",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} продвинул(а)",
   "status.reply": "Ответить",
   "status.replyAll": "Ответить на тред",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Главная",
   "tabs_bar.local_timeline": "Локальная",
   "tabs_bar.notifications": "Уведомления",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
   "upload_area.title": "Перетащите сюда, чтобы загрузить",
   "upload_button.label": "Добавить медиаконтент",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 925b46df6..58274fd2d 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -8,11 +8,11 @@
   "account.edit_profile": "Upraviť profil",
   "account.follow": "Následovať",
   "account.followers": "Sledujúci",
-  "account.follows": "Sledujete",
+  "account.follows": "Následuje",
   "account.follows_you": "Následuje ťa",
   "account.hide_reblogs": "Skryť povýšenia od @{name}",
   "account.media": "Médiá",
-  "account.mention": "Spomeňte @{name}",
+  "account.mention": "Spomeň @{name}",
   "account.moved_to": "{name} sa presunul/a na:",
   "account.mute": "Ignorovať @{name}",
   "account.mute_notifications": "Stĺmiť notifikácie od @{name}",
@@ -22,7 +22,7 @@
   "account.report": "Nahlás @{name}",
   "account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
   "account.share": "Zdieľať @{name} profil",
-  "account.show_reblogs": "Zobraziť povýšenia od @{name}",
+  "account.show_reblogs": "Ukáž povýšenia od @{name}",
   "account.unblock": "Odblokovať @{name}",
   "account.unblock_domain": "Prestať blokovať {domain}",
   "account.unfollow": "Prestať nasledovať",
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Skúsiť znova",
   "column.blocks": "Blokovaní užívatelia",
   "column.community": "Lokálna časová os",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Obľúbené",
   "column.follow_requests": "Žiadosti o sledovanie",
@@ -83,7 +84,7 @@
   "confirmations.mute.message": "Naozaj chcete ignorovať {name}?",
   "confirmations.unfollow.confirm": "Nesledovať",
   "confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?",
-  "embed.instructions": "Umiestnite kód uvedený nižšie pre pridanie tohto statusu na vašu web stránku.",
+  "embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.",
   "embed.preview": "Tu je ako to bude vyzerať:",
   "emoji_button.activity": "Aktivita",
   "emoji_button.custom": "Vlastné",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
   "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Pod týmto hashtagom sa ešte nič nenachádza.",
   "empty_column.home": "Vaša lokálna osa je zatiaľ prázdna! Pre začiatok pozrite {public} alebo použite vyhľadávanie a nájdite tak ostatných používateľov.",
   "empty_column.home.public_timeline": "verejná časová os",
@@ -111,13 +113,13 @@
   "getting_started.appsshort": "Aplikácie",
   "getting_started.faq": "Časté otázky",
   "getting_started.heading": "Začni tu",
-  "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžete na GitHube v {github}.",
+  "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispieť môžeš na GitHube v {github}.",
   "getting_started.userguide": "Používateľská príručka",
   "home.column_settings.advanced": "Pokročilé",
   "home.column_settings.basic": "Základné",
   "home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov",
   "home.column_settings.show_reblogs": "Zobraziť povýšené",
-  "home.column_settings.show_replies": "Zobraziť odpovede",
+  "home.column_settings.show_replies": "Ukázať odpovede",
   "home.settings": "Nastavenia stĺpcov",
   "keyboard_shortcuts.back": "dostať sa naspäť",
   "keyboard_shortcuts.boost": "vyzdvihnúť",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Upraviť profil",
   "navigation_bar.favourites": "Obľúbené",
@@ -169,7 +172,7 @@
   "notification.favourite": "{name} sa páči tvoj status",
   "notification.follow": "{name} ťa začal/a následovať",
   "notification.mention": "{name} ťa spomenul/a",
-  "notification.reblog": "{name} re-tootol tvoj status",
+  "notification.reblog": "{name} zdieľal/a tvoj status",
   "notifications.clear": "Vyčistiť zoznam notifikácii",
   "notifications.clear_confirmation": "Naozaj chcete nenávratne prečistiť všetky vaše notifikácie?",
   "notifications.column_settings.alert": "Notifikácie na ploche",
@@ -235,24 +238,27 @@
   "search_results.accounts": "Ľudia",
   "search_results.hashtags": "Haštagy",
   "search_results.statuses": "Príspevky",
-  "search_results.total": "{count, number} {count, plural, one {result} ostatné {results}}",
-  "standalone.public_title": "Pohľad dovnútra...",
+  "search_results.total": "{count, number} {count, plural, jeden {výsledok} ostatné {výsledky}}",
+  "standalone.public_title": "Náhľad dovnútra...",
   "status.block": "Blokovať @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
   "status.delete": "Zmazať",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Vložiť",
   "status.favourite": "Páči sa mi",
   "status.load_more": "Zobraz viac",
   "status.media_hidden": "Skryté médiá",
-  "status.mention": "Napísať @{name}",
+  "status.mention": "Spomeň @{name}",
   "status.more": "Viac",
   "status.mute": "Utíšiť @{name}",
   "status.mute_conversation": "Ignorovať konverzáciu",
   "status.open": "Otvoriť tento status",
-  "status.pin": "Pripnúť na profil",
+  "status.pin": "Pripni na profil",
   "status.pinned": "Pripnutý príspevok",
   "status.reblog": "Povýšiť",
-  "status.reblogged_by": "{name} povýšil",
+  "status.reblog_private": "Boost to original audience",
+  "status.reblogged_by": "{name} povýšil/a",
   "status.reply": "Odpovedať",
   "status.replyAll": "Odpovedať na diskusiu",
   "status.report": "Nahlásiť @{name}",
@@ -261,7 +267,7 @@
   "status.share": "Zdieľať",
   "status.show_less": "Zobraz menej",
   "status.show_less_all": "Všetkým ukáž menej",
-  "status.show_more": "Zobraz viac",
+  "status.show_more": "Zobraziť viac",
   "status.show_more_all": "Všetkým ukáž viac",
   "status.unmute_conversation": "Prestať ignorovať konverzáciu",
   "status.unpin": "Odopnúť z profilu",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Domov",
   "tabs_bar.local_timeline": "Lokálna",
   "tabs_bar.notifications": "Notifikácie",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.",
   "upload_area.title": "Ťahaj a pusti pre nahratie",
   "upload_button.label": "Pridať médiá",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 8fae49a03..e4d07edd1 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Pokušajte ponovo",
   "column.blocks": "Blokirani korisnici",
   "column.community": "Lokalna lajna",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Omiljeni",
   "column.follow_requests": "Zahtevi za praćenje",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja & mesta",
   "empty_column.community": "Lokalna lajna je prazna. Napišite nešto javno da lajna produva!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Trenutno nema ništa na ovom heštegu.",
   "empty_column.home": "Vaša lajna je prazna! Posetite {public} ili koristite pretragu da počnete i upoznajete nove ljude.",
   "empty_column.home.public_timeline": "javna lajna",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalna lajna",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Izmeni profil",
   "navigation_bar.favourites": "Omiljeni",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
   "standalone.public_title": "Pogled iznutra...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj status ne može da se podrži",
   "status.delete": "Obriši",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Ugradi na sajt",
   "status.favourite": "Omiljeno",
   "status.load_more": "Učitaj još",
@@ -252,6 +257,7 @@
   "status.pin": "Prikači na profil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Podrži",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} podržao(la)",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na diskusiju",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Početna",
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Obaveštenja",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.",
   "upload_area.title": "Prevucite ovde da otpremite",
   "upload_button.label": "Dodaj multimediju",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index a39fea582..60c781e9d 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Покушајте поново",
   "column.blocks": "Блокирани корисници",
   "column.community": "Локална лајна",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Омиљени",
   "column.follow_requests": "Захтеви за праћење",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Симболи",
   "emoji_button.travel": "Путовања & места",
   "empty_column.community": "Локална лајна је празна. Напишите нешто јавно да лајна продува!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Тренутно нема ништа на овом хештегу.",
   "empty_column.home": "Ваша лајна је празна! Посетите {public} или користите претрагу да почнете и упознајете нове људе.",
   "empty_column.home.public_timeline": "јавна лајна",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?",
   "navigation_bar.blocks": "Блокирани корисници",
   "navigation_bar.community_timeline": "Локална лајна",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Измени профил",
   "navigation_bar.favourites": "Омиљени",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
   "standalone.public_title": "Поглед изнутра...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Овај статус не може да се подржи",
   "status.delete": "Обриши",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Угради на сајт",
   "status.favourite": "Омиљено",
   "status.load_more": "Учитај још",
@@ -252,6 +257,7 @@
   "status.pin": "Прикачи на профил",
   "status.pinned": "Pinned toot",
   "status.reblog": "Подржи",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} подржао(ла)",
   "status.reply": "Одговори",
   "status.replyAll": "Одговори на дискусију",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Почетна",
   "tabs_bar.local_timeline": "Локално",
   "tabs_bar.notifications": "Обавештења",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.",
   "upload_area.title": "Превуците овде да отпремите",
   "upload_button.label": "Додај мултимедију",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 014492e19..8fa6992f1 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Försök igen",
   "column.blocks": "Blockerade användare",
   "column.community": "Lokal tidslinje",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriter",
   "column.follow_requests": "Följ förfrågningar",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Resor & Platser",
   "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att få bollen att rulla!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Det finns inget i denna hashtag ännu.",
   "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igång och träffa andra användare.",
   "empty_column.home.public_timeline": "den publika tidslinjen",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?",
   "navigation_bar.blocks": "Blockerade användare",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Redigera profil",
   "navigation_bar.favourites": "Favoriter",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}",
   "standalone.public_title": "En titt inuti...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Detta inlägg kan inte knuffas",
   "status.delete": "Ta bort",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Bädda in",
   "status.favourite": "Favorit",
   "status.load_more": "Ladda fler",
@@ -252,6 +257,7 @@
   "status.pin": "Fäst i profil",
   "status.pinned": "Fäst toot",
   "status.reblog": "Knuff",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} knuffade",
   "status.reply": "Svara",
   "status.replyAll": "Svara på tråden",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Meddelanden",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.",
   "upload_area.title": "Dra & släpp för att ladda upp",
   "upload_button.label": "Lägg till media",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index ecfe7c9b5..3b91c0d2c 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
   "column.community": "Local timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favourites",
   "column.follow_requests": "Follow requests",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
   "navigation_bar.favourites": "Favourites",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
   "status.load_more": "Load more",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boosted",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 58d0e5785..cdf6f46a3 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Engellenen kullanıcılar",
   "column.community": "Yerel zaman tüneli",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriler",
   "column.follow_requests": "Takip istekleri",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Semboller",
   "emoji_button.travel": "Seyahat ve Yerler",
   "empty_column.community": "Yerel zaman tüneliniz boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın.",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Henüz bu hashtag’e sahip hiçbir gönderi yok.",
   "empty_column.home": "Henüz kimseyi takip etmiyorsunuz. {public} ziyaret edebilir veya arama kısmını kullanarak diğer kullanıcılarla iletişime geçebilirsiniz.",
   "empty_column.home.public_timeline": "herkese açık zaman tüneli",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profili düzenle",
   "navigation_bar.favourites": "Favoriler",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Bu gönderi boost edilemez",
   "status.delete": "Sil",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorilere ekle",
   "status.load_more": "Daha fazla",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost'la",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boost etti",
   "status.reply": "Cevapla",
   "status.replyAll": "Konuşmayı cevapla",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Ana sayfa",
   "tabs_bar.local_timeline": "Yerel",
   "tabs_bar.notifications": "Bildirimler",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Upload için sürükle bırak yapınız",
   "upload_button.label": "Görsel ekle",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 63866d339..261e5795e 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Заблоковані користувачі",
   "column.community": "Локальна стрічка",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Вподобане",
   "column.follow_requests": "Запити на підписку",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Символи",
   "emoji_button.travel": "Подорожі",
   "empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Дописів з цим хештегом поки не існує.",
   "empty_column.home": "Ви поки ні на кого не підписані. Погортайте {public}, або скористуйтесь пошуком, щоб освоїтися та познайомитися з іншими користувачами.",
   "empty_column.home.public_timeline": "публічні стрічки",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Заблоковані користувачі",
   "navigation_bar.community_timeline": "Локальна стрічка",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Редагувати профіль",
   "navigation_bar.favourites": "Вподобане",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Цей допис не може бути передмухнутий",
   "status.delete": "Видалити",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Подобається",
   "status.load_more": "Завантажити більше",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Передмухнути",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} передмухнув(-ла)",
   "status.reply": "Відповісти",
   "status.replyAll": "Відповісти на тред",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Головна",
   "tabs_bar.local_timeline": "Локальна",
   "tabs_bar.notifications": "Сповіщення",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Перетягніть сюди, щоб завантажити",
   "upload_button.label": "Додати медіаконтент",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index f7cb49632..aba0bde83 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "重试",
   "column.blocks": "屏蔽用户",
   "column.community": "本站时间轴",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "收藏过的嘟文",
   "column.follow_requests": "关注请求",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "符号",
   "emoji_button.travel": "旅行和地点",
   "empty_column.community": "本站时间轴暂时没有内容,快嘟几个来抢头香啊!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.home.public_timeline": "公共时间轴",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
   "navigation_bar.blocks": "被屏蔽的用户",
   "navigation_bar.community_timeline": "本站时间轴",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "修改个人资料",
   "navigation_bar.favourites": "收藏的内容",
@@ -238,8 +241,10 @@
   "search_results.total": "共 {count, number} 个结果",
   "standalone.public_title": "大家都在干啥?",
   "status.block": "屏蔽 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "无法转嘟这条嘟文",
   "status.delete": "删除",
+  "status.direct": "Direct message @{name}",
   "status.embed": "嵌入",
   "status.favourite": "收藏",
   "status.load_more": "加载更多",
@@ -252,6 +257,7 @@
   "status.pin": "在个人资料页面置顶",
   "status.pinned": "Pinned toot",
   "status.reblog": "转嘟",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 转嘟了",
   "status.reply": "回复",
   "status.replyAll": "回复所有人",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "主页",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "如果你现在离开 Mastodon,你的草稿内容将会被丢弃。",
   "upload_area.title": "将文件拖放到此处开始上传",
   "upload_button.label": "上传媒体文件",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 1cbc9f1c5..b5ebd20fc 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖用戶",
   "column.community": "本站時間軸",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "隱藏的服務站",
   "column.favourites": "最愛的文章",
   "column.follow_requests": "關注請求",
@@ -74,7 +75,7 @@
   "confirmations.block.confirm": "封鎖",
   "confirmations.block.message": "你確定要封鎖{name}嗎?",
   "confirmations.delete.confirm": "刪除",
-  "confirmations.delete.message": "你確定要刪除{name}嗎?",
+  "confirmations.delete.message": "你確定要刪除這文章嗎?",
   "confirmations.delete_list.confirm": "刪除",
   "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?",
   "confirmations.domain_block.confirm": "隱藏整個網站",
@@ -99,11 +100,12 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊景物",
-  "empty_column.community": "本站時間軸暫時未有內容,快文章來搶頭香啊!",
+  "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
-  "empty_column.list": "There is nothing in this list yet.",
+  "empty_column.list": "這個列表暫時未有內容。",
   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
   "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
   "follow_request.authorize": "批准",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
   "navigation_bar.blocks": "被你封鎖的用戶",
   "navigation_bar.community_timeline": "本站時間軸",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "隱藏的服務站",
   "navigation_bar.edit_profile": "修改個人資料",
   "navigation_bar.favourites": "最愛的內容",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} 項結果",
   "standalone.public_title": "站點一瞥…",
   "status.block": "封鎖 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "這篇文章無法被轉推",
   "status.delete": "刪除",
+  "status.direct": "私訊 @{name}",
   "status.embed": "鑲嵌",
   "status.favourite": "收藏",
   "status.load_more": "載入更多",
@@ -252,6 +257,7 @@
   "status.pin": "置頂到資料頁",
   "status.pinned": "置頂文章",
   "status.reblog": "轉推",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 轉推",
   "status.reply": "回應",
   "status.replyAll": "回應所有人",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "搜尋",
   "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index c7925829f..28d634600 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -1,24 +1,24 @@
 {
   "account.block": "封鎖 @{name}",
   "account.block_domain": "隱藏來自 {domain} 的一切貼文",
-  "account.blocked": "Blocked",
-  "account.direct": "Direct message @{name}",
+  "account.blocked": "已被封鎖的",
+  "account.direct": "給 @{name} 私人訊息",
   "account.disclaimer_full": "下列資料不一定完整。",
-  "account.domain_blocked": "Domain hidden",
-  "account.edit_profile": "編輯用者資訊",
+  "account.domain_blocked": "域名隱藏",
+  "account.edit_profile": "編輯使用者資訊",
   "account.follow": "關注",
-  "account.followers": "專注者",
+  "account.followers": "關注者",
   "account.follows": "正關注",
   "account.follows_you": "關注你",
-  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.hide_reblogs": "隱藏來自 @{name} 的轉推",
   "account.media": "媒體",
   "account.mention": "提到 @{name}",
-  "account.moved_to": "{name} has moved to:",
+  "account.moved_to": "{name} 已經移至:",
   "account.mute": "消音 @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Muted",
+  "account.mute_notifications": "消音來自 @{name} 的通知",
+  "account.muted": "消音的",
   "account.posts": "貼文",
-  "account.posts_with_replies": "Toots with replies",
+  "account.posts_with_replies": "貼文及回覆",
   "account.report": "檢舉 @{name}",
   "account.requested": "正在等待許可",
   "account.share": "分享 @{name} 的用者資訊",
@@ -27,10 +27,10 @@
   "account.unblock_domain": "不再隱藏 {domain}",
   "account.unfollow": "取消關注",
   "account.unmute": "不再消音 @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.unmute_notifications": "不再對來自 @{name} 的通知消音",
   "account.view_full_profile": "查看完整資訊",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.message": "發生非預期的錯誤。",
+  "alert.unexpected.title": "哎呀!",
   "boost_modal.combo": "下次你可以按 {combo} 來跳過",
   "bundle_column_error.body": "加載本組件出錯。",
   "bundle_column_error.retry": "重試",
@@ -40,11 +40,12 @@
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖的使用者",
   "column.community": "本地時間軸",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "Direct messages",
+  "column.domain_blocks": "隱藏域名",
   "column.favourites": "最愛",
   "column.follow_requests": "關注請求",
   "column.home": "家",
-  "column.lists": "Lists",
+  "column.lists": "名單",
   "column.mutes": "消音的使用者",
   "column.notifications": "通知",
   "column.pins": "置頂貼文",
@@ -58,17 +59,17 @@
   "column_header.unpin": "取下",
   "column_subheading.navigation": "瀏覽",
   "column_subheading.settings": "設定",
-  "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "compose_form.direct_message_warning": "此則推文只會被所有提到的使用者看見。",
+  "compose_form.hashtag_warning": "此則推文將不會在任何主題標籤中看見,只有公開的推文可以用主題標籤來搜尋。",
   "compose_form.lock_disclaimer": "你的帳號沒有{locked}。任何人都可以關注你,看到發給關注者的貼文。",
   "compose_form.lock_disclaimer.lock": "上鎖",
   "compose_form.placeholder": "在想些什麼?",
   "compose_form.publish": "貼掉",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.sensitive.marked": "此媒體已被標註為敏感的",
+  "compose_form.sensitive.unmarked": "此媒體未被標註為敏感的",
+  "compose_form.spoiler.marked": "文字隱藏在警告後",
+  "compose_form.spoiler.unmarked": "文字不是隱藏的",
   "compose_form.spoiler_placeholder": "內容警告",
   "confirmation_modal.cancel": "取消",
   "confirmations.block.confirm": "封鎖",
@@ -86,20 +87,21 @@
   "embed.instructions": "要內嵌此貼文,請將以下代碼貼進你的網站。",
   "embed.preview": "看上去會變成這樣:",
   "emoji_button.activity": "活動",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "自訂",
   "emoji_button.flags": "旗幟",
-  "emoji_button.food": "食物與飲料",
+  "emoji_button.food": "飲食",
   "emoji_button.label": "插入表情符號",
   "emoji_button.nature": "自然",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "沒有表情符號吼!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "物件",
   "emoji_button.people": "人",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "常用",
   "emoji_button.search": "搜尋…",
-  "emoji_button.search_results": "Search results",
+  "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊與地點",
   "empty_column.community": "本地時間軸是空的。公開寫點什麼吧!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "這個主題標籤下什麼都沒有。",
   "empty_column.home": "你還沒關注任何人。造訪{public}或利用搜尋功能找到其他用者。",
   "empty_column.home.public_timeline": "公開時間軸",
@@ -108,8 +110,8 @@
   "empty_column.public": "這裡什麼都沒有!公開寫些什麼,或是關注其他副本的使用者。",
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
-  "getting_started.appsshort": "Apps",
-  "getting_started.faq": "FAQ",
+  "getting_started.appsshort": "應用程式",
+  "getting_started.faq": "常見問答",
   "getting_started.heading": "馬上開始",
   "getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上做出貢獻或是回報問題。",
   "getting_started.userguide": "使用者指南",
@@ -119,19 +121,19 @@
   "home.column_settings.show_reblogs": "顯示轉推",
   "home.column_settings.show_replies": "顯示回應",
   "home.settings": "欄位設定",
-  "keyboard_shortcuts.back": "to navigate back",
-  "keyboard_shortcuts.boost": "to boost",
+  "keyboard_shortcuts.back": "回到上一個",
+  "keyboard_shortcuts.boost": "到轉推",
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
-  "keyboard_shortcuts.compose": "to focus the compose textarea",
-  "keyboard_shortcuts.description": "Description",
+  "keyboard_shortcuts.compose": "焦點移至撰寫文字區塊",
+  "keyboard_shortcuts.description": "描述",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to favourite",
-  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.hotkey": "Hotkey",
+  "keyboard_shortcuts.favourite": "收藏",
+  "keyboard_shortcuts.heading": "鍵盤快速鍵",
+  "keyboard_shortcuts.hotkey": "快速鍵",
   "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.mention": "to mention author",
-  "keyboard_shortcuts.reply": "to reply",
+  "keyboard_shortcuts.mention": "到提到的作者",
+  "keyboard_shortcuts.reply": "到回應",
   "keyboard_shortcuts.search": "to focus search",
   "keyboard_shortcuts.toot": "to start a brand new toot",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
@@ -150,11 +152,12 @@
   "loading_indicator.label": "讀取中...",
   "media_gallery.toggle_visible": "切換可見性",
   "missing_indicator.label": "找不到",
-  "missing_indicator.sublabel": "This resource could not be found",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "missing_indicator.sublabel": "找不到此資源",
+  "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.domain_blocks": "隱藏的域名",
   "navigation_bar.edit_profile": "編輯用者資訊",
   "navigation_bar.favourites": "最愛",
   "navigation_bar.follow_requests": "關注請求",
@@ -197,7 +200,7 @@
   "onboarding.page_six.github": "Mastodon 是自由的開源軟體。你可以在 {github} 上回報臭蟲、請求新功能或是做出貢獻。",
   "onboarding.page_six.guidelines": "社群指南",
   "onboarding.page_six.read_guidelines": "請閱讀 {domain} 的 {guidelines} !",
-  "onboarding.page_six.various_app": "行動 apps",
+  "onboarding.page_six.various_app": "行動版應用程式",
   "onboarding.page_three.profile": "編輯你的大頭貼、自傳和顯示名稱。你也可以在這邊找到其他設定。",
   "onboarding.page_three.search": "利用搜尋列來找到其他人或是主題標籤,像是 {illustration} 或 {introductions} 。用完整的帳號名稱來找不在這個副本上的使用者。",
   "onboarding.page_two.compose": "在編輯欄寫些什麼。可以上傳圖片、改變隱私設定或是用下面的圖示加上內容警告。",
@@ -211,13 +214,13 @@
   "privacy.public.short": "公開貼",
   "privacy.unlisted.long": "不要貼到公開時間軸",
   "privacy.unlisted.short": "不列出來",
-  "regeneration_indicator.label": "Loading…",
+  "regeneration_indicator.label": "載入中…",
   "regeneration_indicator.sublabel": "Your home feed is being prepared!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
-  "relative_time.just_now": "now",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
+  "relative_time.days": "{number} 天",
+  "relative_time.hours": "{number} 時",
+  "relative_time.just_now": "現在",
+  "relative_time.minutes": "{number} 分",
+  "relative_time.seconds": "{number} 秒",
   "reply_indicator.cancel": "取消",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
@@ -226,20 +229,22 @@
   "report.submit": "送出",
   "report.target": "通報中",
   "search.placeholder": "搜尋",
-  "search_popout.search_format": "Advanced search format",
+  "search_popout.search_format": "進階搜尋格式",
   "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "status",
+  "search_popout.tips.hashtag": "主題標籤",
+  "search_popout.tips.status": "狀態",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
+  "search_popout.tips.user": "使用者",
+  "search_results.accounts": "人",
+  "search_results.hashtags": "主題標籤",
+  "search_results.statuses": "推文",
   "search_results.total": "{count, number} 項結果",
   "standalone.public_title": "站點一瞥…",
-  "status.block": "Block @{name}",
+  "status.block": "封鎖 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "此貼文無法轉推",
   "status.delete": "刪除",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "收藏",
   "status.load_more": "載入更多",
@@ -250,15 +255,16 @@
   "status.mute_conversation": "消音對話",
   "status.open": "展開這個狀態",
   "status.pin": "置頂到個人資訊頁",
-  "status.pinned": "Pinned toot",
+  "status.pinned": "置頂的推文",
   "status.reblog": "轉推",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 轉推了",
   "status.reply": "回應",
   "status.replyAll": "回應這串",
   "status.report": "通報 @{name}",
   "status.sensitive_toggle": "點來看",
   "status.sensitive_warning": "敏感內容",
-  "status.share": "Share",
+  "status.share": "分享",
   "status.show_less": "看少點",
   "status.show_less_all": "Show less for all",
   "status.show_more": "看更多",
@@ -269,17 +275,18 @@
   "tabs_bar.home": "家",
   "tabs_bar.local_timeline": "本地",
   "tabs_bar.notifications": "通知",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "tabs_bar.search": "Search",
+  "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。",
   "upload_area.title": "拖放來上傳",
   "upload_button.label": "增加媒體",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.focus": "Crop",
+  "upload_form.description": "爲視障者加上描述",
+  "upload_form.focus": "裁切",
   "upload_form.undo": "復原",
   "upload_progress.label": "上傳中...",
   "video.close": "關閉影片",
-  "video.exit_fullscreen": "退出全熒幕",
+  "video.exit_fullscreen": "退出全螢幕",
   "video.expand": "展開影片",
-  "video.fullscreen": "全熒幕",
+  "video.fullscreen": "全螢幕",
   "video.hide": "隱藏影片",
   "video.mute": "消音",
   "video.pause": "暫停",
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index 87049ea79..46d9d6c8e 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -36,8 +36,6 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde
 import uuid from '../uuid';
 import { me } from '../initial_state';
 
-const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
-
 const initialState = ImmutableMap({
   mounted: 0,
   sensitive: false,
@@ -137,9 +135,8 @@ const updateSuggestionTags = (state, token) => {
   });
 };
 
-const insertEmoji = (state, position, emojiData) => {
+const insertEmoji = (state, position, emojiData, needsSpace) => {
   const oldText = state.get('text');
-  const needsSpace = emojiData.custom && position > 0 && !allowedAroundShortCode.includes(oldText[position - 1]);
   const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native;
 
   return state.merge({
@@ -288,7 +285,7 @@ export default function compose(state = initialState, action) {
       return state;
     }
   case COMPOSE_EMOJI_INSERT:
-    return insertEmoji(state, action.position, action.emoji);
+    return insertEmoji(state, action.position, action.emoji, action.needsSpace);
   case COMPOSE_UPLOAD_CHANGE_SUCCESS:
     return state
       .set('is_submitting', false)
diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js
index fe8308d0c..ebd01e532 100644
--- a/app/javascript/mastodon/reducers/contexts.js
+++ b/app/javascript/mastodon/reducers/contexts.js
@@ -21,24 +21,31 @@ const normalizeContext = (state, id, ancestors, descendants) => {
   });
 };
 
-const deleteFromContexts = (state, id) => {
-  state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => {
-    state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
+const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
+  state.update('ancestors', immutableAncestors => immutableAncestors.withMutations(ancestors => {
+    state.update('descendants', immutableDescendants => immutableDescendants.withMutations(descendants => {
+      ids.forEach(id => {
+        descendants.get(id, ImmutableList()).forEach(descendantId => {
+          ancestors.update(descendantId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
+        });
 
-  state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => {
-    state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
+        ancestors.get(id, ImmutableList()).forEach(ancestorId => {
+          descendants.update(ancestorId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
+        });
 
-  state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
+        descendants.delete(id);
+        ancestors.delete(id);
+      });
+    }));
+  }));
+});
 
-  return state;
-};
+const filterContexts = (state, relationship, statuses) => {
+  const ownedStatusIds = statuses
+    .filter(status => status.get('account') === relationship.id)
+    .map(status => status.get('id'));
 
-const filterContexts = (state, relationship) => {
-  return state.map(
-    statuses => statuses.filter(
-      status => status.get('account') !== relationship.id));
+  return deleteFromContexts(state, ownedStatusIds);
 };
 
 const updateContext = (state, status, references) => {
@@ -61,11 +68,11 @@ export default function contexts(state = initialState, action) {
   switch(action.type) {
   case ACCOUNT_BLOCK_SUCCESS:
   case ACCOUNT_MUTE_SUCCESS:
-    return filterContexts(state, action.relationship);
+    return filterContexts(state, action.relationship, action.statuses);
   case CONTEXT_FETCH_SUCCESS:
     return normalizeContext(state, action.id, action.ancestors, action.descendants);
   case TIMELINE_DELETE:
-    return deleteFromContexts(state, action.id);
+    return deleteFromContexts(state, [action.id]);
   case TIMELINE_CONTEXT_UPDATE:
     return updateContext(state, action.status, action.references);
   default:
diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js
index 1ac7eb706..da9b8c420 100644
--- a/app/javascript/mastodon/reducers/notifications.js
+++ b/app/javascript/mastodon/reducers/notifications.js
@@ -12,6 +12,7 @@ import {
 } from '../actions/accounts';
 import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
 import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import compareId from '../compare_id';
 
 const initialState = ImmutableMap({
   items: ImmutableList(),
@@ -44,13 +45,6 @@ const normalizeNotification = (state, notification) => {
   });
 };
 
-const newer = (m, n) => {
-  const mId = m.get('id');
-  const nId = n.get('id');
-
-  return mId.length === nId.length ? mId > nId : mId.length > nId.length;
-};
-
 const expandNormalizedNotifications = (state, notifications, next) => {
   let items = ImmutableList();
 
@@ -62,11 +56,11 @@ const expandNormalizedNotifications = (state, notifications, next) => {
     if (!items.isEmpty()) {
       mutable.update('items', list => {
         const lastIndex = 1 + list.findLastIndex(
-          item => item !== null && (newer(item, items.last()) || item.get('id') === items.last().get('id'))
+          item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id'))
         );
 
         const firstIndex = 1 + list.take(lastIndex).findLastIndex(
-          item => item !== null && newer(item, items.first())
+          item => item !== null && compareId(item.get('id'), items.first().get('id')) > 0
         );
 
         return list.take(firstIndex).concat(items, list.skip(lastIndex));
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 390b2a13a..9ec52a7fa 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -58,6 +58,12 @@ const initialState = ImmutableMap({
       body: '',
     }),
   }),
+
+  direct: ImmutableMap({
+    regex: ImmutableMap({
+      body: '',
+    }),
+  }),
 });
 
 const defaultColumns = fromJS([
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index f795e7e08..ad897bcc9 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -13,6 +13,7 @@ import {
   ACCOUNT_UNFOLLOW_SUCCESS,
 } from '../actions/accounts';
 import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+import compareId from '../compare_id';
 
 const initialState = ImmutableMap();
 
@@ -32,8 +33,8 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) =>
     if (!statuses.isEmpty()) {
       mMap.update('items', ImmutableList(), oldIds => {
         const newIds = statuses.map(status => status.get('id'));
-        const lastIndex = oldIds.findLastIndex(id => id !== null && id >= newIds.last()) + 1;
-        const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && id > newIds.first());
+        const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
+        const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) >= 0);
 
         if (firstIndex < 0) {
           return (isPartial ? newIds.unshift(null) : newIds).concat(oldIds.skip(lastIndex));
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 034c35e8a..0a09a38d2 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -169,7 +169,7 @@ $small-breakpoint: 960px;
       background: $ui-base-color;
       font-size: 12px;
       font-weight: 500;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       text-transform: uppercase;
       position: relative;
       z-index: 1;
@@ -186,10 +186,10 @@ $small-breakpoint: 960px;
     font-size: 16px;
     line-height: 30px;
     margin-bottom: 12px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -202,11 +202,11 @@ $small-breakpoint: 960px;
       text-align: center;
       font-size: 12px;
       line-height: 18px;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       margin-bottom: 0;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         text-decoration: underline;
       }
     }
@@ -225,7 +225,7 @@ $small-breakpoint: 960px;
     font-family: inherit;
     font-size: inherit;
     line-height: inherit;
-    color: lighten($ui-primary-color, 10%);
+    color: transparentize($darker-text-color, 0.1);
   }
 
   h1 {
@@ -234,14 +234,14 @@ $small-breakpoint: 960px;
     line-height: 30px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
 
     small {
       font-family: 'mastodon-font-sans-serif', sans-serif;
       display: block;
       font-size: 18px;
       font-weight: 400;
-      color: $ui-base-lighter-color;
+      color: opacify($darker-text-color, 0.1);
     }
   }
 
@@ -251,7 +251,7 @@ $small-breakpoint: 960px;
     line-height: 26px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h3 {
@@ -260,7 +260,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h4 {
@@ -269,7 +269,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h5 {
@@ -278,7 +278,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h6 {
@@ -287,7 +287,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   ul,
@@ -354,10 +354,10 @@ $small-breakpoint: 960px;
         font-weight: 400;
         font-size: 16px;
         line-height: 30px;
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         a {
-          color: $ui-highlight-color;
+          color: $highlight-text-color;
           text-decoration: underline;
         }
       }
@@ -405,7 +405,7 @@ $small-breakpoint: 960px;
         font-size: 14px;
 
         &:hover {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
 
@@ -478,10 +478,10 @@ $small-breakpoint: 960px;
     font-weight: 400;
     font-size: 16px;
     line-height: 30px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -517,7 +517,7 @@ $small-breakpoint: 960px;
 
       span {
         &:last-child {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
 
@@ -548,7 +548,7 @@ $small-breakpoint: 960px;
         font-size: 14px;
         line-height: 24px;
         font-weight: 500;
-        color: $ui-primary-color;
+        color: $darker-text-color;
         padding-bottom: 5px;
         margin-bottom: 15px;
         border-bottom: 1px solid lighten($ui-base-color, 4%);
@@ -559,7 +559,7 @@ $small-breakpoint: 960px;
         a,
         span {
           font-weight: 400;
-          color: darken($ui-primary-color, 10%);
+          color: opacify($darker-text-color, 0.1);
         }
 
         a {
@@ -602,7 +602,7 @@ $small-breakpoint: 960px;
 
         .username {
           display: block;
-          color: $ui-primary-color;
+          color: $darker-text-color;
         }
       }
     }
@@ -775,7 +775,7 @@ $small-breakpoint: 960px;
     }
 
     p a {
-      color: $ui-secondary-color;
+      color: $darker-text-color;
     }
 
     h1 {
@@ -784,10 +784,10 @@ $small-breakpoint: 960px;
       margin-bottom: 0;
 
       small {
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         span {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
     }
@@ -896,7 +896,7 @@ $small-breakpoint: 960px;
       }
 
       a {
-        color: $ui-secondary-color;
+        color: $darker-text-color;
         text-decoration: none;
       }
     }
@@ -935,7 +935,7 @@ $small-breakpoint: 960px;
 
         .fa {
           display: block;
-          color: $ui-primary-color;
+          color: $darker-text-color;
           font-size: 48px;
         }
       }
@@ -943,7 +943,7 @@ $small-breakpoint: 960px;
       .text {
         font-size: 16px;
         line-height: 30px;
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         h6 {
           font-size: inherit;
@@ -969,10 +969,10 @@ $small-breakpoint: 960px;
     font-weight: 400;
     font-size: 16px;
     line-height: 30px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -980,7 +980,7 @@ $small-breakpoint: 960px;
   .footer-links {
     padding-bottom: 50px;
     text-align: right;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
 
     p {
       font-size: 14px;
@@ -995,7 +995,7 @@ $small-breakpoint: 960px;
   &__footer {
     margin-top: 10px;
     text-align: center;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
 
     p {
       font-size: 14px;
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index dd82ab375..f9af6f288 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -75,7 +75,7 @@
     small {
       display: block;
       font-size: 14px;
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       font-weight: 400;
       overflow: hidden;
       text-overflow: ellipsis;
@@ -113,7 +113,7 @@
     width: 33.3%;
     box-sizing: border-box;
     flex: 0 0 auto;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     padding: 5px 10px 0;
     margin-bottom: 10px;
     border-right: 1px solid lighten($ui-base-color, 4%);
@@ -143,7 +143,7 @@
 
     &.active {
       &::after {
-        border-bottom: 4px solid $ui-highlight-color;
+        border-bottom: 4px solid $highlight-text-color;
         opacity: 1;
       }
     }
@@ -178,7 +178,7 @@
     font-size: 14px;
     line-height: 18px;
     padding: 0 15px;
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   @media screen and (max-width: 480px) {
@@ -256,7 +256,7 @@
   .current {
     background: $simple-background-color;
     border-radius: 100px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     cursor: default;
     margin: 0 10px;
   }
@@ -268,7 +268,7 @@
   .older,
   .newer {
     text-transform: uppercase;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   .older {
@@ -293,7 +293,7 @@
 
   .disabled {
     cursor: default;
-    color: lighten($ui-base-color, 10%);
+    color: opacify($lighter-text-color, 0.1);
   }
 
   @media screen and (max-width: 700px) {
@@ -332,7 +332,7 @@
     width: 335px;
     background: $simple-background-color;
     border-radius: 4px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     margin: 0 5px 10px;
     position: relative;
 
@@ -344,7 +344,7 @@
       overflow: hidden;
       height: 100px;
       border-radius: 4px 4px 0 0;
-      background-color: lighten($ui-base-color, 4%);
+      background-color: opacify($lighter-text-color, 0.04);
       background-size: cover;
       background-position: center;
       position: relative;
@@ -392,7 +392,7 @@
 
       a {
         display: block;
-        color: $ui-base-color;
+        color: $inverted-text-color;
         text-decoration: none;
         text-overflow: ellipsis;
         overflow: hidden;
@@ -414,7 +414,7 @@
     }
 
     .username {
-      color: lighten($ui-base-color, 34%);
+      color: $lighter-text-color;
       font-size: 14px;
       font-weight: 400;
     }
@@ -422,7 +422,7 @@
     .account__header__content {
       padding: 10px 15px;
       padding-top: 15px;
-      color: lighten($ui-base-color, 26%);
+      color: transparentize($lighter-text-color, 0.1);
       word-wrap: break-word;
       overflow: hidden;
       text-overflow: ellipsis;
@@ -434,7 +434,7 @@
 .nothing-here {
   width: 100%;
   display: block;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   font-size: 14px;
   font-weight: 500;
   text-align: center;
@@ -493,7 +493,7 @@
 
       span {
         font-size: 14px;
-        color: $ui-primary-color;
+        color: $inverted-text-color;
       }
     }
 
@@ -508,7 +508,7 @@
 
   .account__header__content {
     font-size: 14px;
-    color: $ui-base-color;
+    color: $darker-text-color;
   }
 }
 
@@ -522,18 +522,18 @@
     display: inline-block;
     padding: 15px;
     text-decoration: none;
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-transform: uppercase;
     font-weight: 500;
 
     &:hover,
     &:active,
     &:focus {
-      color: lighten($ui-highlight-color, 8%);
+      color: lighten($highlight-text-color, 8%);
     }
 
     &.active {
-      color: $ui-base-color;
+      color: $inverted-text-color;
       cursor: default;
     }
   }
@@ -563,3 +563,57 @@
     border-color: rgba(lighten($error-red, 12%), 0.5);
   }
 }
+
+.account__header__fields {
+  border-collapse: collapse;
+  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%);
+
+  th,
+  td {
+    padding: 15px;
+    padding-left: 15px;
+    border: 0 none;
+    border-bottom: 1px solid lighten($ui-base-color, 4%);
+    vertical-align: middle;
+  }
+
+  th {
+    padding-left: 15px;
+    font-weight: 500;
+    text-align: center;
+    width: 94px;
+    color: opacify($darker-text-color, 0.1);
+    background: rgba(darken($ui-base-color, 8%), 0.5);
+  }
+
+  td {
+    color: $darker-text-color;
+    text-align: center;
+    width: 100%;
+    padding-left: 0;
+  }
+
+  a {
+    color: $highlight-text-color;
+    text-decoration: none;
+
+    &:hover,
+    &:focus,
+    &:active {
+      text-decoration: underline;
+    }
+  }
+
+  tr {
+    &:last-child {
+      th,
+      td {
+        border-bottom: 0;
+      }
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 6bd659030..348f72078 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -33,7 +33,7 @@
       a {
         display: block;
         padding: 15px;
-        color: rgba($primary-text-color, 0.7);
+        color: $darker-text-color;
         text-decoration: none;
         transition: all 200ms linear;
         border-radius: 4px 0 0 4px;
@@ -90,7 +90,7 @@
     padding-left: 25px;
 
     h2 {
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       font-size: 24px;
       line-height: 28px;
       font-weight: 400;
@@ -98,7 +98,7 @@
     }
 
     h3 {
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       font-size: 20px;
       line-height: 28px;
       font-weight: 400;
@@ -109,7 +109,7 @@
       text-transform: uppercase;
       font-size: 13px;
       font-weight: 500;
-      color: $ui-primary-color;
+      color: $primary-text-color;
       padding-bottom: 8px;
       margin-bottom: 8px;
       border-bottom: 1px solid lighten($ui-base-color, 8%);
@@ -117,7 +117,7 @@
 
     h6 {
       font-size: 16px;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       line-height: 28px;
       font-weight: 400;
     }
@@ -125,7 +125,7 @@
     & > p {
       font-size: 14px;
       line-height: 18px;
-      color: $ui-secondary-color;
+      color: $darker-text-color;
       margin-bottom: 20px;
 
       strong {
@@ -153,10 +153,10 @@
     }
 
     .muted-hint {
-      color: $ui-primary-color;
+      color: $darker-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
     }
 
@@ -253,7 +253,7 @@
 
     a {
       display: inline-block;
-      color: rgba($primary-text-color, 0.7);
+      color: $darker-text-color;
       text-decoration: none;
       text-transform: uppercase;
       font-size: 12px;
@@ -266,7 +266,7 @@
       }
 
       &.selected {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         border-bottom: 2px solid $ui-highlight-color;
       }
     }
@@ -291,7 +291,7 @@
     font-weight: 500;
     font-size: 14px;
     line-height: 18px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
 
     @each $lang in $cjk-langs {
       &:lang(#{$lang}) {
@@ -348,7 +348,7 @@
     padding: 7px 4px;
     margin-bottom: 10px;
     font-size: 16px;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     display: block;
     width: 100%;
     outline: 0;
@@ -402,7 +402,7 @@
   font-size: 14px;
 
   a {
-    color: $classic-highlight-color;
+    color: $highlight-text-color;
     text-decoration: none;
 
     &:hover {
@@ -425,7 +425,7 @@
     align-items: center;
     padding: 10px;
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     border-radius: 4px 4px 0 0;
     font-size: 14px;
     position: relative;
@@ -452,14 +452,14 @@
   }
 
   &__timestamp {
-    color: lighten($ui-base-color, 34%);
+    color: $darker-text-color;
   }
 
   &__extras {
     background: lighten($ui-base-color, 6%);
     border-radius: 0 0 4px 4px;
     padding: 10px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     font-family: 'mastodon-font-monospace', monospace;
     font-size: 12px;
     word-wrap: break-word;
@@ -469,7 +469,7 @@
   &__icon {
     font-size: 28px;
     margin-right: 10px;
-    color: lighten($ui-base-color, 34%);
+    color: $darker-text-color;
   }
 
   &__icon__overlay {
@@ -485,7 +485,7 @@
     }
 
     &.negative {
-      background: $error-red;
+      background: lighten($error-red, 12%);
     }
 
     &.neutral {
@@ -496,17 +496,17 @@
   a,
   .username,
   .target {
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     text-decoration: none;
     font-weight: 500;
   }
 
   .diff-old {
-    color: $error-red;
+    color: lighten($error-red, 12%);
   }
 
   .diff-neutral {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   .diff-new {
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index bec0d4d91..c52e069be 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -75,7 +75,7 @@ body {
   &.error {
     position: absolute;
     text-align: center;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     background: $ui-base-color;
     width: 100%;
     height: 100%;
diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss
index 31053decc..8e11cb596 100644
--- a/app/javascript/styles/mastodon/boost.scss
+++ b/app/javascript/styles/mastodon/boost.scss
@@ -6,13 +6,13 @@
 }
 
 button.icon-button i.fa-retweet {
-  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-base-lighter-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($action-button-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 
   &:hover {
-    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 33%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($action-button-color, 7%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
   }
 }
 
 button.icon-button.disabled i.fa-retweet {
-  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 13%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(darken($action-button-color, 13%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 }
diff --git a/app/javascript/styles/mastodon/compact_header.scss b/app/javascript/styles/mastodon/compact_header.scss
index 90d98cc8c..83ac7a8d0 100644
--- a/app/javascript/styles/mastodon/compact_header.scss
+++ b/app/javascript/styles/mastodon/compact_header.scss
@@ -2,7 +2,7 @@
   h1 {
     font-size: 24px;
     line-height: 28px;
-    color: $ui-primary-color;
+    color: $primary-text-color;
     font-weight: 500;
     margin-bottom: 20px;
     padding: 0 10px;
@@ -20,7 +20,7 @@
 
     small {
       font-weight: 400;
-      color: $ui-secondary-color;
+      color: $darker-text-color;
     }
 
     img {
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 94e3089f8..ce9f316a0 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4,7 +4,7 @@
 }
 
 .button {
-  background-color: darken($ui-highlight-color, 3%);
+  background-color: $ui-highlight-color;
   border: 10px none;
   border-radius: 4px;
   box-sizing: border-box;
@@ -31,7 +31,7 @@
   &:active,
   &:focus,
   &:hover {
-    background-color: lighten($ui-highlight-color, 7%);
+    background-color: lighten($ui-highlight-color, 4%);
     transition: all 200ms ease-out;
   }
 
@@ -52,7 +52,7 @@
   }
 
   &.button-alternative {
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $ui-primary-color;
 
     &:active,
@@ -98,26 +98,10 @@
   position: relative;
 }
 
-.column-icon {
-  background: lighten($ui-base-color, 4%);
-  color: $ui-primary-color;
-  cursor: pointer;
-  font-size: 16px;
-  padding: 15px;
-  position: absolute;
-  right: 0;
-  top: -48px;
-  z-index: 3;
-
-  &:hover {
-    color: lighten($ui-primary-color, 7%);
-  }
-}
-
 .icon-button {
   display: inline-block;
   padding: 0;
-  color: $ui-base-lighter-color;
+  color: $action-button-color;
   border: none;
   background: transparent;
   cursor: pointer;
@@ -126,17 +110,17 @@
   &:hover,
   &:active,
   &:focus {
-    color: lighten($ui-base-color, 33%);
+    color: lighten($action-button-color, 7%);
     transition: color 200ms ease-out;
   }
 
   &.disabled {
-    color: lighten($ui-base-color, 13%);
+    color: darken($action-button-color, 13%);
     cursor: default;
   }
 
   &.active {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &::-moz-focus-inner {
@@ -150,23 +134,23 @@
   }
 
   &.inverted {
-    color: lighten($ui-base-color, 33%);
+    color: $lighter-text-color;
 
     &:hover,
     &:active,
     &:focus {
-      color: $ui-base-lighter-color;
+      color: transparentize($lighter-text-color, 0.07);
     }
 
     &.disabled {
-      color: $ui-primary-color;
+      color: opacify($lighter-text-color, 0.07);
     }
 
     &.active {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
 
       &.disabled {
-        color: lighten($ui-highlight-color, 13%);
+        color: opacify($lighter-text-color, 0.13);
       }
     }
   }
@@ -185,7 +169,7 @@
 }
 
 .text-icon-button {
-  color: lighten($ui-base-color, 33%);
+  color: $lighter-text-color;
   border: none;
   background: transparent;
   cursor: pointer;
@@ -199,17 +183,17 @@
   &:hover,
   &:active,
   &:focus {
-    color: $ui-base-lighter-color;
+    color: opacify($lighter-text-color, 0.07);
     transition: color 200ms ease-out;
   }
 
   &.disabled {
-    color: lighten($ui-base-color, 13%);
+    color: transparentize($lighter-text-color, 0.2);
     cursor: default;
   }
 
   &.active {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &::-moz-focus-inner {
@@ -228,25 +212,6 @@
   transform-origin: 50% 0;
 }
 
-.dropdown--active .icon-button {
-  color: $ui-highlight-color;
-}
-
-.dropdown--active::after {
-  @media screen and (min-width: 631px) {
-    content: "";
-    display: block;
-    position: absolute;
-    width: 0;
-    height: 0;
-    border-style: solid;
-    border-width: 0 4.5px 7.8px;
-    border-color: transparent transparent $ui-secondary-color;
-    bottom: 8px;
-    right: 104px;
-  }
-}
-
 .invisible {
   font-size: 0;
   line-height: 0;
@@ -271,15 +236,11 @@
   }
 }
 
-.lightbox .icon-button {
-  color: $ui-base-color;
-}
-
 .compose-form {
   padding: 10px;
 
   .compose-form__warning {
-    color: darken($ui-secondary-color, 65%);
+    color: $inverted-text-color;
     margin-bottom: 15px;
     background: $ui-primary-color;
     box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3);
@@ -289,7 +250,7 @@
     font-weight: 400;
 
     strong {
-      color: darken($ui-secondary-color, 65%);
+      color: $inverted-text-color;
       font-weight: 500;
 
       @each $lang in $cjk-langs {
@@ -300,7 +261,7 @@
     }
 
     a {
-      color: darken($ui-primary-color, 33%);
+      color: $lighter-text-color;
       font-weight: 500;
       text-decoration: underline;
 
@@ -333,7 +294,7 @@
     box-sizing: border-box;
     width: 100%;
     margin: 0;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $simple-background-color;
     padding: 10px;
     font-family: inherit;
@@ -378,7 +339,7 @@
     box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
     background: $ui-secondary-color;
     border-radius: 0 0 4px 4px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     font-size: 14px;
     padding: 6px;
 
@@ -419,11 +380,11 @@
   }
 
   .autosuggest-account .display-name__account {
-    color: lighten($ui-base-color, 36%);
+    color: $lighter-text-color;
   }
 
   .compose-form__modifiers {
-    color: $ui-base-color;
+    color: $inverted-text-color;
     font-family: inherit;
     font-size: 14px;
     background: $simple-background-color;
@@ -454,7 +415,7 @@
 
         .icon-button {
           flex: 0 1 auto;
-          color: $ui-secondary-color;
+          color: $action-button-color;
           font-size: 14px;
           font-weight: 500;
           padding: 10px;
@@ -463,7 +424,7 @@
           &:hover,
           &:focus,
           &:active {
-            color: lighten($ui-secondary-color, 4%);
+            color: lighten($action-button-color, 7%);
           }
         }
 
@@ -486,7 +447,7 @@
 
         input {
           background: transparent;
-          color: $ui-secondary-color;
+          color: $primary-text-color;
           border: 0;
           padding: 0;
           margin: 0;
@@ -501,7 +462,7 @@
 
           &::placeholder {
             opacity: 0.54;
-            color: $ui-secondary-color;
+            color: $darker-text-color;
           }
         }
 
@@ -563,7 +524,7 @@
         font-family: 'mastodon-font-sans-serif', sans-serif;
         font-size: 14px;
         font-weight: 600;
-        color: lighten($ui-base-color, 12%);
+        color: $lighter-text-color;
 
         &.character-counter--over {
           color: $warning-red;
@@ -617,7 +578,7 @@
 }
 
 .reply-indicator__display-name {
-  color: $ui-base-color;
+  color: $lighter-text-color;
   display: block;
   max-width: 100%;
   line-height: 24px;
@@ -679,7 +640,7 @@
       text-decoration: underline;
 
       .fa {
-        color: lighten($ui-base-color, 40%);
+        color: lighten($action-button-color, 7%);
       }
     }
 
@@ -694,15 +655,15 @@
     }
 
     .fa {
-      color: lighten($ui-base-color, 30%);
+      color: $action-button-color;
     }
   }
 
   .status__content__spoiler-link {
-    background: lighten($ui-base-color, 30%);
+    background: $action-button-color;
 
     &:hover {
-      background: lighten($ui-base-color, 33%);
+      background: lighten($action-button-color, 7%);
       text-decoration: none;
     }
   }
@@ -721,7 +682,7 @@
   border-radius: 2px;
   background: transparent;
   border: 0;
-  color: lighten($ui-base-color, 8%);
+  color: $lighter-text-color;
   font-weight: 700;
   font-size: 11px;
   padding: 0 6px;
@@ -784,36 +745,32 @@
 
   &.status-direct {
     background: lighten($ui-base-color, 8%);
-
-    .icon-button.disabled {
-      color: lighten($ui-base-color, 16%);
-    }
   }
 
   &.light {
     .status__relative-time {
-      color: $ui-primary-color;
+      color: $lighter-text-color;
     }
 
     .status__display-name {
-      color: $ui-base-color;
+      color: $inverted-text-color;
     }
 
     .display-name {
       strong {
-        color: $ui-base-color;
+        color: $inverted-text-color;
       }
 
       span {
-        color: $ui-primary-color;
+        color: $lighter-text-color;
       }
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -833,19 +790,19 @@
     background: transparent;
 
     .icon-button.disabled {
-      color: lighten($ui-base-color, 13%);
+      color: lighten($action-button-color, 13%);
     }
   }
 }
 
 .status__relative-time {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   float: right;
   font-size: 14px;
 }
 
 .status__display-name {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 }
 
 .status__info .status__display-name {
@@ -896,14 +853,14 @@
 
 .status__prepend {
   margin-left: 68px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   padding: 8px 0;
   padding-bottom: 2px;
   font-size: 14px;
   position: relative;
 
   .status__display-name strong {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   > span {
@@ -965,7 +922,7 @@
 
 .detailed-status__meta {
   margin-top: 15px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   font-size: 14px;
   line-height: 18px;
 }
@@ -993,11 +950,11 @@
 }
 
 .reply-indicator__content {
-  color: $ui-base-color;
+  color: $inverted-text-color;
   font-size: 14px;
 
   a {
-    color: lighten($ui-base-color, 20%);
+    color: $lighter-text-color;
   }
 }
 
@@ -1032,7 +989,7 @@
   .account__display-name {
     flex: 1 1 auto;
     display: block;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     overflow: hidden;
     text-decoration: none;
     font-size: 14px;
@@ -1102,7 +1059,7 @@
     }
 
     .account__header__username {
-      color: $ui-primary-color;
+      color: $darker-text-color;
     }
   }
 
@@ -1112,7 +1069,7 @@
   }
 
   .account__header__content {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   .account__header__display-name {
@@ -1127,7 +1084,7 @@
   }
 
   .account__header__username {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     font-size: 14px;
     font-weight: 400;
     display: block;
@@ -1140,7 +1097,7 @@
 .account__disclaimer {
   padding: 10px;
   border-top: 1px solid lighten($ui-base-color, 8%);
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 
   strong {
     font-weight: 500;
@@ -1166,7 +1123,7 @@
 }
 
 .account__header__content {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-size: 14px;
   font-weight: 400;
   overflow: hidden;
@@ -1243,7 +1200,7 @@
     display: block;
     text-transform: uppercase;
     font-size: 11px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
   }
 
   strong {
@@ -1258,10 +1215,6 @@
       }
     }
   }
-
-  abbr {
-    color: $ui-base-lighter-color;
-  }
 }
 
 .account__header__avatar {
@@ -1331,7 +1284,7 @@
 }
 
 .detailed-status__display-name {
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   display: block;
   line-height: 24px;
   margin-bottom: 15px;
@@ -1366,11 +1319,11 @@
 .muted {
   .status__content p,
   .status__content a {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   .status__display-name strong {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   .status__avatar {
@@ -1378,11 +1331,11 @@
   }
 
   a.status__content__spoiler-link {
-    background: $ui-base-lighter-color;
+    background: $darker-text-color;
     color: lighten($ui-base-color, 4%);
 
     &:hover {
-      background: lighten($ui-base-color, 29%);
+      background: transparentize($darker-text-color, 0.07);
       text-decoration: none;
     }
   }
@@ -1398,7 +1351,7 @@
   position: relative;
 
   .fa {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   > span {
@@ -1501,7 +1454,7 @@
   display: flex;
   flex-shrink: 0;
   cursor: default;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   strong {
     color: $primary-text-color;
@@ -1609,7 +1562,7 @@
     box-sizing: border-box;
     text-decoration: none;
     background: $ui-secondary-color;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
@@ -1618,7 +1571,7 @@
     &:hover,
     &:active {
       background: $ui-highlight-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       outline: 0;
     }
   }
@@ -1660,7 +1613,7 @@
     box-sizing: border-box;
     text-decoration: none;
     background: $ui-secondary-color;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
@@ -1671,7 +1624,7 @@
 
     &:hover {
       background: $ui-highlight-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
     }
   }
 }
@@ -1683,7 +1636,7 @@
 .static-content {
   padding: 10px;
   padding-top: 20px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 
   h1 {
     font-size: 16px;
@@ -1935,8 +1888,8 @@
   }
 
   &.active {
-    border-bottom: 2px solid $ui-highlight-color;
-    color: $ui-highlight-color;
+    border-bottom: 2px solid $highlight-text-color;
+    color: $highlight-text-color;
   }
 
   &:hover,
@@ -1991,7 +1944,7 @@
 
 .column-back-button {
   background: lighten($ui-base-color, 4%);
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
   cursor: pointer;
   flex: 0 0 auto;
   font-size: 16px;
@@ -2010,7 +1963,7 @@
   background: lighten($ui-base-color, 4%);
   border: 0;
   font-family: inherit;
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
   cursor: pointer;
   white-space: nowrap;
   font-size: 16px;
@@ -2182,7 +2135,7 @@
 
 .column-subheading {
   background: $ui-base-color;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   padding: 8px 20px;
   font-size: 12px;
   font-weight: 500;
@@ -2205,11 +2158,11 @@
   flex: 1 0 auto;
 
   p {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   a {
-    color: $ui-base-lighter-color;
+    color: opacify($darker-text-color, 0.07);
   }
 }
 
@@ -2235,7 +2188,7 @@
 }
 
 .setting-text {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   background: transparent;
   border: none;
   border-bottom: 2px solid $ui-primary-color;
@@ -2249,23 +2202,12 @@
   &:focus,
   &:active {
     color: $primary-text-color;
-    border-bottom-color: $ui-highlight-color;
+    border-bottom-color: $highlight-text-color;
   }
 
   @media screen and (max-width: 600px) {
     font-size: 16px;
   }
-
-  &.light {
-    color: $ui-base-color;
-    border-bottom: 2px solid lighten($ui-base-color, 27%);
-
-    &:focus,
-    &:active {
-      color: $ui-base-color;
-      border-bottom-color: $ui-highlight-color;
-    }
-  }
 }
 
 .no-reduce-motion button.icon-button i.fa-retweet {
@@ -2288,12 +2230,12 @@
 }
 
 .reduce-motion button.icon-button i.fa-retweet {
-  color: $ui-base-lighter-color;
+  color: $action-button-color;
   transition: color 100ms ease-in;
 }
 
 .reduce-motion button.icon-button.active i.fa-retweet {
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
 }
 
 .status-card {
@@ -2301,7 +2243,7 @@
   font-size: 14px;
   border: 1px solid lighten($ui-base-color, 8%);
   border-radius: 4px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   margin-top: 14px;
   text-decoration: none;
   overflow: hidden;
@@ -2439,7 +2381,7 @@ a.status-card {
 
 .load-more {
   display: block;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   background-color: transparent;
   border: 0;
   font-size: inherit;
@@ -2463,7 +2405,7 @@ a.status-card {
   text-align: center;
   font-size: 16px;
   font-weight: 500;
-  color: lighten($ui-base-color, 16%);
+  color: opacify($darker-text-color, 0.07);
   background: $ui-base-color;
   cursor: default;
   display: flex;
@@ -2503,7 +2445,7 @@ a.status-card {
     strong {
       display: block;
       margin-bottom: 10px;
-      color: lighten($ui-base-color, 34%);
+      color: $darker-text-color;
     }
 
     span {
@@ -2561,15 +2503,15 @@ a.status-card {
   }
 
   & > .column-header__back-button {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &.active {
-    box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3);
+    box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
 
     .column-header__icon {
-      color: $ui-highlight-color;
-      text-shadow: 0 0 10px rgba($ui-highlight-color, 0.4);
+      color: $highlight-text-color;
+      text-shadow: 0 0 10px rgba($highlight-text-color, 0.4);
     }
   }
 
@@ -2615,7 +2557,7 @@ a.status-card {
   max-height: 70vh;
   overflow: hidden;
   overflow-y: auto;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   transition: max-height 150ms ease-in-out, opacity 300ms linear;
   opacity: 1;
 
@@ -2644,7 +2586,7 @@ a.status-card {
 
 .column-header__setting-btn {
   &:hover {
-    color: lighten($ui-primary-color, 4%);
+    color: $darker-text-color;
     text-decoration: underline;
   }
 }
@@ -2678,7 +2620,7 @@ a.status-card {
 }
 
 .loading-indicator {
-  color: lighten($ui-base-color, 26%);
+  color: $darker-text-color;
   font-size: 12px;
   font-weight: 400;
   text-transform: uppercase;
@@ -2763,7 +2705,7 @@ a.status-card {
 
 .media-spoiler {
   background: $base-overlay-background;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   border: 0;
   padding: 0;
   width: 100%;
@@ -2775,7 +2717,7 @@ a.status-card {
   &:active,
   &:focus {
     padding: 0;
-    color: lighten($ui-primary-color, 8%);
+    color: transparentize($darker-text-color, 0.07);
   }
 }
 
@@ -2828,7 +2770,7 @@ a.status-card {
 }
 
 .column-settings__section {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   cursor: default;
   display: block;
   font-weight: 500;
@@ -2886,7 +2828,7 @@ a.status-card {
 
 .setting-toggle__label,
 .setting-meta__label {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   display: inline-block;
   margin-bottom: 14px;
   margin-left: 8px;
@@ -2894,13 +2836,12 @@ a.status-card {
 }
 
 .setting-meta__label {
-  color: $ui-primary-color;
   float: right;
 }
 
 .empty-column-indicator,
 .error-column {
-  color: lighten($ui-base-color, 20%);
+  color: $darker-text-color;
   background: $ui-base-color;
   text-align: center;
   padding: 20px;
@@ -2917,7 +2858,7 @@ a.status-card {
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-decoration: none;
 
     &:hover {
@@ -3102,7 +3043,7 @@ a.status-card {
   display: flex;
   align-items: center;
   justify-content: center;
-  color: $ui-secondary-color;
+  color: $primary-text-color;
   font-size: 18px;
   font-weight: 500;
   border: 2px dashed $ui-base-lighter-color;
@@ -3111,7 +3052,7 @@ a.status-card {
 
 .upload-progress {
   padding: 10px;
-  color: $ui-base-lighter-color;
+  color: $lighter-text-color;
   overflow: hidden;
   display: flex;
 
@@ -3200,7 +3141,7 @@ a.status-card {
 }
 
 .privacy-dropdown__option {
-  color: $ui-base-color;
+  color: $lighter-text-color;
   padding: 10px;
   cursor: pointer;
   display: flex;
@@ -3233,12 +3174,12 @@ a.status-card {
 
 .privacy-dropdown__option__content {
   flex: 1 1 auto;
-  color: darken($ui-primary-color, 24%);
+  color: $lighter-text-color;
 
   strong {
     font-weight: 500;
     display: block;
-    color: $ui-base-color;
+    color: $inverted-text-color;
 
     @each $lang in $cjk-langs {
       &:lang(#{$lang}) {
@@ -3287,7 +3228,7 @@ a.status-card {
   padding-right: 30px;
   font-family: inherit;
   background: $ui-base-color;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-size: 14px;
   margin: 0;
 
@@ -3344,6 +3285,7 @@ a.status-card {
   .fa-times-circle {
     top: 11px;
     transform: rotate(0deg);
+    color: $action-button-color;
     cursor: pointer;
 
     &.active {
@@ -3351,13 +3293,13 @@ a.status-card {
     }
 
     &:hover {
-      color: $primary-text-color;
+      color: lighten($action-button-color, 7%);
     }
   }
 }
 
 .search-results__header {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   background: lighten($ui-base-color, 2%);
   border-bottom: 1px solid darken($ui-base-color, 4%);
   padding: 15px 10px;
@@ -3386,7 +3328,7 @@ a.status-card {
     span {
       display: inline-block;
       background: $ui-base-color;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       font-size: 14px;
       font-weight: 500;
       padding: 10px;
@@ -3405,13 +3347,13 @@ a.status-card {
 .search-results__hashtag {
   display: block;
   padding: 10px;
-  color: $ui-secondary-color;
+  color: darken($primary-text-color, 4%);
   text-decoration: none;
 
   &:hover,
   &:active,
   &:focus {
-    color: lighten($ui-secondary-color, 4%);
+    color: $primary-text-color;
     text-decoration: underline;
   }
 }
@@ -3549,7 +3491,7 @@ a.status-card {
 }
 
 .media-modal__button {
-  background-color: $white;
+  background-color: $primary-text-color;
   height: 12px;
   width: 12px;
   border-radius: 6px;
@@ -3560,7 +3502,7 @@ a.status-card {
 }
 
 .media-modal__button--active {
-  background-color: $ui-highlight-color;
+  background-color: $highlight-text-color;
 }
 
 .media-modal__close {
@@ -3574,7 +3516,7 @@ a.status-card {
 .error-modal,
 .embed-modal {
   background: $ui-secondary-color;
-  color: $ui-base-color;
+  color: $inverted-text-color;
   border-radius: 8px;
   overflow: hidden;
   display: flex;
@@ -3662,7 +3604,7 @@ a.status-card {
 
   .onboarding-modal__nav,
   .error-modal__nav {
-    color: darken($ui-secondary-color, 34%);
+    color: $lighter-text-color;
     border: 0;
     font-size: 14px;
     font-weight: 500;
@@ -3676,18 +3618,18 @@ a.status-card {
     &:hover,
     &:focus,
     &:active {
-      color: darken($ui-secondary-color, 38%);
+      color: transparentize($lighter-text-color, 0.04);
       background-color: darken($ui-secondary-color, 16%);
     }
 
     &.onboarding-modal__done,
     &.onboarding-modal__next {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       &:hover,
       &:focus,
       &:active {
-        color: darken($ui-base-color, 4%);
+        color: lighten($inverted-text-color, 4%);
       }
     }
   }
@@ -3739,17 +3681,17 @@ a.status-card {
   h1 {
     font-size: 18px;
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     margin-bottom: 20px;
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
 
     &:hover,
     &:focus,
     &:active {
-      color: lighten($ui-highlight-color, 4%);
+      color: lighten($highlight-text-color, 4%);
     }
   }
 
@@ -3759,7 +3701,7 @@ a.status-card {
 
   p {
     font-size: 16px;
-    color: lighten($ui-base-color, 8%);
+    color: $lighter-text-color;
     margin-top: 10px;
     margin-bottom: 10px;
 
@@ -3770,7 +3712,7 @@ a.status-card {
     strong {
       font-weight: 500;
       background: $ui-base-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       border-radius: 4px;
       font-size: 14px;
       padding: 3px 6px;
@@ -3822,7 +3764,7 @@ a.status-card {
 
   &__label {
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     margin-bottom: 5px;
     text-transform: uppercase;
     font-size: 12px;
@@ -3830,7 +3772,7 @@ a.status-card {
 
   &__case {
     background: $ui-base-color;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     font-weight: 500;
     padding: 10px;
     border-radius: 4px;
@@ -3847,7 +3789,7 @@ a.status-card {
 
   .figure {
     background: darken($ui-base-color, 8%);
-    color: $ui-secondary-color;
+    color: $darker-text-color;
     margin-bottom: 20px;
     border-radius: 4px;
     padding: 10px;
@@ -3936,7 +3878,7 @@ a.status-card {
 .actions-modal,
 .mute-modal {
   background: lighten($ui-secondary-color, 8%);
-  color: $ui-base-color;
+  color: $inverted-text-color;
   border-radius: 8px;
   overflow: hidden;
   max-width: 90vw;
@@ -3994,7 +3936,7 @@ a.status-card {
   & > div {
     flex: 1 1 auto;
     text-align: right;
-    color: lighten($ui-base-color, 33%);
+    color: $lighter-text-color;
     padding-right: 10px;
   }
 
@@ -4081,7 +4023,7 @@ a.status-card {
     box-sizing: border-box;
     width: 100%;
     margin: 0;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $white;
     padding: 10px;
     font-family: inherit;
@@ -4103,7 +4045,7 @@ a.status-card {
     margin-bottom: 24px;
 
     &__label {
-      color: $ui-base-color;
+      color: $inverted-text-color;
       font-size: 14px;
     }
   }
@@ -4142,7 +4084,7 @@ a.status-card {
 
     li:not(:empty) {
       a {
-        color: $ui-base-color;
+        color: $inverted-text-color;
         display: flex;
         padding: 12px 16px;
         font-size: 15px;
@@ -4178,14 +4120,14 @@ a.status-card {
   .confirmation-modal__cancel-button,
   .mute-modal__cancel-button {
     background-color: transparent;
-    color: darken($ui-secondary-color, 34%);
+    color: $lighter-text-color;
     font-size: 14px;
     font-weight: 500;
 
     &:hover,
     &:focus,
     &:active {
-      color: darken($ui-secondary-color, 38%);
+      color: transparentize($lighter-text-color, 0.04);
     }
   }
 }
@@ -4218,7 +4160,7 @@ a.status-card {
 }
 
 .loading-bar {
-  background-color: $ui-highlight-color;
+  background-color: $highlight-text-color;
   height: 3px;
   position: absolute;
   top: 0;
@@ -4266,7 +4208,7 @@ a.status-card {
 
   &__icon {
     flex: 0 0 auto;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
     padding: 8px 18px;
     cursor: default;
     border-right: 1px solid lighten($ui-base-color, 8%);
@@ -4296,7 +4238,7 @@ a.status-card {
 
     a {
       text-decoration: none;
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
       font-weight: 500;
 
       &:hover {
@@ -4315,7 +4257,7 @@ a.status-card {
     }
 
     .fa {
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
     }
   }
 }
@@ -4511,7 +4453,7 @@ a.status-card {
     z-index: 4;
     border: 0;
     background: $base-shadow-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     transition: none;
     pointer-events: none;
 
@@ -4522,7 +4464,7 @@ a.status-card {
       &:hover,
       &:active,
       &:focus {
-        color: lighten($ui-primary-color, 8%);
+        color: transparentize($darker-text-color, 0.07);
       }
     }
 
@@ -4719,7 +4661,7 @@ a.status-card {
     background-size: cover;
     background-position: center;
     position: absolute;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     text-decoration: none;
     border-radius: 4px;
 
@@ -4727,7 +4669,7 @@ a.status-card {
     &:active,
     &:focus {
       outline: 0;
-      color: $ui-secondary-color;
+      color: transparentize($darker-text-color, 0.07);
 
       &::before {
         content: "";
@@ -4758,7 +4700,7 @@ a.status-card {
   a {
     display: block;
     flex: 1 1 auto;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     padding: 15px 0;
     font-size: 14px;
     font-weight: 500;
@@ -4767,7 +4709,7 @@ a.status-card {
     position: relative;
 
     &.active {
-      color: $ui-secondary-color;
+      color: transparentize($darker-text-color, 0.07);
 
       &::before,
       &::after {
@@ -4802,12 +4744,12 @@ a.status-card {
   padding: 10px 14px;
   padding-bottom: 14px;
   margin-top: 10px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
 
   h4 {
     text-transform: uppercase;
-    color: $ui-primary-color;
+    color: $lighter-text-color;
     font-size: 13px;
     font-weight: 500;
     margin-bottom: 10px;
@@ -4823,7 +4765,7 @@ a.status-card {
 
   em {
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
   }
 }
 
@@ -4839,11 +4781,11 @@ noscript {
   div {
     font-size: 14px;
     margin: 30px auto;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     max-width: 400px;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
 
       &:hover {
@@ -4941,7 +4883,6 @@ noscript {
     }
 
     .embed-modal__html {
-      color: $ui-secondary-color;
       outline: 0;
       box-sizing: border-box;
       display: block;
@@ -4950,7 +4891,7 @@ noscript {
       padding: 10px;
       font-family: 'mastodon-font-monospace', monospace;
       background: $ui-base-color;
-      color: $ui-primary-color;
+      color: $primary-text-color;
       font-size: 14px;
       margin: 0;
       margin-bottom: 15px;
@@ -4993,7 +4934,7 @@ noscript {
   &__message {
     position: relative;
     margin-left: 58px;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
     padding: 8px 0;
     padding-top: 0;
     padding-bottom: 4px;
@@ -5176,3 +5117,40 @@ noscript {
     background: lighten($ui-highlight-color, 7%);
   }
 }
+
+.account__header .account__header__fields {
+  font-size: 14px;
+  line-height: 20px;
+  overflow: hidden;
+  border-collapse: collapse;
+  margin: 20px -10px -20px;
+  border-bottom: 0;
+
+  tr {
+    border-top: 1px solid lighten($ui-base-color, 8%);
+    text-align: center;
+  }
+
+  th,
+  td {
+    padding: 14px 20px;
+    vertical-align: middle;
+    max-height: 40px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+
+  th {
+    color: $darker-text-color;
+    background: darken($ui-base-color, 4%);
+    max-width: 120px;
+    font-weight: 500;
+  }
+
+  td {
+    flex: auto;
+    color: $primary-text-color;
+    background: $ui-base-color;
+  }
+}
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index e761f58eb..8df2902d2 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -100,7 +100,7 @@
 
   .name {
     flex: 1 1 auto;
-    color: $ui-secondary-color;
+    color: $darker-text-color;
     width: calc(100% - 88px);
 
     .username {
diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss
index 4161cc045..3620a6f54 100644
--- a/app/javascript/styles/mastodon/emoji_picker.scss
+++ b/app/javascript/styles/mastodon/emoji_picker.scss
@@ -7,7 +7,7 @@
 
   font-size: 13px;
   display: inline-block;
-  color: $ui-base-color;
+  color: $inverted-text-color;
 
   .emoji-mart-emoji {
     padding: 6px;
@@ -36,7 +36,7 @@
   display: flex;
   justify-content: space-between;
   padding: 0 6px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   line-height: 0;
 }
 
@@ -50,15 +50,15 @@
   cursor: pointer;
 
   &:hover {
-    color: darken($ui-primary-color, 4%);
+    color: opacify($lighter-text-color, 0.04);
   }
 }
 
 .emoji-mart-anchor-selected {
-  color: darken($ui-highlight-color, 3%);
+  color: $highlight-text-color;
 
   &:hover {
-    color: darken($ui-highlight-color, 3%);
+    color: darken($highlight-text-color, 4%);
   }
 
   .emoji-mart-anchor-bar {
@@ -72,7 +72,7 @@
   left: 0;
   width: 100%;
   height: 3px;
-  background-color: darken($ui-highlight-color, 3%);
+  background-color: $highlight-text-color;
 }
 
 .emoji-mart-anchors {
@@ -115,7 +115,7 @@
     display: block;
     width: 100%;
     background: rgba($ui-secondary-color, 0.3);
-    color: $ui-primary-color;
+    color: $inverted-text-color;
     border: 1px solid $ui-secondary-color;
     border-radius: 4px;
 
@@ -184,7 +184,7 @@
   font-size: 14px;
   text-align: center;
   padding-top: 70px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
 
   .emoji-mart-category-label {
     display: none;
diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss
index 2d953b34e..ba2a06954 100644
--- a/app/javascript/styles/mastodon/footer.scss
+++ b/app/javascript/styles/mastodon/footer.scss
@@ -2,7 +2,7 @@
   text-align: center;
   margin-top: 30px;
   font-size: 12px;
-  color: darken($ui-secondary-color, 25%);
+  color: $darker-text-color;
 
   .domain {
     font-weight: 500;
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index d74c5a4fd..3a3b4c326 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -15,16 +15,28 @@ code {
     overflow: hidden;
   }
 
+  .row {
+    display: flex;
+    margin: 0 -5px;
+
+    .input {
+      box-sizing: border-box;
+      flex: 1 1 auto;
+      width: 50%;
+      padding: 0 5px;
+    }
+  }
+
   span.hint {
     display: block;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     font-size: 12px;
     margin-top: 4px;
   }
 
   p.hint {
     margin-bottom: 15px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     &.subtle-hint {
       text-align: center;
@@ -32,10 +44,10 @@ code {
       line-height: 18px;
       margin-top: 15px;
       margin-bottom: 0;
-      color: $ui-primary-color;
+      color: $darker-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
     }
   }
@@ -232,35 +244,35 @@ code {
     }
 
     &:focus:invalid {
-      border-bottom-color: $error-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     &:required:valid {
-      border-bottom-color: $valid-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     &:active,
     &:focus {
-      border-bottom-color: $ui-highlight-color;
+      border-bottom-color: $highlight-text-color;
       background: rgba($base-overlay-background, 0.1);
     }
   }
 
   .input.field_with_errors {
     label {
-      color: $error-value-color;
+      color: lighten($error-red, 12%);
     }
 
     input[type=text],
     input[type=email],
     input[type=password] {
-      border-bottom-color: $error-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     .error {
       display: block;
       font-weight: 500;
-      color: $error-value-color;
+      color: lighten($error-red, 12%);
       margin-top: 4px;
     }
   }
@@ -344,7 +356,7 @@ code {
       padding: 7px 4px;
       padding-bottom: 9px;
       font-size: 16px;
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
       font-family: inherit;
       pointer-events: none;
       cursor: default;
@@ -354,7 +366,7 @@ code {
 
 .flash-message {
   background: lighten($ui-base-color, 8%);
-  color: $ui-primary-color;
+  color: $darker-text-color;
   border-radius: 4px;
   padding: 15px 10px;
   margin-bottom: 30px;
@@ -366,7 +378,6 @@ code {
   }
 
   .oauth-code {
-    color: $ui-secondary-color;
     outline: 0;
     box-sizing: border-box;
     display: block;
@@ -375,7 +386,7 @@ code {
     padding: 10px;
     font-family: 'mastodon-font-monospace', monospace;
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $primary-text-color;
     font-size: 14px;
     margin: 0;
 
@@ -414,7 +425,7 @@ code {
   text-align: center;
 
   a {
-    color: $ui-primary-color;
+    color: $darker-text-color;
     text-decoration: none;
 
     &:hover {
@@ -427,7 +438,7 @@ code {
 .follow-prompt {
   margin-bottom: 30px;
   text-align: center;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   h2 {
     font-size: 16px;
@@ -435,7 +446,7 @@ code {
   }
 
   strong {
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     font-weight: 500;
 
     @each $lang in $cjk-langs {
@@ -472,7 +483,7 @@ code {
 
 .qr-alternative {
   margin-bottom: 20px;
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   flex: 150px;
 
   samp {
@@ -557,7 +568,7 @@ code {
 
 .post-follow-actions {
   text-align: center;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   div {
     margin-bottom: 4px;
@@ -570,7 +581,7 @@ code {
 
   h4 {
     font-size: 16px;
-    color: $ui-base-lighter-color;
+    color: $primary-text-color;
     text-align: center;
     margin-bottom: 20px;
     border: 0;
diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss
index ffa1e149d..651c06ced 100644
--- a/app/javascript/styles/mastodon/landing_strip.scss
+++ b/app/javascript/styles/mastodon/landing_strip.scss
@@ -1,7 +1,7 @@
 .landing-strip,
 .memoriam-strip {
   background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-weight: 400;
   padding: 14px;
   border-radius: 4px;
@@ -45,7 +45,7 @@
   padding: 14px;
   border-radius: 4px;
   background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   font-weight: 400;
   margin-bottom: 20px;
 
@@ -88,7 +88,7 @@
 
     .fa {
       margin-right: 5px;
-      color: $ui-primary-color;
+      color: $darker-text-color;
     }
   }
 
@@ -103,7 +103,7 @@
       text-decoration: none;
 
       span {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         font-weight: 400;
       }
     }
diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss
index cc5ba9d7c..58d4de35f 100644
--- a/app/javascript/styles/mastodon/reset.scss
+++ b/app/javascript/styles/mastodon/reset.scss
@@ -53,11 +53,6 @@ table {
   border-spacing: 0;
 }
 
-::-webkit-scrollbar {
-  width: 8px;
-  height: 8px;
-}
-
 ::-webkit-scrollbar-thumb {
   background: lighten($ui-base-color, 4%);
   border: 0px none $base-border-color;
diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss
index dfdc48d06..c39163ba8 100644
--- a/app/javascript/styles/mastodon/stream_entries.scss
+++ b/app/javascript/styles/mastodon/stream_entries.scss
@@ -84,7 +84,7 @@
         font-size: 14px;
 
         .status__relative-time {
-          color: $ui-primary-color;
+          color: $lighter-text-color;
         }
       }
     }
@@ -93,7 +93,7 @@
       display: block;
       max-width: 100%;
       padding-right: 25px;
-      color: $ui-base-color;
+      color: $lighter-text-color;
     }
 
     .status__avatar {
@@ -123,7 +123,7 @@
 
       strong {
         font-weight: 500;
-        color: $ui-base-color;
+        color: $inverted-text-color;
 
         @each $lang in $cjk-langs {
           &:lang(#{$lang}) {
@@ -134,15 +134,15 @@
 
       span {
         font-size: 14px;
-        color: $ui-primary-color;
+        color: $inverted-text-color;
       }
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -180,7 +180,7 @@
 
         strong {
           font-weight: 500;
-          color: $ui-base-color;
+          color: $inverted-text-color;
 
           @each $lang in $cjk-langs {
             &:lang(#{$lang}) {
@@ -191,7 +191,7 @@
 
         span {
           font-size: 14px;
-          color: $ui-primary-color;
+          color: $lighter-text-color;
         }
       }
     }
@@ -207,10 +207,10 @@
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -225,7 +225,7 @@
 
     .detailed-status__meta {
       margin-top: 15px;
-      color: $ui-primary-color;
+      color: $lighter-text-color;
       font-size: 14px;
       line-height: 18px;
 
@@ -243,7 +243,7 @@
 
     .status-card {
       border-color: lighten($ui-secondary-color, 4%);
-      color: darken($ui-primary-color, 4%);
+      color: $lighter-text-color;
 
       &:hover {
         background: lighten($ui-secondary-color, 4%);
@@ -252,7 +252,7 @@
 
     .status-card__title,
     .status-card__description {
-      color: $ui-base-color;
+      color: $inverted-text-color;
     }
 
     .status-card__image {
@@ -262,7 +262,7 @@
 
   .media-spoiler {
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
   }
 
   .pre-header {
@@ -270,7 +270,7 @@
     padding-left: (48px + 14px * 2);
     padding-bottom: 0;
     margin-bottom: -4px;
-    color: $ui-primary-color;
+    color: $lighter-text-color;
     font-size: 14px;
     position: relative;
 
@@ -280,7 +280,7 @@
     }
 
     .status__display-name.muted strong {
-      color: $ui-primary-color;
+      color: $lighter-text-color;
     }
   }
 
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index 92870e679..c12d84f1c 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -30,7 +30,7 @@
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-decoration: underline;
 
     &:hover {
@@ -68,7 +68,7 @@ a.table-action-link {
   display: inline-block;
   margin-right: 5px;
   padding: 0 10px;
-  color: rgba($primary-text-color, 0.7);
+  color: $darker-text-color;
   font-weight: 500;
 
   &:hover {
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index e456c27ee..dc4e72a2e 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -18,6 +18,11 @@ $base-overlay-background: $black !default;
 $base-border-color: $white !default;
 $simple-background-color: $white !default;
 $primary-text-color: $white !default;
+$darker-text-color: rgba($primary-text-color, 0.7) !default;
+$highlight-text-color: $classic-highlight-color !default;
+$inverted-text-color: $black !default;
+$lighter-text-color: rgba($inverted-text-color, 0.7) !default;
+$action-button-color: #8d9ac2;
 $valid-value-color: $success-green !default;
 $error-value-color: $error-red !default;
 
@@ -26,7 +31,7 @@ $ui-base-color: $classic-base-color !default;                  // Darkest
 $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
 $ui-primary-color: $classic-primary-color !default;            // Lighter
 $ui-secondary-color: $classic-secondary-color !default;        // Lightest
-$ui-highlight-color: $classic-highlight-color !default;        // Vibrant
+$ui-highlight-color: #2b5fd9;
 
 // Language codes that uses CJK fonts
 $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index f19b04ae6..e880499f1 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -19,6 +19,9 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
         'Emoji'                     => 'toot:Emoji',
         'focalPoint'                => { '@container' => '@list', '@id' => 'toot:focalPoint' },
         'featured'                  => 'toot:featured',
+        'schema'                    => 'http://schema.org#',
+        'PropertyValue'             => 'schema:PropertyValue',
+        'value'                     => 'schema:value',
       },
     ],
   }.freeze
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index f7e7a3c23..4124f1660 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -71,6 +71,11 @@ class Formatter
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
+  def format_field(account, str)
+    return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
+    encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
+  end
+
   def linkify(text)
     html = encode_and_link_urls(text)
     html = simple_format(html, {}, sanitize: false)
@@ -85,12 +90,17 @@ class Formatter
     HTMLEntities.new.encode(html)
   end
 
-  def encode_and_link_urls(html, accounts = nil)
+  def encode_and_link_urls(html, accounts = nil, options = {})
     entities = Extractor.extract_entities_with_indices(html, extract_url_without_protocol: false)
 
+    if accounts.is_a?(Hash)
+      options  = accounts
+      accounts = nil
+    end
+
     rewrite(html.dup, entities) do |entity|
       if entity[:url]
-        link_to_url(entity)
+        link_to_url(entity, options)
       elsif entity[:hashtag]
         link_to_hashtag(entity)
       elsif entity[:screen_name]
@@ -177,10 +187,12 @@ class Formatter
     result.flatten.join
   end
 
-  def link_to_url(entity)
+  def link_to_url(entity, options = {})
     url        = Addressable::URI.parse(entity[:url])
     html_attrs = { target: '_blank', rel: 'nofollow noopener' }
 
+    html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
+
     Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs)
   rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
     encode(entity[:url])
diff --git a/app/models/account.rb b/app/models/account.rb
index dd8bad585..db2171102 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -44,6 +44,7 @@
 #  memorial                :boolean          default(FALSE), not null
 #  moved_to_account_id     :integer
 #  featured_collection_url :string
+#  fields                  :jsonb
 #
 
 class Account < ApplicationRecord
@@ -116,7 +117,7 @@ class Account < ApplicationRecord
   scope :without_followers, -> { where(followers_count: 0) }
   scope :with_followers, -> { where('followers_count > 0') }
   scope :expiring, ->(time) { remote.where.not(subscription_expires_at: nil).where('subscription_expires_at < ?', time) }
-  scope :partitioned, -> { order('row_number() over (partition by domain)') }
+  scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
   scope :silenced, -> { where(silenced: true) }
   scope :suspended, -> { where(suspended: true) }
   scope :recent, -> { reorder(id: :desc) }
@@ -192,6 +193,30 @@ class Account < ApplicationRecord
     @keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
   end
 
+  def fields
+    (self[:fields] || []).map { |f| Field.new(self, f) }
+  end
+
+  def fields_attributes=(attributes)
+    fields = []
+
+    attributes.each_value do |attr|
+      next if attr[:name].blank?
+      fields << attr
+    end
+
+    self[:fields] = fields
+  end
+
+  def build_fields
+    return if fields.size >= 4
+
+    raw_fields = self[:fields] || []
+    add_fields = 4 - raw_fields.size
+    add_fields.times { raw_fields << { name: '', value: '' } }
+    self.fields = raw_fields
+  end
+
   def magic_key
     modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component|
       result = []
@@ -241,6 +266,17 @@ class Account < ApplicationRecord
     shared_inbox_url.presence || inbox_url
   end
 
+  class Field < ActiveModelSerializers::Model
+    attributes :name, :value, :account, :errors
+
+    def initialize(account, attr)
+      @account = account
+      @name    = attr['name']
+      @value   = attr['value']
+      @errors  = {}
+    end
+  end
+
   class << self
     def readonly_attributes
       super - %w(statuses_count following_count followers_count)
diff --git a/app/models/status.rb b/app/models/status.rb
index 5d309546f..952661169 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -24,6 +24,7 @@
 #  application_id         :integer
 #  in_reply_to_account_id :integer
 #  local_only             :boolean
+#  full_status_text       :text             default(""), not null
 #
 
 class Status < ApplicationRecord
diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb
index 369ede2b0..307876856 100644
--- a/app/policies/status_policy.rb
+++ b/app/policies/status_policy.rb
@@ -18,7 +18,7 @@ class StatusPolicy < ApplicationPolicy
   end
 
   def reblog?
-    !direct? && !private? && show?
+    !direct? && (!private? || owned?) && show?
   end
 
   def destroy?
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index df3090726..fcf3bdf17 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -11,6 +11,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
   has_one :public_key, serializer: ActivityPub::PublicKeySerializer
 
   has_many :virtual_tags, key: :tag
+  has_many :virtual_attachments, key: :attachment
 
   attribute :moved_to, if: :moved?
 
@@ -107,10 +108,26 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
     object.emojis
   end
 
+  def virtual_attachments
+    object.fields
+  end
+
   def moved_to
     ActivityPub::TagManager.instance.uri_for(object.moved_to_account)
   end
 
   class CustomEmojiSerializer < ActivityPub::EmojiSerializer
   end
+
+  class Account::FieldSerializer < ActiveModel::Serializer
+    attributes :type, :name, :value
+
+    def type
+      'PropertyValue'
+    end
+
+    def value
+      Formatter.instance.format_field(object.account, object.value)
+    end
+  end
 end
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 6097acda5..863238eb7 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -9,6 +9,16 @@ class REST::AccountSerializer < ActiveModel::Serializer
 
   has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
 
+  class FieldSerializer < ActiveModel::Serializer
+    attributes :name, :value
+
+    def value
+      Formatter.instance.format_field(object.account, object.value)
+    end
+  end
+
+  has_many :fields
+
   def id
     object.id.to_s
   end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 21c2fc57a..da32f9615 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -22,13 +22,15 @@ class ActivityPub::ProcessAccountService < BaseService
 
         create_account if @account.nil?
         update_account
-        process_tags(@account)
+        process_tags
       end
     end
 
+    return if @account.nil?
+
     after_protocol_change! if protocol_changed?
     after_key_change! if key_changed?
-    check_featured_collection! if @account&.featured_collection_url&.present?
+    check_featured_collection! if @account.featured_collection_url.present?
 
     @account
   rescue Oj::ParseError
@@ -68,6 +70,7 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.display_name            = @json['name'] || ''
     @account.note                    = @json['summary'] || ''
     @account.locked                  = @json['manuallyApprovesFollowers'] || false
+    @account.fields                  = property_values || {}
   end
 
   def set_fetchable_attributes!
@@ -124,6 +127,11 @@ class ActivityPub::ProcessAccountService < BaseService
     end
   end
 
+  def property_values
+    return unless @json['attachment'].is_a?(Array)
+    @json['attachment'].select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
+  end
+
   def mismatching_origin?(url)
     needle   = Addressable::URI.parse(url).host
     haystack = Addressable::URI.parse(@uri).host
@@ -189,17 +197,18 @@ class ActivityPub::ProcessAccountService < BaseService
     { redis: Redis.current, key: "process_account:#{@uri}" }
   end
 
-  def process_tags(account)
+  def process_tags
     return if @json['tag'].blank?
+
     as_array(@json['tag']).each do |tag|
       case tag['type']
       when 'Emoji'
-        process_emoji tag, account
+        process_emoji tag
       end
     end
   end
 
-  def process_emoji(tag, _account)
+  def process_emoji(tag)
     return if skip_download?
     return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?
 
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index 8e285e1f7..dc8df4a9a 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -17,13 +17,11 @@ class ProcessMentionsService < BaseService
       if mention_undeliverable?(status, mentioned_account)
         begin
           mentioned_account = resolve_account_service.call($1)
-        rescue Goldfinger::Error, HTTP::Error
+        rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError
           mentioned_account = nil
         end
       end
 
-      mentioned_account ||= Account.find_remote(username, domain)
-
       next match if mention_undeliverable?(status, mentioned_account)
 
       mentioned_account.mentions.where(status: status).first_or_create(status: status)
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 00a8b3dd7..5bb395942 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -4,7 +4,7 @@ class SearchService < BaseService
   attr_accessor :query, :account, :limit, :resolve
 
   def call(query, limit, resolve = false, account = nil)
-    @query   = query
+    @query   = query.strip
     @account = account
     @limit   = limit
     @resolve = resolve
diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb
index 64da04120..2c7bce674 100644
--- a/app/validators/status_pin_validator.rb
+++ b/app/validators/status_pin_validator.rb
@@ -5,6 +5,6 @@ class StatusPinValidator < ActiveModel::Validator
     pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
     pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
     pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
-    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4 && pin.account.local?
   end
 end
diff --git a/app/views/about/_administration.html.haml b/app/views/about/_administration.html.haml
new file mode 100644
index 000000000..ec5834f9c
--- /dev/null
+++ b/app/views/about/_administration.html.haml
@@ -0,0 +1,19 @@
+.account
+  .account__wrapper
+    - if @instance_presenter.contact_account
+      = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do
+        .account__avatar-wrapper
+          .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" }
+        %span.display-name
+          %bdi
+            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account)
+          %span.display-name__account @#{@instance_presenter.contact_account.acct}
+    - else
+      .account__display-name
+        .account__avatar-wrapper
+          .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" }
+        %span.display-name
+          %strong= t 'about.contact_missing'
+          %span.display-name__account= t 'about.contact_unavailable'
+
+    = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative'
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 674771cbc..e264c8574 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -109,26 +109,7 @@
                 %p= t 'about.about_mastodon_html'
               %div.contact
                 %h3= t 'about.administered_by'
-
-                .account
-                  .account__wrapper
-                    - if @instance_presenter.contact_account
-                      = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do
-                        .account__avatar-wrapper
-                          .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" }
-                        %span.display-name
-                          %bdi
-                            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account)
-                          %span.display-name__account @#{@instance_presenter.contact_account.acct}
-                    - else
-                      .account__display-name
-                        .account__avatar-wrapper
-                          .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" }
-                        %span.display-name
-                          %strong= t 'about.contact_missing'
-                          %span.display-name__account= t 'about.contact_unavailable'
-
-                    = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative'
+                = render 'administration'
 
             = render 'features'
 
@@ -143,8 +124,13 @@
       - else
         .column-4.non-preview.landing-page__information
           .landing-page__features
-            %h3= t 'about.what_is_mastodon'
-            %p= t 'about.about_mastodon_html'
+            .features-list
+              %div
+                %h3= t 'about.what_is_mastodon'
+                %p= t 'about.about_mastodon_html'
+              %div.contact
+                %h3= t 'about.administered_by'
+                = render 'administration'
 
             = render 'features'
 
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 73af27a4c..76f29d591 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -7,8 +7,8 @@
   .card__bio
     %h1.name
       %span.p-name.emojify= display_name(account)
-      %small
-        %span @#{account.local_username_and_domain}
+      %small<
+        %span>< @#{account.local_username_and_domain}
         = fa_icon('lock') if account.locked?
     - if Setting.show_staff_badge
       - if account.user_admin?
@@ -28,6 +28,14 @@
               %th.emojify>!=i[0]
               %td.emojify>!=i[1]
 
+      - unless account.fields.empty?
+        %table.account__header__fields
+          %tbody
+            - account.fields.each do |field|
+              %tr
+                %th.emojify= field.name
+                %td.emojify= Formatter.instance.format_field(account, field.value)
+
     .details-counters
       .counter{ class: active_nav_class(short_account_url(account)) }
         = link_to short_account_url(account), class: 'u-url u-uid' do
diff --git a/app/views/admin/accounts/_card.html.haml b/app/views/admin/accounts/_card.html.haml
deleted file mode 100644
index 2f5955011..000000000
--- a/app/views/admin/accounts/_card.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-.table-wrapper
-  %table.table
-    %tbody
-      %tr
-        %td= t('admin.accounts.show.created_reports')
-        %td= link_to pluralize(account.reports.count, t('admin.accounts.show.report')), admin_reports_path(account_id: account.id)
-      %tr
-        %td= t('admin.accounts.show.targeted_reports')
-        %td= link_to pluralize(account.targeted_reports.count, t('admin.accounts.show.report')), admin_reports_path(target_account_id: account.id)
-      - if account.silenced? || account.suspended?
-        %tr
-          %td= t('admin.accounts.moderation.title')
-          %td
-            - if account.silenced?
-              %p= t('admin.accounts.moderation.silenced')
-            - if account.suspended?
-              %p= t('admin.accounts.moderation.suspended')
diff --git a/app/views/admin/reports/_account_details.html.haml b/app/views/admin/reports/_account_details.html.haml
new file mode 100644
index 000000000..a8af39bef
--- /dev/null
+++ b/app/views/admin/reports/_account_details.html.haml
@@ -0,0 +1,20 @@
+.table-wrapper
+  %table.table
+    %tbody
+      %tr
+        %td= t('admin.reports.account.created_reports')
+        %td= link_to pluralize(account.reports.count, t('admin.reports.account.report')), admin_reports_path(account_id: account.id)
+      %tr
+        %td= t('admin.reports.account.targeted_reports')
+        %td= link_to pluralize(account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: account.id)
+      %tr
+        %td= t('admin.reports.account.moderation_notes')
+        %td= link_to pluralize(account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: account.id)
+      - if account.silenced? || account.suspended?
+        %tr
+          %td= t('admin.reports.account.moderation.title')
+          %td
+            - if account.silenced?
+              %p= t('admin.reports.account.moderation.silenced')
+            - if account.suspended?
+              %p= t('admin.reports.account.moderation.suspended')
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index a0c1ca283..60a8cab8e 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -57,11 +57,11 @@
   .report-accounts__item
     %h3= t('admin.reports.reported_account')
     = render 'authorize_follows/card', account: @report.target_account, admin: true
-    = render 'admin/accounts/card', account: @report.target_account
+    = render 'admin/reports/account_details', account: @report.target_account
   .report-accounts__item
     %h3= t('admin.reports.reported_by')
     = render 'authorize_follows/card', account: @report.account, admin: true
-    = render 'admin/accounts/card', account: @report.account
+    = render 'admin/reports/account_details', account: @report.account
 
 %h3= t('admin.reports.comment.label')
 
diff --git a/app/views/invites/_invite.html.haml b/app/views/invites/_invite.html.haml
index 81d67eb7d..1c7ec311d 100644
--- a/app/views/invites/_invite.html.haml
+++ b/app/views/invites/_invite.html.haml
@@ -13,5 +13,5 @@
           = l invite.expires_at
   %td= table_link_to 'link', public_invite_url(invite_code: invite.code), public_invite_url(invite_code: invite.code)
   %td
-    - if invite.expired? && policy(invite).destroy?
+    - if !invite.expired? && policy(invite).destroy?
       = table_link_to 'times', t('invites.delete'), invite_path(invite), method: :delete
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index be7bd0ba0..5f63466d9 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -19,6 +19,16 @@
   .fields-group
     = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
 
+  .fields-group
+    .input.with_block_label
+      %label= t('simple_form.labels.defaults.fields')
+      %span.hint= t('simple_form.hints.defaults.fields')
+
+      = f.simple_fields_for :fields do |fields_f|
+        .row
+          = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name')
+          = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value')
+
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
 
diff --git a/app/workers/activitypub/synchronize_featured_collection_worker.rb b/app/workers/activitypub/synchronize_featured_collection_worker.rb
index dd676a3ee..7b16d3426 100644
--- a/app/workers/activitypub/synchronize_featured_collection_worker.rb
+++ b/app/workers/activitypub/synchronize_featured_collection_worker.rb
@@ -3,7 +3,7 @@
 class ActivityPub::SynchronizeFeaturedCollectionWorker
   include Sidekiq::Worker
 
-  sidekiq_options queue: 'pull'
+  sidekiq_options queue: 'pull', unique: :until_executed
 
   def perform(account_id)
     ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id))