diff --git a/external/lang b/external/lang
index 24766f8f5c2147ba866af922e3f5f42ad3ea44e4..2a9ef2c8025dd71a20fddca7c94cb7af30978c4b 160000
--- a/external/lang
+++ b/external/lang
@@ -1 +1 @@
-Subproject commit 24766f8f5c2147ba866af922e3f5f42ad3ea44e4
+Subproject commit 2a9ef2c8025dd71a20fddca7c94cb7af30978c4b
diff --git a/package.json b/package.json
index 595b73b7acdd6baa674907de7726b50d192c0015..2b7932669e2c6a2dc4bf0715601f016afa203a9e 100644
--- a/package.json
+++ b/package.json
@@ -25,8 +25,23 @@
     "preact": "^10.5.13"
   },
   "devDependencies": {
-    "@fontsource/fira-mono": "^4.4.5",
+    "@fontsource/atkinson-hyperlegible": "^4.4.5",
+    "@fontsource/bree-serif": "^4.4.5",
+    "@fontsource/comic-neue": "^4.4.5",
+    "@fontsource/fira-code": "^4.4.5",
+    "@fontsource/inter": "^4.4.5",
+    "@fontsource/lato": "^4.4.5",
+    "@fontsource/montserrat": "^4.4.5",
+    "@fontsource/noto-sans": "^4.4.5",
     "@fontsource/open-sans": "^4.4.5",
+    "@fontsource/poppins": "^4.4.5",
+    "@fontsource/raleway": "^4.4.5",
+    "@fontsource/roboto": "^4.4.5",
+    "@fontsource/roboto-mono": "^4.4.5",
+    "@fontsource/source-code-pro": "^4.4.5",
+    "@fontsource/space-mono": "^4.4.5",
+    "@fontsource/ubuntu": "^4.4.5",
+    "@fontsource/ubuntu-mono": "^4.4.5",
     "@hcaptcha/react-hcaptcha": "^0.3.6",
     "@preact/preset-vite": "^2.0.0",
     "@rollup/plugin-replace": "^2.4.2",
@@ -86,7 +101,7 @@
     "typescript": "^4.3.2",
     "ulid": "^2.3.0",
     "use-resize-observer": "^7.0.0",
-    "vite": "^2.3.7",
+    "vite": "npm:@insertish/vite@2.2.4-dynamic-import-css-f428476",
     "vite-plugin-pwa": "^0.8.1",
     "workbox-precaching": "^6.1.5"
   }
diff --git a/src/components/common/CollapsibleSection.tsx b/src/components/common/CollapsibleSection.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3b6409fb9a32b916f697110693ea57b5890e9302
--- /dev/null
+++ b/src/components/common/CollapsibleSection.tsx
@@ -0,0 +1,50 @@
+import Details from "../ui/Details";
+import { State, store } from "../../redux";
+import { Action } from "../../redux/reducers";
+import { Children } from "../../types/Preact";
+import { ChevronDown } from "@styled-icons/boxicons-regular";
+
+interface Props {
+    id: string;
+    defaultValue: boolean;
+
+    sticky?: boolean;
+    large?: boolean;
+
+    summary: Children;
+    children: Children;
+}
+
+export default function CollapsibleSection({ id, defaultValue, summary, children, ...detailsProps }: Props) {
+    const state: State = store.getState();
+
+    function setState(state: boolean) {
+        if (state === defaultValue) {
+            store.dispatch({
+                type: 'SECTION_TOGGLE_UNSET',
+                id
+            } as Action);
+        } else {
+            store.dispatch({
+                type: 'SECTION_TOGGLE_SET',
+                id,
+                state
+            } as Action);
+        }
+    }
+
+    return (
+        <Details
+            open={state.sectionToggle[id] ?? defaultValue}
+            onToggle={e => setState(e.currentTarget.open)}
+            {...detailsProps}>
+            <summary>
+                <div class="padding">
+                    <ChevronDown size={20} />
+                    { summary }
+                </div>
+            </summary>
+            { children }
+        </Details>
+    )
+}
diff --git a/src/components/common/Tooltip.tsx b/src/components/common/Tooltip.tsx
index c8497e4096a46ae6dc7c871257b2b9e12cb72096..7a9957f4cc6355bf0d4a34d1f7ab3f4f8e35c6c3 100644
--- a/src/components/common/Tooltip.tsx
+++ b/src/components/common/Tooltip.tsx
@@ -24,14 +24,16 @@ const PermissionTooltipBase = styled.div`
     display: flex;
     align-items: center;
     flex-direction: column;
+    
     span {
         font-weight: 700;
         text-transform: uppercase;
         color: var(--secondary-foreground);
         font-size: 11px;
     }
+
     code {
-        font-family: 'Fira Mono';
+        font-family: var(--monoscape-font);
     }
 `;
 
diff --git a/src/components/common/messaging/attachments/Attachment.module.scss b/src/components/common/messaging/attachments/Attachment.module.scss
index 4f23a898a2e9ebaeef9df8f41c2e9b160decb941..5aa89744d83a997be557862d27105019ed2cfaf2 100644
--- a/src/components/common/messaging/attachments/Attachment.module.scss
+++ b/src/components/common/messaging/attachments/Attachment.module.scss
@@ -2,6 +2,13 @@
     border-radius: 6px;
     margin: .125rem 0 .125rem;
     
+    height: auto;
+    
+    max-height: 640px;
+    max-width: min(480px, 100%);
+    
+    object-fit: contain;
+    
     &[data-spoiler="true"] {
         filter: blur(30px);
         pointer-events: none;
@@ -71,7 +78,7 @@
             }
     
             pre code {
-                font-family: "Fira Mono", sans-serif;
+                font-family: var(--monoscape-font), sans-serif;
             }
 
             &[data-loading="true"] {
diff --git a/src/components/common/messaging/attachments/Attachment.tsx b/src/components/common/messaging/attachments/Attachment.tsx
index eb144ebee6813d21426d49a28a7065899cfe002f..15c4253658859c3a910e4c3407385be13e8a7036 100644
--- a/src/components/common/messaging/attachments/Attachment.tsx
+++ b/src/components/common/messaging/attachments/Attachment.tsx
@@ -15,60 +15,33 @@ interface Props {
 }
 
 const MAX_ATTACHMENT_WIDTH = 480;
-const MAX_ATTACHMENT_HEIGHT = 640;
 
 export default function Attachment({ attachment, hasContent }: Props) {
     const client = useContext(AppContext);
     const { openScreen } = useIntermediate();
     const { filename, metadata } = attachment;
     const [ spoiler, setSpoiler ] = useState(filename.startsWith("SPOILER_"));
-    const maxWidth = Math.min(useContext(MessageAreaWidthContext), MAX_ATTACHMENT_WIDTH);
 
     const url = client.generateFileURL(attachment, { width: MAX_ATTACHMENT_WIDTH * 1.5 }, true);
-    let width  = 0,
-        height = 0;
-    
-    if (metadata.type === 'Image' || metadata.type === 'Video') {
-        let limitingWidth = Math.min(
-            maxWidth,
-            metadata.width
-        );
 
-        let limitingHeight = Math.min(
-            MAX_ATTACHMENT_HEIGHT,
-            metadata.height
-        );
-
-        // Calculate smallest possible WxH.
-        width = Math.min(
-            limitingWidth,
-            limitingHeight * (metadata.width / metadata.height)
-        );
-
-        height = Math.min(
-            limitingHeight,
-            limitingWidth * (metadata.height / metadata.width)
-        );
-    }
 
     switch (metadata.type) {
         case "Image": {
             return (
                 <div
-                    style={{ width }}
                     className={styles.container}
                     onClick={() => spoiler && setSpoiler(false)}
                 >
                     {spoiler && (
                         <div className={styles.overflow}>
-                            <div style={{ width, height }}>
-                                <span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
-                            </div>
+                            <span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
                         </div>
                     )}
                     <img
                         src={url}
                         alt={filename}
+                        width={metadata.width}
+                        height={metadata.height}
                         data-spoiler={spoiler}
                         data-has-content={hasContent}
                         className={classNames(styles.attachment, styles.image)}
@@ -79,7 +52,6 @@ export default function Attachment({ attachment, hasContent }: Props) {
                             ev.button === 1 &&
                             window.open(url, "_blank")
                         }
-                        style={{ width, height }}
                     />
                 </div>
             );
@@ -102,13 +74,10 @@ export default function Attachment({ attachment, hasContent }: Props) {
                     onClick={() => spoiler && setSpoiler(false)}>
                     {spoiler && (
                         <div className={styles.overflow}>
-                            <div style={{ width, height }}>
-                                <span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
-                            </div>
+                            <span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
                         </div>
                     )}
                     <div
-                        style={{ width }}
                         data-spoiler={spoiler}
                         data-has-content={hasContent}
                         className={classNames(styles.attachment, styles.video)}
@@ -117,7 +86,6 @@ export default function Attachment({ attachment, hasContent }: Props) {
                         <video
                             src={url}
                             controls
-                            style={{ width, height }}
                             onMouseDown={ev =>
                                 ev.button === 1 &&
                                 window.open(url, "_blank")
diff --git a/src/components/common/messaging/bars/FilePreview.tsx b/src/components/common/messaging/bars/FilePreview.tsx
index 9f0a98c4c41e7e812e0c9c4c6cad868df8e505f1..80f9fae594cb8f04aad16e8a1b42710b61322e23 100644
--- a/src/components/common/messaging/bars/FilePreview.tsx
+++ b/src/components/common/messaging/bars/FilePreview.tsx
@@ -101,6 +101,7 @@ const PreviewBox = styled.div`
 
     .icon {
         height: 100px;
+        width: 100%;
         margin-bottom: 4px;
         object-fit: contain;
     }
diff --git a/src/components/markdown/Markdown.module.scss b/src/components/markdown/Markdown.module.scss
index de439a3482daa4d065c74cf02744edb2f2700766..202ce12edde7c4561ce07162fa31584fb698e235 100644
--- a/src/components/markdown/Markdown.module.scss
+++ b/src/components/markdown/Markdown.module.scss
@@ -1,5 +1,3 @@
-@import "@fontsource/fira-mono/400.css";
-
 .markdown {
     :global(.emoji) {
         height: 1.25em;
@@ -89,7 +87,7 @@
         font-size: 90%;
         border-radius: 4px;
         background: var(--block);
-        font-family: "Fira Mono", monospace;
+        font-family: var(--monoscape-font), monospace;
     }
 
     input[type="checkbox"] {
@@ -136,7 +134,7 @@
     }
 
     :global(.code) {
-        font-family: "Fira Mono", monospace;
+        font-family: var(--monoscape-font), monospace;
 
         :global(.lang) {
             // height: 8px;
diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx
index cd98bc7e6b7cbcd209118edbfe97129e37206a5d..16e586bbcfe6f93e451479596ac3a81faad160e0 100644
--- a/src/components/navigation/left/HomeSidebar.tsx
+++ b/src/components/navigation/left/HomeSidebar.tsx
@@ -1,4 +1,4 @@
-import { Localizer, Text } from "preact-i18n";
+import { Text } from "preact-i18n";
 import { useContext, useEffect } from "preact/hooks";
 import { Home, UserDetail, Wrench, Notepad } from "@styled-icons/boxicons-solid";
 
@@ -105,13 +105,9 @@ function HomeSidebar(props: Props) {
                         </ButtonItem>
                     </Link>
                 )}
-                <Localizer>
-                    <Category
-                        text={<Text id="app.main.categories.conversations" />}
-                        /** @ts-ignore : ignored due to conflicting naming between the Category property name and the existing JSX attribute */
-                        action={() => openScreen({ id: "special_input", type: "create_group" })}
-                    />
-                </Localizer>
+                <Category
+                    text={<Text id="app.main.categories.conversations" />}
+                    action={() => openScreen({ id: "special_input", type: "create_group" })} />
                 {channelsArr.length === 0 && <img src={placeholderSVG} />}
                 {channelsArr.map(x => {
                     let user;
diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx
index 49e5795951f632cb783c283744640de2dc4ec939..3dd7aff9d37dddba1ef063fa6414b0978d4d8e27 100644
--- a/src/components/navigation/left/ServerSidebar.tsx
+++ b/src/components/navigation/left/ServerSidebar.tsx
@@ -14,6 +14,7 @@ import ServerHeader from "../../common/ServerHeader";
 import { useEffect } from "preact/hooks";
 import Category from "../../ui/Category";
 import ConditionalLink from "../../../lib/ConditionalLink";
+import CollapsibleSection from "../../common/CollapsibleSection";
 
 interface Props {
     unreads: Unreads;
@@ -69,6 +70,7 @@ function ServerSidebar(props: Props & WithDispatcher) {
 
     let uncategorised = new Set(server.channels);
     let elements = [];
+    
     function addChannel(id: string) {
         const entry = channels.find(x => x._id === id);
         if (!entry) return;
@@ -76,9 +78,8 @@ function ServerSidebar(props: Props & WithDispatcher) {
         const active = channel?._id === entry._id;
 
         return (
-            <ConditionalLink active={active} to={`/server/${server!._id}/channel/${entry._id}`}>
+            <ConditionalLink key={entry._id} active={active} to={`/server/${server!._id}/channel/${entry._id}`}>
                 <ChannelButton
-                    key={entry._id}
                     channel={entry}
                     active={active}
                     alert={entry.unread}
@@ -90,16 +91,24 @@ function ServerSidebar(props: Props & WithDispatcher) {
 
     if (server.categories) {
         for (let category of server.categories) {
-            elements.push(<Category text={category.title} />);
-
+            let channels = [];
             for (let id of category.channels) {
                 uncategorised.delete(id);
-                elements.push(addChannel(id));
+                channels.push(addChannel(id));
             }
+
+            elements.push(
+                <CollapsibleSection
+                    id={`category_${category.id}`}
+                    defaultValue
+                    summary={<Category text={category.title} />}>
+                    { channels }
+                </CollapsibleSection>
+            );
         }
     }
 
-    for (let id of uncategorised) {
+    for (let id of Array.from(uncategorised).reverse()) {
         elements.unshift(addChannel(id));
     }
 
diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx
index c5bcaf677cb29d47d405246af34b729109cd0e05..03287b15375b39de5919faf944c1111308b7e25b 100644
--- a/src/components/navigation/right/MemberSidebar.tsx
+++ b/src/components/navigation/right/MemberSidebar.tsx
@@ -2,6 +2,7 @@ import { Text } from "preact-i18n";
 import { useContext, useEffect, useState } from "preact/hooks";
 
 import { User } from "revolt.js";
+import Details from "../../../components/ui/Details";
 import Category from "../../ui/Category";
 import { useParams } from "react-router";
 import { UserButton } from "../items/ButtonItem";
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
index 8582a19370252b93174386135a652c4b66a6e9ff..870e7ed22a463a87be437f63f97039f21925c89d 100644
--- a/src/components/ui/Button.tsx
+++ b/src/components/ui/Button.tsx
@@ -10,7 +10,7 @@ export default styled.button<Props>`
     padding: 8px;
     font-size: 16px;
     text-align: center;
-    font-family: 'Open Sans', sans-serif;
+    font-family: inherit;
 
     transition: 0.2s ease opacity;
     transition: 0.2s ease background-color;
diff --git a/src/components/ui/Category.tsx b/src/components/ui/Category.tsx
index 79f1d0bc33de6a025ba65ce260ee39ebf554bbc1..38980b19f5906a4db78c276c78ae32c2a08057cb 100644
--- a/src/components/ui/Category.tsx
+++ b/src/components/ui/Category.tsx
@@ -31,7 +31,7 @@ const CategoryBase = styled.div<Pick<Props, 'variant'>>`
     ` }
 `;
 
-type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & {
+type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as' | 'action'> & {
     text: Children;
     // TODO: rename from action to prevent type conflicts with the dom
     action?: () => void;
diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx
index bc87c5cc98ac323fc4609c5dd3f62363844a6540..7fad94d8adccb67348effc20a8f7867804b797ab 100644
--- a/src/components/ui/Checkbox.tsx
+++ b/src/components/ui/Checkbox.tsx
@@ -3,9 +3,9 @@ import { Children } from "../../types/Preact";
 import styled, { css } from "styled-components";
 
 const CheckboxBase = styled.label`
+    margin-top: 20px;
     gap: 4px;
     z-index: 1;
-    padding: 4px;
     display: flex;
     border-radius: 4px;
     align-items: center;
@@ -16,25 +16,19 @@ const CheckboxBase = styled.label`
 
     transition: 0.2s ease all;
 
-    p {
-        margin: 0;
-    }
-
     input {
         display: none;
     }
 
     &:hover {
-        background: var(--secondary-background);
-
         .check {
             background: var(--background);
         }
     }
 
     &[disabled] {
-        opacity: 0.5;
-        cursor: unset;
+        opacity: .5;
+        cursor: not-allowed;
 
         &:hover {
             background: unset;
@@ -43,15 +37,15 @@ const CheckboxBase = styled.label`
 `;
 
 const CheckboxContent = styled.span`
-    flex-grow: 1;
     display: flex;
+    flex-grow: 1;
     font-size: 1rem;
     font-weight: 600;
     flex-direction: column;
 `;
 
 const CheckboxDescription = styled.span`
-    font-size: 0.8em;
+    font-size: .75rem;
     font-weight: 400;
     color: var(--secondary-foreground);
 `;
diff --git a/src/components/ui/ColourSwatches.tsx b/src/components/ui/ColourSwatches.tsx
index cbc13bd284f1d4c12b9b6b58573c061a21cd455a..46a473fc80ca6077b82daf219088d95fad4b49b1 100644
--- a/src/components/ui/ColourSwatches.tsx
+++ b/src/components/ui/ColourSwatches.tsx
@@ -1,5 +1,6 @@
 import { useRef } from "preact/hooks";
-import { Check, Pencil } from "@styled-icons/boxicons-regular";
+import { Check } from "@styled-icons/boxicons-regular";
+import { Palette } from "@styled-icons/boxicons-solid";
 import styled, { css } from "styled-components";
 
 interface Props {
@@ -98,7 +99,7 @@ export default function ColourSwatches({ value, onChange }: Props) {
                 type="large"
                 onClick={() => ref.current.click()}
             >
-                <Pencil size={32} />
+                <Palette size={32} />
             </Swatch>
             <input
                 type="color"
diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx
index ef9ba9f91c0045cf077ffab655b46077cad7fc26..989f0787b351c5b9e8c632494956de7e14ade81f 100644
--- a/src/components/ui/ComboBox.tsx
+++ b/src/components/ui/ComboBox.tsx
@@ -2,15 +2,19 @@ import styled from "styled-components";
 
 export default styled.select`
     padding: 8px;
-    border-radius: 2px;
+    border-radius: 6px;
+    font-family: inherit;
     color: var(--secondary-foreground);
     background: var(--secondary-background);
-
+    font-size: .875rem;
     border: none;
     outline: 2px solid transparent;
     transition: outline-color 0.2s ease-in-out;
+    transition: box-shadow .3s;
+    cursor: pointer;
+    width: 100%;
 
     &:focus {
-        outline-color: var(--accent);
+        box-shadow: 0 0 0 2pt var(--accent);
     }
 `;
diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f06a67f70a3604faf69a2586d57e35d486d0916c
--- /dev/null
+++ b/src/components/ui/Details.tsx
@@ -0,0 +1,68 @@
+import styled, { css } from "styled-components";
+
+export default styled.details<{ sticky?: boolean, large?: boolean }>`
+    summary {
+        ${ props => props.sticky && css`
+            top: -1px;
+            z-index: 10;
+            position: sticky;
+        ` }
+
+        ${ props => props.large && css`
+            /*padding: 5px 0;*/
+            background: var(--primary-background);
+            color: var(--secondary-foreground);
+
+            .padding { /*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/
+                display: flex;
+                align-items: center;
+                padding: 5px 0;
+                margin: 0.8em 0px 0.4em;
+                cursor: pointer;
+            }
+        ` }
+
+        outline: none;
+        cursor: pointer;
+        list-style: none;
+        align-items: center;
+        transition: .2s opacity;
+        
+        font-size: 12px;
+        font-weight: 600;
+        text-transform: uppercase;
+
+        &::marker, &::-webkit-details-marker {
+            display: none;
+        }
+
+        .title {
+            flex-grow: 1;
+            margin-top: 1px;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: nowrap;
+        }
+
+        .padding { 
+            display: flex;
+            align-items: center;
+
+            > svg {
+                flex-shrink: 0;
+                margin-inline-end: 4px;
+                transition: .2s ease transform;
+            }
+        }
+    }
+
+    &:not([open]) {
+        summary {
+            opacity: .7;
+        }
+        
+        summary svg {
+            transform: rotateZ(-90deg);
+        }
+    }
+`;
diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx
index 376523970ad399b2ee268c42083e2c4b0b5d710c..d4e0e3f90f605d2857f5c10b2fe1affc81cc3857 100644
--- a/src/components/ui/Header.tsx
+++ b/src/components/ui/Header.tsx
@@ -46,6 +46,6 @@ export default styled.div<Props>`
     ` }
 
     ${ props => props.borders && css`    
-        border-end-start-radius: 8px;
+        border-start-start-radius: 8px;
     ` }
 `;
diff --git a/src/components/ui/InputBox.tsx b/src/components/ui/InputBox.tsx
index ac3746468d64539919f3ed1b1f77783bb4c62631..75e3510b9c822161457f4dbfd7cf84019adec716 100644
--- a/src/components/ui/InputBox.tsx
+++ b/src/components/ui/InputBox.tsx
@@ -9,6 +9,7 @@ export default styled.input<Props>`
     padding: 8px 16px;
     border-radius: 6px;
     
+    font-family: inherit;
     color: var(--foreground);
     background: var(--primary-background);
     transition: 0.2s ease background-color;
diff --git a/src/components/ui/Overline.tsx b/src/components/ui/Overline.tsx
index 5a9f9d04ca4af9446550599bcb6de8ebd1b2513f..171590770c6ae480838b913a71f49260a60bea50 100644
--- a/src/components/ui/Overline.tsx
+++ b/src/components/ui/Overline.tsx
@@ -5,6 +5,7 @@ import { Text } from 'preact-i18n';
 type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & {
     error?: string;
     block?: boolean;
+    spaced?: boolean;
     children?: Children;
     type?: "default" | "subtle" | "error";
 }
@@ -12,7 +13,10 @@ type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & {
 const OverlineBase = styled.div<Omit<Props, "children" | "error">>`
     display: inline;
     margin: 0.4em 0;
-    margin-top: 0.8em;
+
+    ${ props => props.spaced && css`
+        margin-top: 0.8em;
+    ` }
 
     font-size: 14px;
     font-weight: 600;
diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx
index d61e43c79eddab2b543f864169fd6fcea0206a68..88f5959dff523b78a3f84c8086e0c10db0dc0e9e 100644
--- a/src/components/ui/TextArea.tsx
+++ b/src/components/ui/TextArea.tsx
@@ -39,8 +39,10 @@ export default styled.textarea<TextAreaProps>`
     }
 
     ${ props => props.code ? css`
-        font-family: 'Fira Mono', 'Courier New', Courier, monospace;
+        font-family: var(--monoscape-font-font), monospace;
     ` : css`
-        font-family: 'Open Sans', sans-serif;
+        font-family: inherit;
     ` }
+    
+    font-variant-ligatures: var(--ligatures);
 `;
diff --git a/src/components/ui/Tip.tsx b/src/components/ui/Tip.tsx
index 489572b915f688b63a9101c50e8630b12a8d4ed6..f664a58cac3965f6632a73a437ab295df0ef13f3 100644
--- a/src/components/ui/Tip.tsx
+++ b/src/components/ui/Tip.tsx
@@ -7,6 +7,13 @@ interface Props {
     error?: boolean
 }
 
+export const Separator = styled.div<Props>`
+    height: 1px;
+    width: calc(100% - 10px);
+    background: var(--secondary-header);
+    margin: 18px auto;
+`;
+
 export const TipBase = styled.div<Props>`
     display: flex;
     padding: 12px;
@@ -46,9 +53,13 @@ export const TipBase = styled.div<Props>`
 export default function Tip(props: Props & { children: Children }) {
     const { children, ...tipProps } = props;
     return (
-        <TipBase {...tipProps}>
-            <InfoCircle size={20} />
-            <span>{props.children}</span>
-        </TipBase>
+        <>
+            <Separator />
+            <TipBase {...tipProps}>
+                <InfoCircle size={20} />
+                <span>{props.children}</span>
+            </TipBase>
+        </>
+        
     );
 }
diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx
index af894b40affb3cf2a7a5b4b92aede719943a7b5d..6fdff9e216a3173b2e8e6eedac9b9744d9d70d6e 100644
--- a/src/context/Locale.tsx
+++ b/src/context/Locale.tsx
@@ -43,7 +43,6 @@ export enum Language {
     PIRATE = "pr",
     BOTTOM = "bottom",
     PIGLATIN = "piglatin",
-    HARDCORE = "hardcore",
 }
 
 export interface LanguageEntry {
@@ -107,13 +106,6 @@ export const Languages: { [key in Language]: LanguageEntry } = {
         dayjs: "en-gb",
         alt: true
     },
-    hardcore: {
-        display: "Hardcore Mode",
-        emoji: "🔥",
-        i18n: "hardcore",
-        dayjs: "en-gb",
-        alt: true
-    },
 };
 
 interface Props {
@@ -126,8 +118,10 @@ function Locale({ children, locale }: Props) {
     const [defns, setDefinition] = useState<Record<string, unknown>>(definition);
     const lang = Languages[locale];
 
-    // TOOD: clean this up and use the built in Intl API
-    function transformLanguage(obj: { [key: string]: any }) {
+    // TODO: clean this up and use the built in Intl API
+    function transformLanguage(source: { [key: string]: any }) {
+        const obj = defaultsDeep(source, definition);
+
         const dayjs = obj.dayjs;
         const defaults = dayjs.defaults;
 
@@ -151,23 +145,16 @@ function Locale({ children, locale }: Props) {
 
     useEffect(() => {
         if (locale === "en") {
-            transformLanguage(definition);
-            setDefinition(definition);
+            const defn = transformLanguage(definition);
+            setDefinition(defn);
             dayjs.locale("en");
-            dayjs.updateLocale('en', { calendar: definition.dayjs });
-            return;
-        }
-
-        if (lang.i18n === "hardcore") {
-            // eslint-disable-next-line @typescript-eslint/no-explicit-any
-            setDefinition({});
+            dayjs.updateLocale('en', { calendar: defn.dayjs });
             return;
         }
 
         import(`../../external/lang/${lang.i18n}.json`).then(
             async (lang_file) => {
-                const defn = lang_file.default;
-                transformLanguage(defn);
+                const defn = transformLanguage(lang_file.default);
                 const target = lang.dayjs ?? lang.i18n;
                 const dayjs_locale = await import(`../../node_modules/dayjs/esm/locale/${target}.js`);
 
@@ -176,7 +163,7 @@ function Locale({ children, locale }: Props) {
                 }
 
                 dayjs.locale(dayjs_locale.default);
-                setDefinition(defaultsDeep(defn, definition));
+                setDefinition(defn);
             }
         );
     }, [locale, lang]);
diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx
index b6131c6f64c15227ac87ba676ff887dc9672c805..02f5ca6611c4673fcb84f6a57d26eb8cad99f211 100644
--- a/src/context/Theme.tsx
+++ b/src/context/Theme.tsx
@@ -33,18 +33,161 @@ export type Variables =
     | "status-invisible"
     | "sidebar-active";
 
+export type Fonts = 'Open Sans' | 'Inter' | 'Atkinson Hyperlegible' | 'Roboto' | 'Noto Sans' | 'Lato' | 'Bree Serif' | 'Montserrat' | 'Poppins' | 'Raleway' | 'Ubuntu' | 'Comic Neue';
+export type MonoscapeFonts = 'Fira Code' | 'Roboto Mono' | 'Source Code Pro' | 'Space Mono' | 'Ubuntu Mono';
+
 export type Theme = {
     [variable in Variables]: string;
 } & {
     light?: boolean;
+    font?: Fonts;
     css?: string;
+    monoscapeFont?: MonoscapeFonts;
 };
 
 export interface ThemeOptions {
     preset?: string;
+    ligatures?: boolean;
     custom?: Partial<Theme>;
 }
 
+// import aaa from "@fontsource/open-sans/300.css?raw";
+// console.info(aaa);
+
+export const FONTS: Record<Fonts, { name: string, load: () => void }> = {
+    "Open Sans": {
+        name: "Open Sans",
+        load: async () => {
+            await import("@fontsource/open-sans/300.css");
+            await import("@fontsource/open-sans/400.css");
+            await import("@fontsource/open-sans/600.css");
+            await import("@fontsource/open-sans/700.css");
+            await import("@fontsource/open-sans/400-italic.css");
+        }
+    },
+    Inter: {
+        name: "Inter",
+        load: async () => {
+            await import("@fontsource/inter/300.css");
+            await import("@fontsource/inter/400.css");
+            await import("@fontsource/inter/600.css");
+            await import("@fontsource/inter/700.css");
+        }
+    },
+    "Atkinson Hyperlegible": {
+        name: "Atkinson Hyperlegible",
+        load: async () => {
+            await import("@fontsource/atkinson-hyperlegible/400.css");
+            await import("@fontsource/atkinson-hyperlegible/700.css");
+            await import("@fontsource/atkinson-hyperlegible/400-italic.css");
+        }
+    },
+    "Roboto": {
+        name: "Roboto",
+        load: async () => {
+            await import("@fontsource/roboto/400.css");
+            await import("@fontsource/roboto/700.css");
+            await import("@fontsource/roboto/400-italic.css");
+        }
+    },
+    "Noto Sans": {
+        name: "Noto Sans",
+        load: async () => {
+            await import("@fontsource/noto-sans/400.css");
+            await import("@fontsource/noto-sans/700.css");
+            await import("@fontsource/noto-sans/400-italic.css");
+        }
+    },
+    "Bree Serif": {
+        name: "Bree Serif",
+        load: () => import("@fontsource/bree-serif/400.css")
+    },
+    "Lato": {
+        name: "Lato",
+        load: async () => {
+            await import("@fontsource/lato/300.css");
+            await import("@fontsource/lato/400.css");
+            await import("@fontsource/lato/700.css");
+            await import("@fontsource/lato/400-italic.css");
+        }
+    },
+    "Montserrat": {
+        name: "Montserrat",
+        load: async () => {
+            await import("@fontsource/montserrat/300.css");
+            await import("@fontsource/montserrat/400.css");
+            await import("@fontsource/montserrat/600.css");
+            await import("@fontsource/montserrat/700.css");
+            await import("@fontsource/montserrat/400-italic.css");
+        }
+    },
+    "Poppins": {
+        name: "Poppins",
+        load: async () => {
+            await import("@fontsource/poppins/300.css");
+            await import("@fontsource/poppins/400.css");
+            await import("@fontsource/poppins/600.css");
+            await import("@fontsource/poppins/700.css");
+            await import("@fontsource/poppins/400-italic.css");
+        }
+    },
+    "Raleway": {
+        name: "Raleway",
+        load: async () => {
+            await import("@fontsource/raleway/300.css");
+            await import("@fontsource/raleway/400.css");
+            await import("@fontsource/raleway/600.css");
+            await import("@fontsource/raleway/700.css");
+            await import("@fontsource/raleway/400-italic.css");
+        }
+    },
+    "Ubuntu": {
+        name: "Ubuntu",
+        load: async () => {
+            await import("@fontsource/ubuntu/300.css");
+            await import("@fontsource/ubuntu/400.css");
+            await import("@fontsource/ubuntu/500.css");
+            await import("@fontsource/ubuntu/700.css");
+            await import("@fontsource/ubuntu/400-italic.css");
+        }
+    },
+    "Comic Neue": {
+        name: "Comic Neue",
+        load: async () => {
+            await import("@fontsource/comic-neue/300.css");
+            await import("@fontsource/comic-neue/400.css");
+            await import("@fontsource/comic-neue/700.css");
+            await import("@fontsource/comic-neue/400-italic.css");
+        }
+    }
+};
+
+export const MONOSCAPE_FONTS: Record<MonoscapeFonts, { name: string, load: () => void }> = {
+    "Fira Code": {
+        name: "Fira Code",
+        load: () => import("@fontsource/fira-code/400.css")
+    },
+    "Roboto Mono": {
+        name: "Roboto Mono",
+        load: () => import("@fontsource/roboto-mono/400.css")
+    },
+    "Source Code Pro": {
+        name: "Source Code Pro",
+        load: () => import("@fontsource/source-code-pro/400.css")
+    },
+    "Space Mono": {
+        name: "Space Mono",
+        load: () => import("@fontsource/space-mono/400.css")
+    },
+    "Ubuntu Mono": {
+        name: "Ubuntu Mono",
+        load: () => import("@fontsource/ubuntu-mono/400.css")
+    }
+};
+
+export const FONT_KEYS = Object.keys(FONTS).sort();
+export const MONOSCAPE_FONT_KEYS = Object.keys(MONOSCAPE_FONTS).sort();
+
 // Generated from https://gitlab.insrt.uk/revolt/community/themes
 export const PRESETS: Record<string, Theme> = {
     light: {
@@ -124,15 +267,32 @@ interface Props {
     options?: ThemeOptions;
 }
 
-function Theme(props: Props) {
+function Theme({ children, options }: Props) {
     const theme: Theme = {
         ...PRESETS["dark"],
         ...PRESETS[props.options?.preset ?? ''],
         ...props.options?.custom
     };
 
+    const root = document.documentElement.style;
+    useEffect(() => {
+        const font = theme.font ?? 'Inter';
+        root.setProperty('--font', `"${font}"`);
+        FONTS[font].load();
+    }, [ theme.font ]);
+
+    useEffect(() => {
+        const font = theme.monoscapeFont ?? 'Fira Code';
+        root.setProperty('--monoscape-font', `"${font}"`);
+        MONOSCAPE_FONTS[font].load();
+    }, [ theme.monoscapeFont ]);
+
+    useEffect(() => {
+        root.setProperty('--ligatures', options?.ligatures ? 'normal' : 'none');
+    }, [ options?.ligatures ]);
+
     useEffect(() => {
-        const resize = () => document.documentElement.style.setProperty('--app-height', `${window.innerHeight}px`);
+        const resize = () => root.setProperty('--app-height', `${window.innerHeight}px`);
         resize();
 
         window.addEventListener('resize', resize);
@@ -155,7 +315,7 @@ function Theme(props: Props) {
             {theme.css && (
                 <style dangerouslySetInnerHTML={{ __html: theme.css }} />
             )}
-            {props.children}
+            { children }
         </ThemeContext.Provider>
     );
 }
diff --git a/src/context/intermediate/modals/Prompt.module.scss b/src/context/intermediate/modals/Prompt.module.scss
index a107fcf6aff98f5e39dc739199459064d7b36ee6..a228b4ccaa784449d998faa44c42a1cbe3fb293e 100644
--- a/src/context/intermediate/modals/Prompt.module.scss
+++ b/src/context/intermediate/modals/Prompt.module.scss
@@ -7,7 +7,7 @@
         user-select: all;
         font-size: 1.4em;
         text-align: center;
-        font-family: "Fira Mono";
+        font-family: var(--monoscape-font);
     }
 }
 
diff --git a/src/context/revoltjs/FileUploads.tsx b/src/context/revoltjs/FileUploads.tsx
index 95f50e8cab728d1ddf77500291daf402dfc61231..87781e2f2ef9cb78b2c59dc995010a693446db34 100644
--- a/src/context/revoltjs/FileUploads.tsx
+++ b/src/context/revoltjs/FileUploads.tsx
@@ -8,7 +8,8 @@ import { useContext, useEffect, useState } from "preact/hooks";
 import Preloader from "../../components/ui/Preloader";
 import { determineFileSize } from "../../lib/fileSize";
 import IconButton from '../../components/ui/IconButton';
-import { Edit, Plus, X, XCircle } from "@styled-icons/boxicons-regular";
+import { Plus, X, XCircle } from "@styled-icons/boxicons-regular";
+import { Pencil } from "@styled-icons/boxicons-solid";
 import { useIntermediate } from "../intermediate/Intermediate";
 
 type Props = {
@@ -190,7 +191,7 @@ export function FileUploader(props: Props) {
                             <Preloader type="ring" />
                         </div> :
                         <div className={styles.edit}>
-                            <Edit size={30} />
+                            <Pencil size={30} />
                         </div> }
                 </div>
                 <div className={styles.modify}>
diff --git a/src/lib/TextAreaAutoSize.tsx b/src/lib/TextAreaAutoSize.tsx
index bfc515b071a8dd63f5f3f3709f4c74e0b431368a..c602655489632aa84eee5c96aff38d16dbccb220 100644
--- a/src/lib/TextAreaAutoSize.tsx
+++ b/src/lib/TextAreaAutoSize.tsx
@@ -23,6 +23,7 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
     const ref = useRef<HTMLTextAreaElement>();
 
     useEffect(() => {
+        if (isTouchscreenDevice) return;
         autoFocus && ref.current.focus();
     }, [value]);
     
diff --git a/src/pages/developer/Developer.tsx b/src/pages/developer/Developer.tsx
index 1a392bc0119055c4afd45a451ab4ff725d93c889..e8c3e187da029c0aeff7ea6dfad48cf1d5cccc8c 100644
--- a/src/pages/developer/Developer.tsx
+++ b/src/pages/developer/Developer.tsx
@@ -4,6 +4,7 @@ import Header from "../../components/ui/Header";
 import PaintCounter from "../../lib/PaintCounter";
 import { AppContext } from "../../context/revoltjs/RevoltClient";
 import { useUserPermission } from "../../context/revoltjs/hooks";
+import { Wrench } from "@styled-icons/boxicons-solid";
 
 export default function Developer() {
     // const voice = useContext(VoiceContext);
@@ -12,7 +13,10 @@ export default function Developer() {
 
     return (
         <div>
-            <Header placement="primary">Developer Tab</Header>
+            <Header placement="primary">
+                <Wrench size="24" />
+                Developer Tab
+            </Header>
             <div style={{ padding: "16px" }}>
                 <PaintCounter always />
             </div>
diff --git a/src/pages/friends/Friend.module.scss b/src/pages/friends/Friend.module.scss
index af867b28721f6907074ed08681c09a4df44e6bed..ee9c6f90ef010b31c2b21ecfcdab3ee17473ad95 100644
--- a/src/pages/friends/Friend.module.scss
+++ b/src/pages/friends/Friend.module.scss
@@ -14,57 +14,6 @@
     padding: 0 10px 10px 10px;
     user-select: none;
     overflow-y: scroll;
-    
-    summary {
-        position: sticky;
-        z-index: 10;
-        top: -1px;
-    }
-
-    .overline {
-        display: flex;
-        align-items: center;
-        background: var(--primary-background);
-        padding: 5px 0;
-        cursor: pointer;
-
-        .title {
-            text-overflow: ellipsis;
-            overflow: hidden;
-            white-space: nowrap;
-        }
-        
-        svg {
-            margin-inline-end: 4px;
-        }
-    }
-
-    details {
-        summary {
-            outline: none;
-            list-style: none;
-            transition: .2s opacity;
-
-            &::marker, &::-webkit-details-marker {
-                display: none;
-            }
-
-            svg {
-                flex-shrink: 0;
-                transition: .2s ease transform;
-            }
-        }
-
-        &:not([open]) {
-            summary {
-                opacity: .7;
-            }
-            
-            summary svg {
-                transform: rotateZ(-90deg);
-            }
-        }
-    }
 
     &[data-empty="true"] {
         img {
@@ -145,7 +94,6 @@
 .divider {
     width: 1px;
     height: 24px;
-    margin: 0 8px;
     background: var(--primary-background);
 }
 
diff --git a/src/pages/friends/Friends.tsx b/src/pages/friends/Friends.tsx
index 7ccf4761974ea253b10efd86f61e4568703f2eaf..d90b5d58781dbe7b70a539bf8c4e1b79b5392991 100644
--- a/src/pages/friends/Friends.tsx
+++ b/src/pages/friends/Friends.tsx
@@ -14,6 +14,8 @@ import { ChevronDown, ChevronRight, ListPlus } from "@styled-icons/boxicons-regu
 import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid";
 import { TextReact } from "../../lib/i18n";
 import { Children } from "../../types/Preact";
+import Details from "../../components/ui/Details";
+import CollapsibleSection from "../../components/common/CollapsibleSection";
 
 export default function Friends() {
     const { openScreen } = useIntermediate();
@@ -29,17 +31,17 @@ export default function Friends() {
         ) ],
         [ 'app.special.friends.sent', users.filter(x =>
             x.relationship === Users.Relationship.Outgoing
-        ) ],
+        ), 'outgoing' ],
         [ 'app.status.online', friends.filter(x =>
             x.online && x.status?.presence !== Users.Presence.Invisible
-        ) ],
+        ), 'online' ],
         [ 'app.status.offline', friends.filter(x =>
             !x.online || x.status?.presence === Users.Presence.Invisible
-        ) ],
-        [ 'app.special.friends.blocked', friends.filter(x =>
+        ), 'offline' ],
+        [ 'app.special.friends.blocked', users.filter(x =>
             x.relationship === Users.Relationship.Blocked
-        ) ]
-    ] as [ string, User[] ][];
+        ), 'blocked' ]
+    ] as [ string, User[], string ][];
 
     const incoming = lists[0][1];
     const userlist: Children[] = incoming.map(x => <b>{ x.username }</b>);
@@ -56,7 +58,7 @@ export default function Friends() {
                 <div className={styles.actions}>
                     {/*<Tooltip content={"Create Category"} placement="bottom">
                         <IconButton onClick={() => openScreen({ id: 'special_input', type: 'create_group' })}>
-                            <ListPlus size={24} />
+                            <ListPlus size={28} />
                         </IconButton>
                     </Tooltip>
                     <div className={styles.divider} />*/}
@@ -107,22 +109,18 @@ export default function Friends() {
                 </div> }
 
                 {
-                    lists.map(([i18n, list], index) => {
+                    lists.map(([i18n, list, section_id], index) => {
                         if (index === 0) return;
                         if (list.length === 0) return;
 
                         return (
-                            <details open>
-                                <summary>
-                                    <Overline className={styles.overline} type="subtle">
-                                        <ChevronDown size={20} />
-                                        <div className={styles.title}>
-                                            <Text id={i18n} /> — { list.length }
-                                        </div>
-                                    </Overline>
-                                </summary>
+                            <CollapsibleSection
+                                id={`friends_${section_id}`}
+                                defaultValue={true}
+                                sticky large
+                                summary={<div class="title"><Text id={i18n} /> — { list.length }</div>}>
                                 { list.map(x => <Friend key={x._id} user={x} />) }
-                            </details>
+                            </CollapsibleSection>
                         )
                     })
                 }
diff --git a/src/pages/settings/Settings.module.scss b/src/pages/settings/Settings.module.scss
index 82f5ba03420d08ab8afa900e622f7b389b91d6e1..e8648eb9cc7d26f3815e256f721dd8bdb7293325 100644
--- a/src/pages/settings/Settings.module.scss
+++ b/src/pages/settings/Settings.module.scss
@@ -93,7 +93,7 @@
             margin: 1rem 12px 0;
             font-size: 10px;
             color: var(--secondary-foreground);
-            font-family: "Fira Mono", monospace;
+            font-family: var(--monoscape-font), monospace;
             user-select: text;
 
             display: grid;
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
index 0141409dba7720b57c74b3d8f950caa9e87b9e04..4fa2b5827333b1fb9f186fe5176263182f3a0e92 100644
--- a/src/pages/settings/Settings.tsx
+++ b/src/pages/settings/Settings.tsx
@@ -10,22 +10,19 @@ import {
     Bell,
     Palette,
     Coffee,
-    Globe,
     IdCard,
-    LogOut,
-    Sync as SyncIcon,
-    Shield,
-    Vial,
-    User
-} from "@styled-icons/boxicons-regular";
-import { Brush, Megaphone } from "@styled-icons/boxicons-solid";
+    CheckShield,
+    Flask,
+    User,
+    Megaphone
+} from "@styled-icons/boxicons-solid";
+import { Sync as SyncIcon, Globe, LogOut } from "@styled-icons/boxicons-regular";
 import { Gitlab } from "@styled-icons/boxicons-logos";
 import { GIT_BRANCH, GIT_REVISION, REPO_URL } from "../../revision";
 import LineDivider from "../../components/ui/LineDivider";
 import RequiresOnline from "../../context/revoltjs/RequiresOnline";
 import ButtonItem from "../../components/navigation/items/ButtonItem";
 import { AppContext, OperationsContext } from "../../context/revoltjs/RevoltClient";
-
 import { Account } from "./panes/Account";
 import { Profile } from "./panes/Profile";
 import { Sessions } from "./panes/Sessions";
@@ -64,7 +61,7 @@ export default function Settings() {
                 },
                 {
                     id: 'sessions',
-                    icon: <Shield size={20} />,
+                    icon: <CheckShield size={20} />,
                     title: <Text id="app.settings.pages.sessions.title" />
                 },
                 {
@@ -91,7 +88,7 @@ export default function Settings() {
                 {
                     divider: true,
                     id: 'experiments',
-                    icon: <Vial size={20} />,
+                    icon: <Flask size={20} />,
                     title: <Text id="app.settings.pages.experiments.title" />
                 },
                 {
diff --git a/src/pages/settings/panes/Account.tsx b/src/pages/settings/panes/Account.tsx
index 7fd102147ee17d67e46fa69fcc1e825bf81b7423..88a7839c70b7e53a71451de484fb3a5624ed129d 100644
--- a/src/pages/settings/panes/Account.tsx
+++ b/src/pages/settings/panes/Account.tsx
@@ -5,7 +5,8 @@ import Button from "../../../components/ui/Button";
 import { Users } from "revolt.js/dist/api/objects";
 import { Link, useHistory } from "react-router-dom";
 import Overline from "../../../components/ui/Overline";
-import { At, Key, Envelope } from "@styled-icons/boxicons-regular";
+import { Envelope, Key } from "@styled-icons/boxicons-solid";
+import { At } from "@styled-icons/boxicons-regular";
 import { useContext, useEffect, useState } from "preact/hooks";
 import UserIcon from "../../../components/common/user/UserIcon";
 import { useForceUpdate, useSelf } from "../../../context/revoltjs/hooks";
@@ -47,9 +48,7 @@ export function Account() {
     return (
         <div className={styles.user}>
             <div className={styles.banner}>
-                <Link to="/settings/profile">
-                    <UserIcon target={user} size={72} />
-                </Link>
+                <UserIcon className={styles.avatar} target={user} size={72} onClick={() => switchPage("profile")}/>
                 <div className={styles.username}>@{user.username}</div>
             </div>
             <div className={styles.details}>
diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx
index fdf209d1da94c23a64b3c8fc982946f5c86401b9..6683021338d9c29d0a3900db568e1ab3ad2e5bb8 100644
--- a/src/pages/settings/panes/Appearance.tsx
+++ b/src/pages/settings/panes/Appearance.tsx
@@ -2,15 +2,18 @@ import { Text } from "preact-i18n";
 import styles from "./Panes.module.scss";
 import { debounce } from "../../../lib/debounce";
 import Button from "../../../components/ui/Button";
+import Checkbox from "../../../components/ui/Checkbox";
+import ComboBox from "../../../components/ui/ComboBox";
 import InputBox from "../../../components/ui/InputBox";
 import { connectState } from "../../../redux/connector";
 import { WithDispatcher } from "../../../redux/reducers";
 import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
 import ColourSwatches from "../../../components/ui/ColourSwatches";
 import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
-import { Theme, ThemeContext, ThemeOptions } from "../../../context/Theme";
 import { useCallback, useContext, useEffect, useState } from "preact/hooks";
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
+import CollapsibleSection from "../../../components/common/CollapsibleSection";
+import { FONTS, FONT_KEYS, MONOSCAPE_FONTS, MONOSCAPE_FONT_KEYS, Theme, ThemeContext, ThemeOptions } from "../../../context/Theme";
 
 // @ts-ignore
 import pSBC from 'shade-blend-color';
@@ -129,6 +132,25 @@ export function Component(props: Props & WithDispatcher) {
                 </Radio>
             </div>*/}
 
+            <h3>
+                <Text id="app.settings.pages.appearance.font" />
+            </h3>
+            <ComboBox value={theme.font} onChange={e => setTheme({ custom: { font: e.currentTarget.value as any } })}>
+                {
+                    FONT_KEYS
+                        .map(key =>
+                            <option value={key}>{ FONTS[key as keyof typeof FONTS].name }</option>
+                        )
+                }
+            </ComboBox>
+            <p>
+                <Checkbox checked={props.settings.theme?.ligatures === true}
+                    onChange={() => setTheme({ ligatures: !props.settings.theme?.ligatures })}
+                    description={<Text id="app.settings.pages.appearance.ligatures_desc" />}>
+                    <Text id="app.settings.pages.appearance.ligatures" />
+                </Checkbox>
+            </p>
+
             <h3>
                 <Text id="app.settings.pages.appearance.emoji_pack" />
             </h3>
@@ -171,11 +193,7 @@ export function Component(props: Props & WithDispatcher) {
                 </div>
             </div>
 
-            <details>
-                <summary>
-                    <Text id="app.settings.pages.appearance.advanced" />
-                    <div className={styles.divider}></div>
-                </summary>
+            <CollapsibleSection id="settings_advanced_appearance" defaultValue={false} summary={<Text id="app.settings.pages.appearance.advanced" />}>
                 <h3>
                     <Text id="app.settings.pages.appearance.overrides" />
                 </h3>
@@ -263,6 +281,19 @@ export function Component(props: Props & WithDispatcher) {
                         </div>
                     ))}
                 </div>
+
+                <h3>
+                    <Text id="app.settings.pages.appearance.mono_font" />
+                </h3>
+                <ComboBox value={theme.monoscapeFont} onChange={e => setTheme({ custom: { monoscapeFont: e.currentTarget.value as any } })}>
+                    {
+                        MONOSCAPE_FONT_KEYS
+                            .map(key =>
+                                <option value={key}>{ MONOSCAPE_FONTS[key as keyof typeof MONOSCAPE_FONTS].name }</option>
+                            )
+                    }
+                </ComboBox>
+
                 <h3>
                     <Text id="app.settings.pages.appearance.custom_css" />
                 </h3>
@@ -272,7 +303,7 @@ export function Component(props: Props & WithDispatcher) {
                     code
                     value={css}
                     onChange={ev => setCSS(ev.currentTarget.value)} />
-            </details>
+            </CollapsibleSection>
         </div>
     );
 }
diff --git a/src/pages/settings/panes/Notifications.tsx b/src/pages/settings/panes/Notifications.tsx
index 6122d28453729beb1c9e998386828eb2958b9575..eccd0cead1fef22f4779d8a945aa2817d52bae6f 100644
--- a/src/pages/settings/panes/Notifications.tsx
+++ b/src/pages/settings/panes/Notifications.tsx
@@ -39,6 +39,7 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) {
             <Checkbox
                 disabled={!("Notification" in window)}
                 checked={options?.desktopEnabled ?? false}
+                description={<Text id="app.settings.pages.notifications.descriptions.enable_desktop" />}
                 onChange={async desktopEnabled => {
                     if (desktopEnabled) {
                         let permission = await Notification.requestPermission();
@@ -57,13 +58,11 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) {
                 }}
             >
                 <Text id="app.settings.pages.notifications.enable_desktop" />
-                <p>
-                    <Text id="app.settings.pages.notifications.descriptions.enable_desktop" />
-                </p>
             </Checkbox>
             <Checkbox
                 disabled={typeof pushEnabled === "undefined"}
                 checked={pushEnabled ?? false}
+                description={<Text id="app.settings.pages.notifications.descriptions.enable_push" />}
                 onChange={async pushEnabled => {
                     try {
                         const reg = await navigator.serviceWorker?.getRegistration();
@@ -99,9 +98,6 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) {
                 }}
             >
                 <Text id="app.settings.pages.notifications.enable_push" />
-                <p>
-                    <Text id="app.settings.pages.notifications.descriptions.enable_push" />
-                </p>
             </Checkbox>
             <h3>
                 <Text id="app.settings.pages.notifications.sounds" />
diff --git a/src/pages/settings/panes/Panes.module.scss b/src/pages/settings/panes/Panes.module.scss
index 4695af59e7d12ad07838bd4cb25e25811cbb3d49..206810205c814fa4da06b0474ae654d43e9255e1 100644
--- a/src/pages/settings/panes/Panes.module.scss
+++ b/src/pages/settings/panes/Panes.module.scss
@@ -8,16 +8,18 @@
         align-items: center;
         background: var(--secondary-header);
 
-        .username {
-            font-size: 24px;
-        }
-
-        a {
+        .avatar {
+            cursor: pointer;
             transition: 0.2s ease filter;
+
+            &:hover {
+                filter: brightness(80%);
+            }
         }
 
-        a:hover {
-            filter: brightness(80%);
+        .username {
+            font-size: 24px;
+            font-weight: 600;
         }
     }
 
@@ -32,6 +34,10 @@
             display: flex;
             align-items: center;
             flex-direction: row;
+
+            > svg {
+                flex-shrink: 0;
+            }
         }
 
         .detail {
@@ -321,30 +327,16 @@
     }
 }
 
-.notifications {
-    label {
-        margin-top: 12px;
-    }
-
-    p {
-        margin-top: 0;
-        font-size: 0.9em;
-        color: var(--secondary-foreground);
-    }
-}
-
 .languages {
     .list {
         margin-bottom: 1em;
         
         .entry {
-            padding: 2px 8px;
             height: 50px;
-            border-radius: 4px;
         }
 
         .entry > span > span {
-            gap: 8px;
+            gap: 20px;
             display: flex;
             align-items: center;
             flex-direction: row;
diff --git a/src/pages/settings/panes/Sync.tsx b/src/pages/settings/panes/Sync.tsx
index ad7c84f82ba327c07bcc2845798da099b4e9a647..2055016b2c89dd208841822527e5e992db511fb5 100644
--- a/src/pages/settings/panes/Sync.tsx
+++ b/src/pages/settings/panes/Sync.tsx
@@ -25,6 +25,7 @@ export function Component(props: Props & WithDispatcher) {
                     ([ key, title ]) =>
                         <Checkbox
                             checked={(props.options?.disabled ?? []).indexOf(key) === -1}
+                            description={<Text id={`app.settings.pages.sync.descriptions.${key}`} />}
                             onChange={enabled => {
                                 props.dispatcher({
                                     type: enabled ? 'SYNC_ENABLE_KEY' : 'SYNC_DISABLE_KEY',
@@ -33,9 +34,6 @@ export function Component(props: Props & WithDispatcher) {
                             }}
                         >
                             <Text id={`app.settings.pages.${title}`} />
-                            <p>
-                                <Text id={`app.settings.pages.sync.descriptions.${key}`} />
-                            </p>
                         </Checkbox>
                 )
             }
diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx
index e7d30a6f0f8ff2eec6931b2d42ee7ed53d0d29ba..8f0c171061e8caf37e54e7ff9ac7335fe824f10b 100644
--- a/src/pages/settings/server/Members.tsx
+++ b/src/pages/settings/server/Members.tsx
@@ -1,7 +1,6 @@
 import styles from './Panes.module.scss';
 import { useEffect, useState } from "preact/hooks";
 import { Servers } from "revolt.js/dist/api/objects";
-import UserIcon from "../../../components/common/user/UserIcon";
 import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
 
 interface Props {
@@ -23,7 +22,7 @@ export function Members({ server }: Props) {
     return (
         <div className={styles.members}>
             <div className={styles.subtitle}>
-                X Members
+                { members?.length ?? 0 } Members
             </div>
             { members && members.length > 0 && users?.map(x => x && 
                 <div className={styles.member}>
diff --git a/src/pages/settings/server/Overview.tsx b/src/pages/settings/server/Overview.tsx
index fe530276a1736e84f6077ba407b18bacb1254a07..849e0e01517e595f002ae37042e4b787dfd35181 100644
--- a/src/pages/settings/server/Overview.tsx
+++ b/src/pages/settings/server/Overview.tsx
@@ -1,10 +1,13 @@
 import { Text } from "preact-i18n";
+import isEqual from "lodash.isequal";
 import styles from './Panes.module.scss';
 import Button from "../../../components/ui/Button";
-import { Servers } from "revolt.js/dist/api/objects";
 import InputBox from "../../../components/ui/InputBox";
+import ComboBox from "../../../components/ui/ComboBox";
+import { Servers, Server } from "revolt.js/dist/api/objects";
 import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
 import { useContext, useEffect, useState } from "preact/hooks";
+import { getChannelName } from "../../../context/revoltjs/util";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { FileUploader } from "../../../context/revoltjs/FileUploads";
 
@@ -17,16 +20,18 @@ export function Overview({ server }: Props) {
 
     const [name, setName] = useState(server.name);
     const [description, setDescription] = useState(server.description ?? '');
+    const [systemMessages, setSystemMessages] = useState(server.system_messages);
 
     useEffect(() => setName(server.name), [ server.name ]);
     useEffect(() => setDescription(server.description ?? ''), [ server.description ]);
+    useEffect(() => setSystemMessages(server.system_messages), [ server.system_messages ]);
 
     const [ changed, setChanged ] = useState(false);
     function save() {
-        let changes: any = {};
+        let changes: Partial<Pick<Servers.Server, 'name' | 'description' | 'system_messages'>> = {};
         if (name !== server.name) changes.name = name;
-        if (description !== server.description)
-            changes.description = description;
+        if (description !== server.description) changes.description = description;
+        if (!isEqual(systemMessages, server.system_messages)) changes.system_messages = systemMessages;
         
         client.servers.edit(server._id, changes);
         setChanged(false);
@@ -76,11 +81,6 @@ export function Overview({ server }: Props) {
                     if (!changed) setChanged(true)
                 }}
             />
-            <p>
-                <Button onClick={save} contrast disabled={!changed}>
-                    <Text id="app.special.modals.actions.save" />
-                </Button>
-            </p>
 
             <h3>
                 <Text id="app.main.servers.custom_banner" />
@@ -95,6 +95,48 @@ export function Overview({ server }: Props) {
                 previewURL={client.servers.getBannerURL(server._id, { width: 1000 }, true)}
                 remove={() => client.servers.edit(server._id, { remove: 'Banner' })}
             />
+
+            <h3>
+                <Text id="app.settings.server_pages.overview.system_messages" />
+            </h3>
+            {[
+                [ 'User Joined', 'user_joined' ],
+                [ 'User Left', 'user_left' ],
+                [ 'User Kicked', 'user_kicked' ],
+                [ 'User Banned', 'user_banned' ]
+            ].map(([ i18n, key ]) =>
+                // ! FIXME: temporary code just so we can expose the options
+                <p style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
+                    <span style={{ flexShrink: '0', flex: `25%` }}>{i18n}</span>
+                    <ComboBox value={systemMessages?.[key as keyof typeof systemMessages] ?? 'disabled'}
+                        onChange={e => {
+                            if (!changed) setChanged(true)
+                            const v = e.currentTarget.value;
+                            if (v === 'disabled') {
+                                const { [key as keyof typeof systemMessages]: _, ...other } = systemMessages;
+                                setSystemMessages(other);
+                            } else {
+                                setSystemMessages({
+                                    ...systemMessages,
+                                    [key]: v
+                                });
+                            }
+                        }}>
+                        <option value='disabled'><Text id="general.disabled" /></option>
+                        { server.channels.map(id => {
+                            const channel = client.channels.get(id);
+                            if (!channel) return null;
+                            return <option value={id}>{ getChannelName(client, channel, true) }</option>;
+                        }) }
+                    </ComboBox>
+                </p>
+            )}
+
+            <p>
+                <Button onClick={save} contrast disabled={!changed}>
+                    <Text id="app.special.modals.actions.save" />
+                </Button>
+            </p>
         </div>
     );
 }
diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx
index cd827fe3bcba0a809f363216ae184cd0d5920a5f..09bf0921da57e1ce4921c3bf911c180900950737 100644
--- a/src/pages/settings/server/Roles.tsx
+++ b/src/pages/settings/server/Roles.tsx
@@ -49,7 +49,7 @@ export function Roles({ server }: Props) {
             <div className={styles.list}>
                 <div className={styles.title}>
                     <h1><Text id="app.settings.server_pages.roles.title" /></h1>
-                    <Plus size={16} onClick={() =>
+                    <Plus size={22} onClick={() =>
                         openScreen({ id: 'special_input', type: 'create_role', server: server._id, callback: id => setRole(id) })} />
                 </div>
                 { [ 'default', ...Object.keys(roles) ]
diff --git a/src/redux/index.ts b/src/redux/index.ts
index dc90bec03a461bd90ec4a8730cc41942684a02c6..4bee0dcd667006f406e247a19cb1e8d3c7f1ab35 100644
--- a/src/redux/index.ts
+++ b/src/redux/index.ts
@@ -14,6 +14,7 @@ import { QueuedMessage } from "./reducers/queue";
 import { ExperimentOptions } from "./reducers/experiments";
 import { LastOpened } from "./reducers/last_opened";
 import { Notifications } from "./reducers/notifications";
+import { SectionToggle } from "./reducers/section_toggle";
 
 export type State = {
     config: Core.RevoltNodeConfiguration,
@@ -28,6 +29,7 @@ export type State = {
     experiments: ExperimentOptions;
     lastOpened: LastOpened;
     notifications: Notifications;
+    sectionToggle: SectionToggle;
 };
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -56,7 +58,8 @@ store.subscribe(() => {
         sync,
         experiments,
         lastOpened,
-        notifications
+        notifications,
+        sectionToggle
     } = store.getState() as State;
 
     localForage.setItem("state", {
@@ -70,6 +73,7 @@ store.subscribe(() => {
         sync,
         experiments,
         lastOpened,
-        notifications
+        notifications,
+        sectionToggle
     });
 });
diff --git a/src/redux/reducers/index.ts b/src/redux/reducers/index.ts
index fe47ccbdc3de006eed1f954b21004518c5e7516c..baf217dcf1dcc9c2252104e10f25309c5914b785 100644
--- a/src/redux/reducers/index.ts
+++ b/src/redux/reducers/index.ts
@@ -13,6 +13,7 @@ import { sync, SyncAction } from "./sync";
 import { experiments, ExperimentsAction } from "./experiments";
 import { lastOpened, LastOpenedAction } from "./last_opened";
 import { notifications, NotificationsAction } from "./notifications";
+import { sectionToggle, SectionToggleAction } from "./section_toggle";
 
 export default combineReducers({
     config,
@@ -26,7 +27,8 @@ export default combineReducers({
     sync,
     experiments,
     lastOpened,
-    notifications
+    notifications,
+    sectionToggle
 });
 
 export type Action =
@@ -42,6 +44,7 @@ export type Action =
     | ExperimentsAction
     | LastOpenedAction
     | NotificationsAction
+    | SectionToggleAction
     | { type: "__INIT"; state: State };
 
 export type WithDispatcher = { dispatcher: (action: Action) => void };
diff --git a/src/redux/reducers/section_toggle.ts b/src/redux/reducers/section_toggle.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26b23dcac096f90985f5383bb7c64c18de4dfc49
--- /dev/null
+++ b/src/redux/reducers/section_toggle.ts
@@ -0,0 +1,37 @@
+export interface SectionToggle {
+    [key: string]: boolean
+}
+
+export type SectionToggleAction =
+    | { type: undefined }
+    | {
+        type: "SECTION_TOGGLE_SET";
+        id: string;
+        state: boolean;
+      }
+    | {
+        type: "SECTION_TOGGLE_UNSET";
+        id: string;
+      }
+    | {
+          type: "RESET";
+      };
+
+export function sectionToggle(state = {} as SectionToggle, action: SectionToggleAction): SectionToggle {
+    switch (action.type) {
+        case "SECTION_TOGGLE_SET": {
+            return {
+                ...state,
+                [action.id]: action.state
+            }
+        }
+        case "SECTION_TOGGLE_UNSET": {
+            const { [action.id]: _, ...newState } = state;
+            return newState;
+        }
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
+}
diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts
index bd7e8220296936c19d5dbbeaeb05339ed110988d..35f5f737edbae93063ad9a3ef145c4403cfe4120 100644
--- a/src/redux/reducers/settings.ts
+++ b/src/redux/reducers/settings.ts
@@ -65,7 +65,7 @@ export function settings(
             return {
                 ...state,
                 theme: {
-                    ...filter(state.theme, ["custom", "preset"]),
+                    ...filter(state.theme, ["custom", "preset", "ligatures"]),
                     ...action.theme,
                 },
             };
diff --git a/src/styles/_context-menu.scss b/src/styles/_context-menu.scss
index 8f33978b788dc889e13ff43c2448fc3ad83009d5..d5f15ee13cb5b84af8b20c876c65353ddd3632ac 100644
--- a/src/styles/_context-menu.scss
+++ b/src/styles/_context-menu.scss
@@ -51,7 +51,13 @@
         }
 
         .status {
-            font-size: .6rem;
+            max-width: 132px;
+            font-size: .625rem;
+            color: var(--secondary-foreground);
+
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: nowrap;
         }
     }
 
diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss
deleted file mode 100644
index 9552f22f5cd612b4cf4a4158d007c48f67808772..0000000000000000000000000000000000000000
--- a/src/styles/_fonts.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-@import "@fontsource/open-sans/300.css";
-@import "@fontsource/open-sans/400.css";
-@import "@fontsource/open-sans/600.css";
-@import "@fontsource/open-sans/700.css";
-
-@import "@fontsource/open-sans/400-italic.css";
diff --git a/src/styles/_page.scss b/src/styles/_page.scss
index 124e530abe5ee01968fe711145f576d8048def8d..26b8f34976ff2f5ea265af3ce2742c0ac960050a 100644
--- a/src/styles/_page.scss
+++ b/src/styles/_page.scss
@@ -18,7 +18,9 @@ html,
 body {
     margin: 0;
     height: 100%;
-    font-family: "Open Sans", sans-serif;
+    font-family: var(--font), sans-serif;
+    font-variant-ligatures: var(--ligatures);
+
     -webkit-font-smoothing: antialiased;
     -moz-osx-font-smoothing: grayscale;
     caret-color: var(--accent);
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index 14024c1681fa7d10df538442187d4bc0b0798642..6f3e02aed1ed229af8f97d3644bd653a3d262a24 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -1,4 +1,7 @@
 :root {
+    --ligatures: none;
+    --font: "Open Sans";
     --app-height: 100vh;
+    --codeblock-font: "Fira Code";
     --sidebar-active: var(--secondary-background);
 }
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 4fb88f540a4e863ab855213ec8a8ef44d780f9b3..95c95f1ea77bb63fb8ea74443210cc5cb407a3e3 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,7 +1,6 @@
 @import "variables";
 @import "context-menu";
 @import "elements";
-@import "fonts";
 @import "page";
 
 @import "react-overlapping-panels/dist";
diff --git a/yarn.lock b/yarn.lock
index 292ec6d867db8b148850660cb26625e625efe509..c3f2ec5cafca71498fc892def353f04185b20b9d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -912,16 +912,91 @@
     minimatch "^3.0.4"
     strip-json-comments "^3.1.1"
 
-"@fontsource/fira-mono@^4.4.5":
+"@fontsource/atkinson-hyperlegible@^4.4.5":
   version "4.4.5"
-  resolved "https://registry.yarnpkg.com/@fontsource/fira-mono/-/fira-mono-4.4.5.tgz#ceac70967cd3c4262195603aba567cd4582493f8"
-  integrity sha512-LWbsPhTr1JRV3zUgvMrOxQDn1BG9F4R0FPeBkqWP8/oqPxvVYAhEepg1DN9M1k6L9sRN2I2HWHBpt4QVbDGXpw==
+  resolved "https://registry.yarnpkg.com/@fontsource/atkinson-hyperlegible/-/atkinson-hyperlegible-4.4.5.tgz#4f2ecf34491d5798ec2a346d7de403d99ca82d39"
+  integrity sha512-HmhcEIWucTCfvl4qB/ucGIGjp7zMyOfGgfeY+n84Qz7x4sFosOpQlziPFmgO1MHiU1Quw9VGGj2t+zNU5EtBFQ==
+
+"@fontsource/bree-serif@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/bree-serif/-/bree-serif-4.4.5.tgz#ac5dc36b9b96bcae17820d66eee7d9f64647fc76"
+  integrity sha512-/a8Ef8O5TTWcKSQKFs7kc/oVZddSFEunxgWJwt9f8q9Z9qSYNeUrBft0q8SGA+V6c4m0svaZSWICqBTg/aeDcQ==
+
+"@fontsource/comic-neue@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/comic-neue/-/comic-neue-4.4.5.tgz#00cb5546ca2a9fe9cc2e0fa73349e9202c7b28d5"
+  integrity sha512-0oTyJMmWxiJaf1ZTyyK10RRHBqhYgatAqE0SJPCdithDTzETkHWINEi51lkd95evo418pqmjmjNHGYBgO5CN9w==
+
+"@fontsource/fira-code@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/fira-code/-/fira-code-4.4.5.tgz#380c964676b1e873b192862ce03a7ad1c7838516"
+  integrity sha512-ap1UKABzbPrUTgIB376n8CSJ7mbJYxGm52A60BHnGHjU2cSDBqNUAColdERgFWgLHfybWXQT13ZrNhAdynf9rg==
+
+"@fontsource/inter@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.4.5.tgz#bce26ff9dd83898bd610924cff1342cc30d0e89a"
+  integrity sha512-eRUmB54CbyC3MhAuw2VvTPEc3wy8sFi0/1mVHaC7ZXglXcrSWXY+p8Xad/Zy8AcmS9BuqCCEH2qJbOt9XdX65Q==
+
+"@fontsource/lato@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/lato/-/lato-4.4.5.tgz#82c16ef01ab1a1039f74d774e89ef1f76111def6"
+  integrity sha512-pm9am2tdLnK+21PKCK060oY8R8OKFVzTVvM6pPS+mS7JOZKe3mmDgfB2DGzzyHG/Azj3Pn6onU72BrQsh3XjNw==
+
+"@fontsource/montserrat@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/montserrat/-/montserrat-4.4.5.tgz#f97ebea605f4a2d6b9b23a19bc9bf8fc6f4c79d5"
+  integrity sha512-1F3cR9OIp4Qi1AJHFg/SeFeUWLYemMgI3ldetx0mrlHoWeMuK+p2YentYIIZVkV9ogXOIE2QEw7VnHlGBPo7iw==
+
+"@fontsource/noto-sans@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-4.4.5.tgz#0053bf374f8d5222908c825aa1db6a3078a79cf6"
+  integrity sha512-1P+AMhUvjfpMwuDsLHG1F95ij1SULGM8IiW/4RmdP7+ZHJuvBsSG465Ujps34IDio5bcv6OaLpGyU1YBVTTe+Q==
 
 "@fontsource/open-sans@^4.4.5":
   version "4.4.5"
   resolved "https://registry.yarnpkg.com/@fontsource/open-sans/-/open-sans-4.4.5.tgz#07b31617e62ed753c94cabcf552ebaed4de497ce"
   integrity sha512-PDWEvO1/p8OAHHiielvEmwGXHNbZhrZn96ojV7+/mKgFu+cCUcGVJl9sFs97rCWLe3hKQsYLEsJs4EiLjwa+UQ==
 
+"@fontsource/poppins@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-4.4.5.tgz#c75dcc453f7a9381de3f9b932366c2457887184c"
+  integrity sha512-pu675yvBGFhAHD8D7uj7C0NaHqDxD7mlgmLM8ziyYL4kYH79fil7rJ6U5X2oaKxoyRk3rxxb9k1fG0CCc7CquA==
+
+"@fontsource/raleway@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/raleway/-/raleway-4.4.5.tgz#483871d82aa35e6a981b370b44dbae0b9d59ca6c"
+  integrity sha512-jUOBbqGDadtgjtxQe8YpaiRVy9WGIBUCfQoKlfyuBuI6BNHg1HdsBwL9qetru99uGTTy9L0fEwYoY9J8lhNXqw==
+
+"@fontsource/roboto-mono@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/roboto-mono/-/roboto-mono-4.4.5.tgz#63c54719a8d592d793632501a56d570572c17065"
+  integrity sha512-y9fbKH1eCCkcqtRGhevFlmR5schCRz1GiTT/VYz6z8Ij0WHW0tS26BhMyXhmSgEIiFt+254yS8teqP+cc7Xq0w==
+
+"@fontsource/roboto@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.4.5.tgz#9ef1b9288ac4a97b97906da920c63c08dba97c89"
+  integrity sha512-e3s7BF8MDBLpkA2r6lnl5PMnllF0McVvpolK9h2zzvVJw2WPexP1GTgMKHISlglYZRij2lKg/ZjQcIUUYDsAXg==
+
+"@fontsource/source-code-pro@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/source-code-pro/-/source-code-pro-4.4.5.tgz#927ac48da9a7de6054b44b2e1979abd908f00f47"
+  integrity sha512-PuoF6JHR+s3IaceWMZB02fHLIOehIAfYQUGHXBEyuPQ+BdonbbYpS+RIyduKsUYt7ajZURysbBFlg/N0cb/Jhw==
+
+"@fontsource/space-mono@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/space-mono/-/space-mono-4.4.5.tgz#2acc2f08c5060bc983bfbf14f8827770a5ce3c2a"
+  integrity sha512-QBU55UPj+JOzkwI/ezR1y1qBk+iJtYhjxBTHqXJMTrKCXIuWMD+NIXvVYHIJW8i6Aov+6kOksgVOqrU7rqOK8Q==
+
+"@fontsource/ubuntu-mono@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/ubuntu-mono/-/ubuntu-mono-4.4.5.tgz#2653f4eedf89f4efa3708b6cdc990c06f90b5faf"
+  integrity sha512-TvkybOBDDfZwrIE9imcoMMi4xhdZY+0Q1gmmdrGCG2xFxPM2/fuHXBcn6FKLvm5/regKKq93DO6JIPcH2G1r/w==
+
+"@fontsource/ubuntu@^4.4.5":
+  version "4.4.5"
+  resolved "https://registry.yarnpkg.com/@fontsource/ubuntu/-/ubuntu-4.4.5.tgz#4d386755a4f3f88bfbc6a9d517f33dcdf263f992"
+  integrity sha512-dK/WdBncYJAZrwhANBGYN7wKrB2+NsYX/QomNBkJuze9kel9uijz7cNQPYdUDogink/X8D4MF2PeLEhF565fSA==
+
 "@hapi/address@^2.1.2":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -1985,10 +2060,10 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
-esbuild@^0.12.5:
-  version "0.12.9"
-  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.9.tgz#bed4e7087c286cd81d975631f77d47feb1660070"
-  integrity sha512-MWRhAbMOJ9RJygCrt778rz/qNYgA4ZVj6aXnNPxFjs7PmIpb0fuB9Gmg5uWrr6n++XKwwm/RmSz6RR5JL2Ocsw==
+esbuild@^0.11.19:
+  version "0.11.23"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8"
+  integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q==
 
 escalade@^3.1.1:
   version "3.1.1"
@@ -3119,7 +3194,7 @@ postcss-value-parser@^4.0.2:
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
   integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
 
-postcss@^8.3.0:
+postcss@^8.2.1:
   version "8.3.5"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
   integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==
@@ -3976,13 +4051,13 @@ vite-plugin-pwa@^0.8.1:
     workbox-build "^6.1.5"
     workbox-window "^6.1.5"
 
-vite@^2.3.7:
-  version "2.3.7"
-  resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.7.tgz#3023892419367465e1af1739578f8663d04243b2"
-  integrity sha512-Y0xRz11MPYu/EAvzN94+FsOZHbSvO6FUvHv127CyG7mV6oDoay2bw+g5y9wW3Blf8OY3chaz3nc/DcRe1IQ3Nw==
+"vite@npm:@insertish/vite@2.2.4-dynamic-import-css-f428476":
+  version "2.2.4-dynamic-import-css-f428476"
+  resolved "https://registry.yarnpkg.com/@insertish/vite/-/vite-2.2.4-dynamic-import-css-f428476.tgz#33e0de5a3504c90d900e32c8536e0567dda9de17"
+  integrity sha512-rUKEbkNbUUNbVt5pb1OiHnkt09d+IiHfEOHGexSYmYKGEIPxQAKiGjrfvjpqH0Dzb0B5BbQ+FI23QmNKbari2Q==
   dependencies:
-    esbuild "^0.12.5"
-    postcss "^8.3.0"
+    esbuild "^0.11.19"
+    postcss "^8.2.1"
     resolve "^1.19.0"
     rollup "^2.38.5"
   optionalDependencies: