about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-10-29 21:14:54 +0000
committerGitHub <noreply@github.com>2024-10-29 21:14:54 +0000
commitc8f264b78b1dfb95f68bfb820bd012828cd5fddc (patch)
treeeca795959b8980d14a19169be8f0e71850bfc091
parentab492cd77a2588c58899793d5a51c7d4dd0a4968 (diff)
downloadvoidsky-c8f264b78b1dfb95f68bfb820bd012828cd5fddc.tar.zst
Settings revamp (#5745)
* start building storybook

* add title

* add some styles

* try out new icons

* more settings list component parts

* make text do the spacing

* clean up storybook

* gated new settings screen

* switch account

* add current profile

* use Layout.Screen

* Layout.Header and Layout.Content

* translate helpdesk text

thanks @surfdude29!

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* add account settings

* undo changes to export car dialog

* privacy and security screen

* Translate protect account stuff

Thanks @surfdude29!

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* content and media settings

* about settings

* 2fa copy

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* a11y and appearance

* use new components for appearance settings

* redesign accessibility settings

* Update ContentAndMediaSettings.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* add divider

* remove a11y and appearance middleman screen

* fix web settingslist styles

* new SettingsList.Group component

* explain how portal magic works

* hide pwioptout in old location

* Update Settings.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* replace gate with `IS_INTERNAL`

* add IS_INTERNAL to app-info.web

* fix profile area growing

* add close button to switch account

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
-rw-r--r--assets/icons/bubbles_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/car_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/circleQuestion_stroke2_corner2_rounded.svg2
-rw-r--r--assets/icons/codeBrackets_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/codeLines_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/earth_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/freeze_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/haptic_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/home_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/key_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/macintosh_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/newspaper_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/pencilLine_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/personGroup_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/raisingHand4finger_stroke2_corner2_rounded.svg1
-rw-r--r--assets/icons/wrench_stroke2_corner2_rounded.svg1
-rw-r--r--bskyweb/cmd/bskyweb/server.go4
-rw-r--r--src/Navigation.tsx38
-rw-r--r--src/alf/atoms.ts3
-rw-r--r--src/components/Layout.tsx85
-rw-r--r--src/components/dialogs/BirthDateSettings.tsx2
-rw-r--r--src/components/dialogs/SwitchAccount.tsx2
-rw-r--r--src/components/icons/Bubble.tsx4
-rw-r--r--src/components/icons/Car.tsx5
-rw-r--r--src/components/icons/CircleQuestion.tsx2
-rw-r--r--src/components/icons/CodeBrackets.tsx4
-rw-r--r--src/components/icons/CodeLines.tsx5
-rw-r--r--src/components/icons/Freeze.tsx5
-rw-r--r--src/components/icons/Globe.tsx4
-rw-r--r--src/components/icons/Haptic.tsx5
-rw-r--r--src/components/icons/Home.tsx6
-rw-r--r--src/components/icons/Key.tsx5
-rw-r--r--src/components/icons/Macintosh.tsx5
-rw-r--r--src/components/icons/Newspaper.tsx5
-rw-r--r--src/components/icons/Pencil.tsx4
-rw-r--r--src/components/icons/Person.tsx4
-rw-r--r--src/components/icons/RaisingHand.tsx6
-rw-r--r--src/components/icons/Wrench.tsx5
-rw-r--r--src/lib/app-info.web.ts1
-rw-r--r--src/lib/routes/types.ts6
-rw-r--r--src/routes.ts17
-rw-r--r--src/screens/Moderation/index.tsx27
-rw-r--r--src/screens/Settings/AboutSettings.tsx78
-rw-r--r--src/screens/Settings/AccessibilitySettings.tsx113
-rw-r--r--src/screens/Settings/AccountSettings.tsx180
-rw-r--r--src/screens/Settings/AppearanceSettings.tsx165
-rw-r--r--src/screens/Settings/ContentAndMediaSettings.tsx104
-rw-r--r--src/screens/Settings/PrivacyAndSecuritySettings.tsx91
-rw-r--r--src/screens/Settings/Settings.tsx282
-rw-r--r--src/screens/Settings/components/Email2FAToggle.tsx66
-rw-r--r--src/screens/Settings/components/PwiOptOut.tsx100
-rw-r--r--src/screens/Settings/components/SettingsList.tsx300
-rw-r--r--src/view/com/modals/DeleteAccount.tsx13
-rw-r--r--src/view/screens/AccessibilitySettings.tsx12
-rw-r--r--src/view/screens/Settings/DisableEmail2FADialog.tsx12
-rw-r--r--src/view/screens/Settings/ExportCarDialog.tsx2
-rw-r--r--src/view/screens/Settings/index.tsx13
-rw-r--r--src/view/screens/Storybook/Settings.tsx134
-rw-r--r--src/view/screens/Storybook/index.tsx3
59 files changed, 1811 insertions, 133 deletions
diff --git a/assets/icons/bubbles_stroke2_corner2_rounded.svg b/assets/icons/bubbles_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..f09fcaa33
--- /dev/null
+++ b/assets/icons/bubbles_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M6.002 6a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-1v1a3 3 0 0 1-3 3h-4.24l-4.274 2.374a1 1 0 0 1-1.486-.874V19a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3h1V6Zm-1 3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h1a1 1 0 0 1 1 1v.8l3.015-1.674a1 1 0 0 1 .485-.126h4.5a1 1 0 0 0 1-1v-1.933a1 1 0 0 1 0-.134V10a1 1 0 0 0-1-1h-10Zm13 4v-3a3 3 0 0 0-3-3h-7V6a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-1Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/car_stroke2_corner2_rounded.svg b/assets/icons/car_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..ca29cefd1
--- /dev/null
+++ b/assets/icons/car_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M7.018 6a1 1 0 0 0-.808.412l-.81-.588.809.588L3 10.825V17a1 1 0 1 0 2 0 1 1 0 0 1 1-1h12a1 1 0 0 1 1 1 1 1 0 1 0 2 0v-5.998l-3.22-4.577A1 1 0 0 0 16.962 6H7.018ZM23 11.686V17a3 3 0 0 1-5.83 1H6.83A3.001 3.001 0 0 1 1 17v-5.5a1 1 0 1 1 0-2h.49l3.102-4.265A3 3 0 0 1 7.018 4h9.944a3 3 0 0 1 2.453 1.274l3.104 4.412H23a1 1 0 1 1 0 2ZM5 13a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Zm10 0a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/circleQuestion_stroke2_corner2_rounded.svg b/assets/icons/circleQuestion_stroke2_corner2_rounded.svg
index a534f9871..c025a1450 100644
--- a/assets/icons/circleQuestion_stroke2_corner2_rounded.svg
+++ b/assets/icons/circleQuestion_stroke2_corner2_rounded.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Z" clip-rule="evenodd"/><path fill="#000" fill-rule="evenodd" d="M12 9a1 1 0 0 0-.879.522 1 1 0 0 1-1.754-.96A3 3 0 0 1 12 7c1.515 0 2.567 1.006 2.866 2.189.302 1.189-.156 2.574-1.524 3.258A.62.62 0 0 0 13 13a1 1 0 1 1-2 0c0-.992.56-1.898 1.447-2.342.455-.227.572-.618.48-.978C12.836 9.314 12.529 9 12 9Z" clip-rule="evenodd"/><path fill="#000" d="M13 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm10-3a1 1 0 0 0-.879.522 1 1 0 0 1-1.754-.96A3 3 0 0 1 12 7c1.515 0 2.567 1.006 2.866 2.189.302 1.189-.156 2.574-1.524 3.258A.62.62 0 0 0 13 13a1 1 0 1 1-2 0c0-.992.56-1.898 1.447-2.342.455-.227.572-.618.48-.978C12.836 9.314 12.529 9 12 9Zm1 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></svg>
diff --git a/assets/icons/codeBrackets_stroke2_corner2_rounded.svg b/assets/icons/codeBrackets_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..d40e7b003
--- /dev/null
+++ b/assets/icons/codeBrackets_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M14.243 3.03a1 1 0 0 1 .727 1.213l-4 16a1 1 0 1 1-1.94-.485l4-16a1 1 0 0 1 1.213-.728ZM6.707 7.293a1 1 0 0 1 0 1.414l-2.586 2.586a1 1 0 0 0 0 1.414l2.586 2.586a1 1 0 1 1-1.414 1.414l-2.586-2.586a3 3 0 0 1 0-4.242l2.586-2.586a1 1 0 0 1 1.414 0Zm10.586 0a1 1 0 0 1 1.414 0l2.586 2.586a3 3 0 0 1 0 4.242l-2.586 2.586a1 1 0 1 1-1.414-1.414l2.586-2.586a1 1 0 0 0 0-1.414l-2.586-2.586a1 1 0 0 1 0-1.414Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/codeLines_stroke2_corner2_rounded.svg b/assets/icons/codeLines_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..65a7b1e85
--- /dev/null
+++ b/assets/icons/codeLines_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M2 5a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm15 0a1 1 0 0 1 1-1h3a1 1 0 1 1 0 2h-3a1 1 0 0 1-1-1ZM2 12a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm10 0a1 1 0 0 1 1-1h8a1 1 0 1 1 0 2h-8a1 1 0 0 1-1-1ZM2 19a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm12 0a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/earth_stroke2_corner2_rounded.svg b/assets/icons/earth_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..b8922629a
--- /dev/null
+++ b/assets/icons/earth_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M4.4 9.493C4.14 10.28 4 11.124 4 12a8 8 0 1 0 10.899-7.459l-.67 2.679a2.95 2.95 0 0 1-2.14 2.142l-2.173.547a.32.32 0 0 0-.205.164 2.316 2.316 0 0 1-3.457.81L4.4 9.493Zm.883-1.84 2.171 1.63a.315.315 0 0 0 .471-.11c.303-.6.851-1.04 1.503-1.204l2.174-.546a.95.95 0 0 0 .687-.688l.97.242-.97-.242.67-2.678a7.99 7.99 0 0 0-7.676 3.597ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm8.048.543a2.2 2.2 0 0 1 1.69-.636l.827.053c.52.033 1.023.204 1.456.495l1.37.921a2.453 2.453 0 0 1-1.367 4.489h-.98a2.95 2.95 0 0 1-2.45-1.312L9.77 15.32a2.2 2.2 0 0 1 .278-2.776Zm1.563 1.36a.197.197 0 0 0-.177.306l.823 1.235c.176.263.471.42.787.42h.98a.453.453 0 0 0 .252-.828l-1.37-.921a.95.95 0 0 0-.468-.159l-.827-.053Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/freeze_stroke2_corner2_rounded.svg b/assets/icons/freeze_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..109145478
--- /dev/null
+++ b/assets/icons/freeze_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M8.789 2.293a1 1 0 0 1 1.414 0l1.793 1.793 1.793-1.793a1 1 0 1 1 1.414 1.414l-2.207 2.207v4.355l3.772-2.178.808-3.015a1 1 0 1 1 1.931.518l-.656 2.449 2.45.656a1 1 0 1 1-.518 1.932l-3.015-.808L13.998 12l3.77 2.177 3.015-.808a1 1 0 1 1 .517 1.932l-2.449.656.657 2.45a1 1 0 1 1-1.932.517l-.808-3.015-3.772-2.178v4.355l2.207 2.207a1 1 0 0 1-1.414 1.414l-1.793-1.793-1.793 1.793a1 1 0 0 1-1.414-1.414l2.207-2.207v-4.353l-3.77 2.176-.807 3.015a1 1 0 0 1-1.932-.518l.656-2.449-2.449-.656a1 1 0 1 1 .518-1.932l3.015.808L9.997 12l-3.77-2.177-3.015.808a1 1 0 0 1-.518-1.932l2.45-.656-.657-2.45a1 1 0 0 1 1.932-.517l.808 3.015 3.77 2.176V5.914L8.788 3.707a1 1 0 0 1 0-1.414Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/haptic_stroke2_corner2_rounded.svg b/assets/icons/haptic_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..7a93daf66
--- /dev/null
+++ b/assets/icons/haptic_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M9 5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H9ZM6 6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6Zm4 1a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Zm-5.793.293a1 1 0 0 1 0 1.414L3.155 9.76a.546.546 0 0 0-.05.713 2.55 2.55 0 0 1 0 3.056.546.546 0 0 0 .05.713l1.052 1.052a1 1 0 1 1-1.414 1.414L1.74 15.655a2.546 2.546 0 0 1-.237-3.327.55.55 0 0 0 0-.655 2.546 2.546 0 0 1 .237-3.328l1.052-1.052a1 1 0 0 1 1.414 0Zm15.586 0a1 1 0 0 1 1.414 0l1.052 1.052c.896.896.997 2.314.237 3.327a.55.55 0 0 0 0 .656 2.546 2.546 0 0 1-.237 3.327l-1.052 1.052a1 1 0 0 1-1.414-1.414l1.052-1.052a.546.546 0 0 0 .05-.713 2.55 2.55 0 0 1 0-3.056.546.546 0 0 0-.05-.713l-1.052-1.052a1 1 0 0 1 0-1.414Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/home_stroke2_corner2_rounded.svg b/assets/icons/home_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..5665b24b8
--- /dev/null
+++ b/assets/icons/home_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12.659 3.905a1 1 0 0 0-1.318 0l-6 5.25A1 1 0 0 0 5 9.907V18a1 1 0 0 0 1 1h3v-3a3 3 0 0 1 6 0v3h3a1 1 0 0 0 1-1V9.907a1 1 0 0 0-.341-.752l-6-5.25ZM10.024 2.4a3 3 0 0 1 3.952 0l6 5.25A3 3 0 0 1 21 9.907V18a3 3 0 0 1-3 3h-3a2 2 0 0 1-2-2v-3a1 1 0 0 0-2 0v3a2 2 0 0 1-2 2H6a3 3 0 0 1-3-3V9.907A3 3 0 0 1 4.024 7.65l6-5.25Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/key_stroke2_corner2_rounded.svg b/assets/icons/key_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..2beb2b4ae
--- /dev/null
+++ b/assets/icons/key_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M3 12a4 4 0 0 1 7.212-2.385c.363.488.963.885 1.696.885h8.111l1.2 1.5-1.2 1.5h-1.783l-1.789-.894a1 1 0 0 0-.894 0l-1.79.894h-1.855c-.733 0-1.333.397-1.696.885A4 4 0 0 1 3 12Zm4-6a6 6 0 1 0 4.817 9.579.3.3 0 0 1 .076-.072l.017-.007H14a1 1 0 0 0 .447-.106L16 14.618l1.553.776c.139.07.292.106.447.106h2.02a2 2 0 0 0 1.561-.75l1.2-1.5a2 2 0 0 0 0-2.5l-1.2-1.5a2 2 0 0 0-1.562-.75h-8.11l-.016-.007a.3.3 0 0 1-.077-.071A6 6 0 0 0 7 6Zm0 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/macintosh_stroke2_corner2_rounded.svg b/assets/icons/macintosh_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..543531972
--- /dev/null
+++ b/assets/icons/macintosh_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M4 5a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v11a3 3 0 0 1-1 2.236V20a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.764c-.614-.55-1-1.348-1-2.236V5Zm3 14v1h10v-1H7ZM7 4a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7Zm0 2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V6Zm2 1v4h6V7H9Zm4 8a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/newspaper_stroke2_corner2_rounded.svg b/assets/icons/newspaper_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..3eb823cf2
--- /dev/null
+++ b/assets/icons/newspaper_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M1 6.5A2.5 2.5 0 0 1 3.5 4H9a4 4 0 0 1 3 1.354A4 4 0 0 1 15 4h5.5A2.5 2.5 0 0 1 23 6.5v11a2.5 2.5 0 0 1-2.5 2.5h-5.223c-.52 0-1 .125-1.4.372-.421.26-.761.633-.983 1.075a1 1 0 0 1-1.788 0 2.66 2.66 0 0 0-.983-1.075c-.4-.247-.88-.372-1.4-.372H3.5A2.5 2.5 0 0 1 1 17.5v-11ZM11 8a2 2 0 0 0-2-2H3.5a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h5.223c.776 0 1.564.173 2.277.569V8Zm2 10.569A4.7 4.7 0 0 1 15.277 18H20.5a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5H15a2 2 0 0 0-2 2v10.569Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/pencilLine_stroke2_corner2_rounded.svg b/assets/icons/pencilLine_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..92f1fed1c
--- /dev/null
+++ b/assets/icons/pencilLine_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M15.379 2.707a3 3 0 0 1 4.242 0l1.672 1.672a3 3 0 0 1 0 4.242l-12.5 12.5A3 3 0 0 1 6.672 22H3a1 1 0 0 1-1-1v-3.672a3 3 0 0 1 .879-2.121l12.5-12.5Zm2.828 1.414a1 1 0 0 0-1.414 0l-12.5 12.5a1 1 0 0 0-.293.707V20h2.672a1 1 0 0 0 .707-.293l12.5-12.5.707.707-.707-.707a1 1 0 0 0 0-1.414L18.207 4.12ZM13 21a1 1 0 0 1 1-1h7a1 1 0 0 1 0 2h-7a1 1 0 0 1-1-1Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/personGroup_stroke2_corner2_rounded.svg b/assets/icons/personGroup_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..fad8a8e7f
--- /dev/null
+++ b/assets/icons/personGroup_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M8 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM4 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm13-1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm-3.5 1.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Zm7.301 9.7c-.836-2.6-2.88-3.503-4.575-3.111a1 1 0 0 1-.451-1.949c2.815-.651 5.81.966 6.93 4.448a2.49 2.49 0 0 1-.506 2.43A2.92 2.92 0 0 1 20 20h-2a1 1 0 1 1 0-2h2a.92.92 0 0 0 .69-.295.49.49 0 0 0 .112-.505ZM8 14c-1.865 0-3.878 1.274-4.681 4.151a.57.57 0 0 0 .132.55c.15.171.4.299.695.299h7.708a.93.93 0 0 0 .695-.299.57.57 0 0 0 .132-.55C11.878 15.274 9.865 14 8 14Zm0-2c2.87 0 5.594 1.98 6.607 5.613.53 1.9-1.09 3.387-2.753 3.387H4.146c-1.663 0-3.283-1.487-2.753-3.387C2.406 13.981 5.129 12 8 12Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/raisingHand4finger_stroke2_corner2_rounded.svg b/assets/icons/raisingHand4finger_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..f6beb5647
--- /dev/null
+++ b/assets/icons/raisingHand4finger_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12.5 4a.5.5 0 0 0-.5.5V10a1 1 0 1 1-2 0V5.5a.5.5 0 0 0-1 0V11a1 1 0 1 1-2 0V7.5a.5.5 0 0 0-1 0v6a6.5 6.5 0 1 0 13 0V9a1.996 1.996 0 0 0-2 2v.838c0 .826-.529 1.559-1.312 1.82A2.47 2.47 0 0 0 14 16a1 1 0 1 1-2 0 4.47 4.47 0 0 1 3-4.22V11c0-1.014.379-1.941 1-2.646V5.5a.5.5 0 0 0-1 0V10a1 1 0 1 1-2 0V4.5a.5.5 0 0 0-.5-.5Zm2.112-.838A2.5 2.5 0 0 1 18 5.5v1.626q.481-.124 1-.126a2 2 0 0 1 2 2v4.5a8.5 8.5 0 0 1-17 0v-6a2.5 2.5 0 0 1 3.039-2.442 2.5 2.5 0 0 1 3.349-1.896 2.498 2.498 0 0 1 4.224 0Z" clip-rule="evenodd"/></svg>
diff --git a/assets/icons/wrench_stroke2_corner2_rounded.svg b/assets/icons/wrench_stroke2_corner2_rounded.svg
new file mode 100644
index 000000000..601614c88
--- /dev/null
+++ b/assets/icons/wrench_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M14.5 4a5.5 5.5 0 0 0-5.078 7.616 1 1 0 0 1-.216 1.092L4.37 17.543a1 1 0 0 0 0 1.414l.672.672a1 1 0 0 0 1.414 0l4.835-4.835a1 1 0 0 1 1.092-.216A5.5 5.5 0 0 0 20 9.414l-1.293 1.293a3.828 3.828 0 1 1-5.414-5.414L14.585 4H14.5ZM7 9.5a7.5 7.5 0 0 1 9.969-7.084 1 1 0 0 1 .378 1.651l-2.64 2.64a1.829 1.829 0 0 0 2.586 2.586l2.64-2.64a1 1 0 0 1 1.65.378 7.5 7.5 0 0 1-9.328 9.627l-4.384 4.385a3 3 0 0 1-4.242 0l-.672-.672a3 3 0 0 1 0-4.242l4.385-4.385A7.5 7.5 0 0 1 7 9.5Z" clip-rule="evenodd"/></svg>
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 12b7b8960..d0d6b4c49 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -253,6 +253,10 @@ func serve(cctx *cli.Context) error {
 	e.GET("/settings/external-embeds", server.WebGeneric)
 	e.GET("/settings/accessibility", server.WebGeneric)
 	e.GET("/settings/appearance", server.WebGeneric)
+	e.GET("/settings/account", server.WebGeneric)
+	e.GET("/settings/privacy-and-security", server.WebGeneric)
+	e.GET("/settings/content-and-media", server.WebGeneric)
+	e.GET("/settings/about", server.WebGeneric)
 	e.GET("/sys/debug", server.WebGeneric)
 	e.GET("/sys/debug-mod", server.WebGeneric)
 	e.GET("/sys/log", server.WebGeneric)
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 81d08c7da..436f2488a 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -95,6 +95,10 @@ import {Wizard} from '#/screens/StarterPack/Wizard'
 import {useTheme} from '#/alf'
 import {router} from '#/routes'
 import {Referrer} from '../modules/expo-bluesky-swiss-army'
+import {AboutSettingsScreen} from './screens/Settings/AboutSettings'
+import {AccountSettingsScreen} from './screens/Settings/AccountSettings'
+import {ContentAndMediaSettingsScreen} from './screens/Settings/ContentAndMediaSettings'
+import {PrivacyAndSecuritySettingsScreen} from './screens/Settings/PrivacyAndSecuritySettings'
 
 const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
 
@@ -322,7 +326,39 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
         name="AppearanceSettings"
         getComponent={() => AppearanceSettingsScreen}
         options={{
-          title: title(msg`Appearance Settings`),
+          title: title(msg`Appearance`),
+          requireAuth: true,
+        }}
+      />
+      <Stack.Screen
+        name="AccountSettings"
+        getComponent={() => AccountSettingsScreen}
+        options={{
+          title: title(msg`Account`),
+          requireAuth: true,
+        }}
+      />
+      <Stack.Screen
+        name="PrivacyAndSecuritySettings"
+        getComponent={() => PrivacyAndSecuritySettingsScreen}
+        options={{
+          title: title(msg`Privacy and Security`),
+          requireAuth: true,
+        }}
+      />
+      <Stack.Screen
+        name="ContentAndMediaSettings"
+        getComponent={() => ContentAndMediaSettingsScreen}
+        options={{
+          title: title(msg`Content and Media`),
+          requireAuth: true,
+        }}
+      />
+      <Stack.Screen
+        name="AboutSettings"
+        getComponent={() => AboutSettingsScreen}
+        options={{
+          title: title(msg`About`),
           requireAuth: true,
         }}
       />
diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts
index ca4c14dc6..434420a97 100644
--- a/src/alf/atoms.ts
+++ b/src/alf/atoms.ts
@@ -102,6 +102,9 @@ export const atoms = {
   /*
    * Flex
    */
+  gap_0: {
+    gap: 0,
+  },
   gap_2xs: {
     gap: tokens.space._2xs,
   },
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index 57e373164..ea11e2217 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -1,16 +1,22 @@
-import React from 'react'
+import React, {useContext, useMemo} from 'react'
 import {View, ViewStyle} from 'react-native'
 import {StyleProp} from 'react-native'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 
+import {ViewHeader} from '#/view/com/util/ViewHeader'
+import {ScrollView} from '#/view/com/util/Views'
+import {CenteredView} from '#/view/com/util/Views'
 import {atoms as a} from '#/alf'
 
 // Every screen should have a Layout component wrapping it.
 // This component provides a default padding for the top of the screen.
 // This allows certain screens to avoid the top padding if they want to.
-//
-// In a future PR I will add a unified header component to this file and
-// things like a preconfigured scrollview.
+
+const LayoutContext = React.createContext({
+  withinScreen: false,
+  topPaddingDisabled: false,
+  withinScrollView: false,
+})
 
 /**
  * Every screen should have a Layout.Screen component wrapping it.
@@ -18,7 +24,7 @@ import {atoms as a} from '#/alf'
  * and height/minHeight
  */
 let Screen = ({
-  disableTopPadding,
+  disableTopPadding = false,
   style,
   ...props
 }: React.ComponentProps<typeof View> & {
@@ -26,16 +32,69 @@ let Screen = ({
   style?: StyleProp<ViewStyle>
 }): React.ReactNode => {
   const {top} = useSafeAreaInsets()
+  const context = useMemo(
+    () => ({
+      withinScreen: true,
+      topPaddingDisabled: disableTopPadding,
+      withinScrollView: false,
+    }),
+    [disableTopPadding],
+  )
   return (
-    <View
-      style={[
-        {paddingTop: disableTopPadding ? 0 : top},
-        a.util_screen_outer,
-        style,
-      ]}
-      {...props}
-    />
+    <LayoutContext.Provider value={context}>
+      <View
+        style={[
+          {paddingTop: disableTopPadding ? 0 : top},
+          a.util_screen_outer,
+          style,
+        ]}
+        {...props}
+      />
+    </LayoutContext.Provider>
   )
 }
 Screen = React.memo(Screen)
 export {Screen}
+
+let Header = (
+  props: React.ComponentProps<typeof ViewHeader>,
+): React.ReactNode => {
+  const {withinScrollView} = useContext(LayoutContext)
+  if (!withinScrollView) {
+    return (
+      <CenteredView topBorder={false} sideBorders>
+        <ViewHeader showOnDesktop showBorder {...props} />
+      </CenteredView>
+    )
+  } else {
+    return <ViewHeader showOnDesktop showBorder {...props} />
+  }
+}
+Header = React.memo(Header)
+export {Header}
+
+let Content = ({
+  style,
+  contentContainerStyle,
+  ...props
+}: React.ComponentProps<typeof ScrollView> & {
+  style?: StyleProp<ViewStyle>
+  contentContainerStyle?: StyleProp<ViewStyle>
+}): React.ReactNode => {
+  const context = useContext(LayoutContext)
+  const newContext = useMemo(
+    () => ({...context, withinScrollView: true}),
+    [context],
+  )
+  return (
+    <LayoutContext.Provider value={newContext}>
+      <ScrollView
+        style={[a.flex_1, style]}
+        contentContainerStyle={[{paddingBottom: 100}, contentContainerStyle]}
+        {...props}
+      />
+    </LayoutContext.Provider>
+  )
+}
+Content = React.memo(Content)
+export {Content}
diff --git a/src/components/dialogs/BirthDateSettings.tsx b/src/components/dialogs/BirthDateSettings.tsx
index 81d0c6740..8f47d05b0 100644
--- a/src/components/dialogs/BirthDateSettings.tsx
+++ b/src/components/dialogs/BirthDateSettings.tsx
@@ -29,7 +29,7 @@ export function BirthDateSettingsDialog({
   const {isLoading, error, data: preferences} = usePreferencesQuery()
 
   return (
-    <Dialog.Outer control={control}>
+    <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
       <Dialog.Handle />
       <Dialog.ScrollableInner label={_(msg`My Birthday`)}>
         <View style={[a.gap_sm, a.pb_lg]}>
diff --git a/src/components/dialogs/SwitchAccount.tsx b/src/components/dialogs/SwitchAccount.tsx
index ea870e2da..daad01d2a 100644
--- a/src/components/dialogs/SwitchAccount.tsx
+++ b/src/components/dialogs/SwitchAccount.tsx
@@ -56,6 +56,8 @@ export function SwitchAccountDialog({
             pendingDid={pendingDid}
           />
         </View>
+
+        <Dialog.Close />
       </Dialog.ScrollableInner>
     </Dialog.Outer>
   )
diff --git a/src/components/icons/Bubble.tsx b/src/components/icons/Bubble.tsx
index ff6da2531..3654fcf78 100644
--- a/src/components/icons/Bubble.tsx
+++ b/src/components/icons/Bubble.tsx
@@ -11,3 +11,7 @@ export const Bubble_Stroke2_Corner2_Rounded = createSinglePathSVG({
 export const Bubble_Stroke2_Corner3_Rounded = createSinglePathSVG({
   path: 'M2.002 7a4 4 0 0 1 4-4h12a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H12.28l-4.762 2.858A1 1 0 0 1 6.002 21v-2a4 4 0 0 1-4-4V7Zm4-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h1a1 1 0 0 1 1 1v1.234l3.486-2.092a1 1 0 0 1 .514-.142h6a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-12Z',
 })
+
+export const Bubbles_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M6.002 6a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-1v1a3 3 0 0 1-3 3h-4.24l-4.274 2.374a1 1 0 0 1-1.486-.874V19a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3h1V6Zm-1 3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h1a1 1 0 0 1 1 1v.8l3.015-1.674a1 1 0 0 1 .485-.126h4.5a1 1 0 0 0 1-1v-1.933a1 1 0 0 1 0-.134V10a1 1 0 0 0-1-1h-10Zm13 4v-3a3 3 0 0 0-3-3h-7V6a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-1Z',
+})
diff --git a/src/components/icons/Car.tsx b/src/components/icons/Car.tsx
new file mode 100644
index 000000000..25859176a
--- /dev/null
+++ b/src/components/icons/Car.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Car_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M7.018 6a1 1 0 0 0-.808.412L5.4 5.824l.809.588L3 10.825V17a1 1 0 1 0 2 0 1 1 0 0 1 1-1h12a1 1 0 0 1 1 1 1 1 0 1 0 2 0v-5.998l-3.22-4.577A1 1 0 0 0 16.962 6H7.018ZM23 11.686V17a3 3 0 0 1-5.83 1H6.83A3.001 3.001 0 0 1 1 17v-5.5a1 1 0 1 1 0-2h.49l3.102-4.265A3 3 0 0 1 7.018 4h9.944a3 3 0 0 1 2.453 1.274l3.104 4.412H23a1 1 0 1 1 0 2ZM5 13a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Zm10 0a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Z',
+})
diff --git a/src/components/icons/CircleQuestion.tsx b/src/components/icons/CircleQuestion.tsx
index 4eb369379..6242a2b51 100644
--- a/src/components/icons/CircleQuestion.tsx
+++ b/src/components/icons/CircleQuestion.tsx
@@ -1,5 +1,5 @@
 import {createSinglePathSVG} from './TEMPLATE'
 
 export const CircleQuestion_Stroke2_Corner2_Rounded = createSinglePathSVG({
-  path: 'M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Z" clip-rule="evenodd"/><path fill="#000" fill-rule="evenodd" d="M12 9a1 1 0 0 0-.879.522 1 1 0 0 1-1.754-.96A3 3 0 0 1 12 7c1.515 0 2.567 1.006 2.866 2.189.302 1.189-.156 2.574-1.524 3.258A.62.62 0 0 0 13 13a1 1 0 1 1-2 0c0-.992.56-1.898 1.447-2.342.455-.227.572-.618.48-.978C12.836 9.314 12.529 9 12 9Z',
+  path: 'M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Z M12 9a1 1 0 0 0-.879.522 1 1 0 0 1-1.754-.96A3 3 0 0 1 12 7c1.515 0 2.567 1.006 2.866 2.189.302 1.189-.156 2.574-1.524 3.258A.62.62 0 0 0 13 13a1 1 0 1 1-2 0c0-.992.56-1.898 1.447-2.342.455-.227.572-.618.48-.978C12.836 9.314 12.529 9 12 9Z M13 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z',
 })
diff --git a/src/components/icons/CodeBrackets.tsx b/src/components/icons/CodeBrackets.tsx
index 59d5fca90..98c679f04 100644
--- a/src/components/icons/CodeBrackets.tsx
+++ b/src/components/icons/CodeBrackets.tsx
@@ -3,3 +3,7 @@ import {createSinglePathSVG} from './TEMPLATE'
 export const CodeBrackets_Stroke2_Corner0_Rounded = createSinglePathSVG({
   path: 'M14.242 3.03a1 1 0 0 1 .728 1.213l-4 16a1 1 0 1 1-1.94-.485l4-16a1 1 0 0 1 1.213-.728ZM6.707 7.293a1 1 0 0 1 0 1.414L3.414 12l3.293 3.293a1 1 0 1 1-1.414 1.414l-4-4a1 1 0 0 1 0-1.414l4-4a1 1 0 0 1 1.414 0Zm10.586 0a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1 0 1.414l-4 4a1 1 0 1 1-1.414-1.414L20.586 12l-3.293-3.293a1 1 0 0 1 0-1.414Z',
 })
+
+export const CodeBrackets_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M14.243 3.03a1 1 0 0 1 .727 1.213l-4 16a1 1 0 1 1-1.94-.485l4-16a1 1 0 0 1 1.213-.728ZM6.707 7.293a1 1 0 0 1 0 1.414l-2.586 2.586a1 1 0 0 0 0 1.414l2.586 2.586a1 1 0 1 1-1.414 1.414l-2.586-2.586a3 3 0 0 1 0-4.242l2.586-2.586a1 1 0 0 1 1.414 0Zm10.586 0a1 1 0 0 1 1.414 0l2.586 2.586a3 3 0 0 1 0 4.242l-2.586 2.586a1 1 0 1 1-1.414-1.414l2.586-2.586a1 1 0 0 0 0-1.414l-2.586-2.586a1 1 0 0 1 0-1.414Z',
+})
diff --git a/src/components/icons/CodeLines.tsx b/src/components/icons/CodeLines.tsx
new file mode 100644
index 000000000..833dc2316
--- /dev/null
+++ b/src/components/icons/CodeLines.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const CodeLines_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M2 5a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm15 0a1 1 0 0 1 1-1h3a1 1 0 1 1 0 2h-3a1 1 0 0 1-1-1ZM2 12a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm10 0a1 1 0 0 1 1-1h8a1 1 0 1 1 0 2h-8a1 1 0 0 1-1-1ZM2 19a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm12 0a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1Z',
+})
diff --git a/src/components/icons/Freeze.tsx b/src/components/icons/Freeze.tsx
new file mode 100644
index 000000000..628157b4f
--- /dev/null
+++ b/src/components/icons/Freeze.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Freeze_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M8.789 2.293a1 1 0 0 1 1.414 0l1.793 1.793 1.793-1.793a1 1 0 1 1 1.414 1.414l-2.207 2.207v4.355l3.772-2.178.808-3.015a1 1 0 1 1 1.931.518l-.656 2.449 2.45.656a1 1 0 1 1-.518 1.932l-3.015-.808L13.998 12l3.77 2.177 3.015-.808a1 1 0 1 1 .517 1.932l-2.449.656.657 2.45a1 1 0 1 1-1.932.517l-.808-3.015-3.772-2.178v4.355l2.207 2.207a1 1 0 0 1-1.414 1.414l-1.793-1.793-1.793 1.793a1 1 0 0 1-1.414-1.414l2.207-2.207v-4.353l-3.77 2.176-.807 3.015a1 1 0 0 1-1.932-.518l.656-2.449-2.449-.656a1 1 0 1 1 .518-1.932l3.015.808L9.997 12l-3.77-2.177-3.015.808a1 1 0 0 1-.518-1.932l2.45-.656-.657-2.45a1 1 0 0 1 1.932-.517l.808 3.015 3.77 2.176V5.914L8.788 3.707a1 1 0 0 1 0-1.414Z',
+})
diff --git a/src/components/icons/Globe.tsx b/src/components/icons/Globe.tsx
index 53ef84eec..837c7c1a1 100644
--- a/src/components/icons/Globe.tsx
+++ b/src/components/icons/Globe.tsx
@@ -7,3 +7,7 @@ export const Globe_Stroke2_Corner0_Rounded = createSinglePathSVG({
 export const Earth_Stroke2_Corner0_Rounded = createSinglePathSVG({
   path: 'M4.4 9.493C4.14 10.28 4 11.124 4 12a8 8 0 1 0 10.899-7.459l-.953 3.81a1 1 0 0 1-.726.727l-3.444.866-.772 1.533a1 1 0 0 1-1.493.35L4.4 9.493Zm.883-1.84L7.756 9.51l.44-.874a1 1 0 0 1 .649-.52l3.306-.832.807-3.227a7.993 7.993 0 0 0-7.676 3.597ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm8.43.162a1 1 0 0 1 .77-.29l1.89.121a1 1 0 0 1 .494.168l2.869 1.928a1 1 0 0 1 .336 1.277l-.973 1.946a1 1 0 0 1-.894.553h-2.92a1 1 0 0 1-.831-.445L9.225 14.5a1 1 0 0 1 .126-1.262l1.08-1.076Zm.915 1.913.177-.177 1.171.074 1.914 1.286-.303.607h-1.766l-1.194-1.79Z',
 })
+
+export const Earth_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M4.4 9.493C4.14 10.28 4 11.124 4 12a8 8 0 1 0 10.899-7.459l-.67 2.679a2.95 2.95 0 0 1-2.14 2.142l-2.173.547a.32.32 0 0 0-.205.164 2.316 2.316 0 0 1-3.457.81L4.4 9.493Zm.883-1.84 2.171 1.63a.315.315 0 0 0 .471-.11c.303-.6.851-1.04 1.503-1.204l2.174-.546a.95.95 0 0 0 .687-.688l.97.242-.97-.242.67-2.678a7.993 7.993 0 0 0-7.676 3.597ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm8.048.543a2.2 2.2 0 0 1 1.69-.636l.827.053c.52.033 1.023.204 1.456.495l1.37.921a2.453 2.453 0 0 1-1.367 4.489h-.98a2.95 2.95 0 0 1-2.45-1.312L9.77 15.32a2.2 2.2 0 0 1 .278-2.776Zm1.563 1.36a.197.197 0 0 0-.177.306l.823 1.235c.176.263.471.42.787.42h.98a.453.453 0 0 0 .252-.828l-1.37-.921a.95.95 0 0 0-.468-.159l-.827-.053Z',
+})
diff --git a/src/components/icons/Haptic.tsx b/src/components/icons/Haptic.tsx
new file mode 100644
index 000000000..09726e2f1
--- /dev/null
+++ b/src/components/icons/Haptic.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Haptic_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M9 5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H9ZM6 6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6Zm4 1a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Zm-5.793.293a1 1 0 0 1 0 1.414L3.155 9.76a.546.546 0 0 0-.05.713c.678.906.678 2.15 0 3.056a.546.546 0 0 0 .05.713l1.052 1.052a1 1 0 1 1-1.414 1.414L1.74 15.655a2.546 2.546 0 0 1-.237-3.327.55.55 0 0 0 0-.655 2.546 2.546 0 0 1 .237-3.328l1.052-1.052a1 1 0 0 1 1.414 0Zm15.586 0a1 1 0 0 1 1.414 0l1.052 1.052c.896.896.997 2.314.237 3.327a.55.55 0 0 0 0 .656 2.546 2.546 0 0 1-.237 3.327l-1.052 1.052a1 1 0 0 1-1.414-1.414l1.052-1.052a.546.546 0 0 0 .05-.713 2.55 2.55 0 0 1 0-3.056.546.546 0 0 0-.05-.713l-1.052-1.052a1 1 0 0 1 0-1.414Z',
+})
diff --git a/src/components/icons/Home.tsx b/src/components/icons/Home.tsx
index e150b7b81..a5e796e6c 100644
--- a/src/components/icons/Home.tsx
+++ b/src/components/icons/Home.tsx
@@ -1,7 +1,11 @@
 import {createSinglePathSVG} from './TEMPLATE'
 
 export const Home_Stroke2_Corner0_Rounded = createSinglePathSVG({
-  path: 'M11.46 1.362a2 2 0 0 1 1.08 0c.249.07.448.188.611.301.146.102.306.232.467.363l6.421 5.218.046.036c.169.137.38.308.54.53a2 2 0 0 1 .304.64c.073.264.072.536.071.753v9.229c0 .252 0 .498-.017.706a2.023 2.023 0 0 1-.201.77 2 2 0 0 1-.874.874 2.02 2.02 0 0 1-.77.201c-.208.017-.454.017-.706.017H5.568c-.252 0-.498 0-.706-.017a2.02 2.02 0 0 1-.77-.201 2 2 0 0 1-.874-.874 2.022 2.022 0 0 1-.201-.77C3 18.93 3 18.684 3 18.432V9.203c0-.217-.002-.49.07-.754a2 2 0 0 1 .304-.638c.16-.223.372-.394.541-.53l.045-.037 6.422-5.218c.161-.13.321-.26.467-.362.163-.114.362-.232.612-.302Zm.532 1.943c-.077.054-.18.136-.37.29l-6.4 5.2a6.315 6.315 0 0 0-.215.18c-.002 0-.003.002-.004.003v.004C5 9.036 5 9.112 5 9.262V18.4a8.18 8.18 0 0 0 .011.588l.014.002c.116.01.278.01.575.01H8v-5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v5h2.4a8.207 8.207 0 0 0 .589-.012v-.013c.01-.116.011-.279.011-.575V9.262c0-.15 0-.226-.003-.28v-.004l-.003-.003a6.448 6.448 0 0 0-.216-.18l-6.4-5.2a7.373 7.373 0 0 0-.37-.29L12 3.299l-.008.006ZM14 19v-5h-4v5h4Z',
+  path: 'M11.37 1.724a1 1 0 0 1 1.26 0l8 6.5A1 1 0 0 1 21 9v11a1 1 0 0 1-1 1h-6a1 1 0 0 1-1-1v-5h-2v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 .37-.776l8-6.5ZM5 9.476V19h4v-5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v5h4V9.476l-7-5.688-7 5.688Z',
+})
+
+export const Home_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M12.659 3.905a1 1 0 0 0-1.318 0l-6 5.25A1 1 0 0 0 5 9.907V18a1 1 0 0 0 1 1h3v-3a3 3 0 0 1 6 0v3h3a1 1 0 0 0 1-1V9.907a1 1 0 0 0-.341-.752l-6-5.25ZM10.024 2.4a3 3 0 0 1 3.952 0l6 5.25A3 3 0 0 1 21 9.907V18a3 3 0 0 1-3 3h-3a2 2 0 0 1-2-2v-3a1 1 0 0 0-2 0v3a2 2 0 0 1-2 2H6a3 3 0 0 1-3-3V9.907A3 3 0 0 1 4.024 7.65l6-5.25Z',
 })
 
 export const Home_Filled_Corner0_Rounded = createSinglePathSVG({
diff --git a/src/components/icons/Key.tsx b/src/components/icons/Key.tsx
new file mode 100644
index 000000000..a7abd6b9a
--- /dev/null
+++ b/src/components/icons/Key.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Key_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M3 12a4 4 0 0 1 7.212-2.385c.363.488.963.885 1.696.885h8.111l1.2 1.5-1.2 1.5h-1.783l-1.789-.894a1 1 0 0 0-.894 0l-1.79.894h-1.855c-.733 0-1.333.397-1.696.885A4 4 0 0 1 3 12Zm4-6a6 6 0 1 0 4.817 9.579.3.3 0 0 1 .076-.072l.017-.007H14a1 1 0 0 0 .447-.106L16 14.618l1.553.776c.139.07.292.106.447.106h2.02a2 2 0 0 0 1.561-.75l1.2-1.5a2 2 0 0 0 0-2.5l-1.2-1.5a2 2 0 0 0-1.562-.75h-8.11l-.016-.007a.3.3 0 0 1-.077-.071A6 6 0 0 0 7 6Zm0 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z',
+})
diff --git a/src/components/icons/Macintosh.tsx b/src/components/icons/Macintosh.tsx
new file mode 100644
index 000000000..8cc598126
--- /dev/null
+++ b/src/components/icons/Macintosh.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Macintosh_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M4 5a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v11c0 .889-.386 1.687-1 2.236V20a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.764c-.614-.55-1-1.348-1-2.236V5Zm3 14v1h10v-1H7ZM7 4a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7Zm0 2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V6Zm2 1v4h6V7H9Zm4 8a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Z',
+})
diff --git a/src/components/icons/Newspaper.tsx b/src/components/icons/Newspaper.tsx
new file mode 100644
index 000000000..bd4b85590
--- /dev/null
+++ b/src/components/icons/Newspaper.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Newspaper_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M1 6.5A2.5 2.5 0 0 1 3.5 4H9a4 4 0 0 1 3 1.354A4 4 0 0 1 15 4h5.5A2.5 2.5 0 0 1 23 6.5v11a2.5 2.5 0 0 1-2.5 2.5h-5.223c-.52 0-1 .125-1.4.372-.421.26-.761.633-.983 1.075a1 1 0 0 1-1.788 0 2.66 2.66 0 0 0-.983-1.075c-.4-.247-.88-.372-1.4-.372H3.5A2.5 2.5 0 0 1 1 17.5v-11ZM11 8a2 2 0 0 0-2-2H3.5a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h5.223c.776 0 1.564.173 2.277.569V8Zm2 10.569A4.7 4.7 0 0 1 15.277 18H20.5a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5H15a2 2 0 0 0-2 2v10.569Z',
+})
diff --git a/src/components/icons/Pencil.tsx b/src/components/icons/Pencil.tsx
index 51fd8ba79..a99f8d614 100644
--- a/src/components/icons/Pencil.tsx
+++ b/src/components/icons/Pencil.tsx
@@ -7,3 +7,7 @@ export const Pencil_Stroke2_Corner0_Rounded = createSinglePathSVG({
 export const PencilLine_Stroke2_Corner0_Rounded = createSinglePathSVG({
   path: 'M15.586 2.5a2 2 0 0 1 2.828 0L21.5 5.586a2 2 0 0 1 0 2.828l-13 13A2 2 0 0 1 7.086 22H3a1 1 0 0 1-1-1v-4.086a2 2 0 0 1 .586-1.414l13-13ZM17 3.914l-13 13V20h3.086l13-13L17 3.914ZM13 21a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2h-7a1 1 0 0 1-1-1Z',
 })
+
+export const PencilLine_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M15.379 2.707a3 3 0 0 1 4.242 0l1.672 1.672a3 3 0 0 1 0 4.242l-12.5 12.5A3 3 0 0 1 6.672 22H3a1 1 0 0 1-1-1v-3.672a3 3 0 0 1 .879-2.121l12.5-12.5Zm2.828 1.414a1 1 0 0 0-1.414 0l-12.5 12.5a1 1 0 0 0-.293.707V20h2.672a1 1 0 0 0 .707-.293l12.5-12.5.707.707-.707-.707a1 1 0 0 0 0-1.414L18.207 4.12ZM13 21a1 1 0 0 1 1-1h7a1 1 0 0 1 0 2h-7a1 1 0 0 1-1-1Z',
+})
diff --git a/src/components/icons/Person.tsx b/src/components/icons/Person.tsx
index 4fcc83891..31d7078d9 100644
--- a/src/components/icons/Person.tsx
+++ b/src/components/icons/Person.tsx
@@ -23,3 +23,7 @@ export const PersonPlus_Stroke2_Corner0_Rounded = createSinglePathSVG({
 export const PersonPlus_Filled_Stroke2_Corner0_Rounded = createSinglePathSVG({
   path: 'M7.5 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM12 12c-4.758 0-8.083 3.521-8.496 7.906A1 1 0 0 0 4.5 21H15a3 3 0 1 1 0-6c0-.824.332-1.571.87-2.113C14.739 12.32 13.435 12 12 12Zm6 2a1 1 0 0 1 1 1v2h2a1 1 0 1 1 0 2h-2v2a1 1 0 1 1-2 0v-2h-2a1 1 0 1 1 0-2h2v-2a1 1 0 0 1 1-1Z',
 })
+
+export const PersonGroup_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M8 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM4 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm13-1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm-3.5 1.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Zm7.301 9.7c-.836-2.6-2.88-3.503-4.575-3.111a1 1 0 0 1-.451-1.949c2.815-.651 5.81.966 6.93 4.448a2.49 2.49 0 0 1-.506 2.43A2.92 2.92 0 0 1 20 20h-2a1 1 0 1 1 0-2h2a.92.92 0 0 0 .69-.295.49.49 0 0 0 .112-.505ZM8 14c-1.865 0-3.878 1.274-4.681 4.151a.57.57 0 0 0 .132.55c.15.171.4.299.695.299h7.708a.93.93 0 0 0 .695-.299.57.57 0 0 0 .132-.55C11.878 15.274 9.865 14 8 14Zm0-2c2.87 0 5.594 1.98 6.607 5.613.53 1.9-1.09 3.387-2.753 3.387H4.146c-1.663 0-3.283-1.487-2.753-3.387C2.406 13.981 5.129 12 8 12Z',
+})
diff --git a/src/components/icons/RaisingHand.tsx b/src/components/icons/RaisingHand.tsx
index cd023cb7e..d9d73957d 100644
--- a/src/components/icons/RaisingHand.tsx
+++ b/src/components/icons/RaisingHand.tsx
@@ -1,5 +1,9 @@
 import {createSinglePathSVG} from './TEMPLATE'
 
-export const RaisingHande4Finger_Stroke2_Corner0_Rounded = createSinglePathSVG({
+export const RaisingHand4Finger_Stroke2_Corner0_Rounded = createSinglePathSVG({
   path: 'M10.25 4a.75.75 0 0 0-.75.75V11a1 1 0 1 1-2 0V6.75a.75.75 0 0 0-1.5 0V14a6 6 0 0 0 12 0V9a2 2 0 0 0-2 2v1.5a1 1 0 0 1-.684.949l-.628.21A2.469 2.469 0 0 0 13 16a1 1 0 1 1-2 0 4.469 4.469 0 0 1 3-4.22V11c0-.703.181-1.364.5-1.938V5.75a.75.75 0 0 0-1.5 0V9a1 1 0 1 1-2 0V4.75a.75.75 0 0 0-.75-.75Zm2.316-.733A2.75 2.75 0 0 1 16.5 5.75v1.54c.463-.187.97-.29 1.5-.29h1a1 1 0 0 1 1 1v6a8 8 0 1 1-16 0V6.75a2.75 2.75 0 0 1 3.571-2.625 2.751 2.751 0 0 1 4.995-.858Z',
 })
+
+export const RaisingHand4Finger_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M12.5 4a.5.5 0 0 0-.5.5V10a1 1 0 1 1-2 0V5.5a.5.5 0 0 0-1 0V11a1 1 0 1 1-2 0V7.5a.5.5 0 0 0-1 0v6a6.5 6.5 0 1 0 13 0V9c-.513 0-.979.192-1.333.509-.41.368-.667.899-.667 1.491v.838c0 .826-.529 1.559-1.312 1.82A2.47 2.47 0 0 0 14 16a1 1 0 1 1-2 0 4.47 4.47 0 0 1 3-4.22V11c0-1.014.379-1.941 1-2.646V5.5a.5.5 0 0 0-1 0V10a1 1 0 1 1-2 0V4.5a.5.5 0 0 0-.5-.5Zm2.112-.838A2.5 2.5 0 0 1 18 5.5v1.626q.481-.124 1-.126a2 2 0 0 1 2 2v4.5a8.5 8.5 0 0 1-17 0v-6a2.5 2.5 0 0 1 3.039-2.442 2.5 2.5 0 0 1 3.349-1.896 2.498 2.498 0 0 1 4.224 0Z',
+})
diff --git a/src/components/icons/Wrench.tsx b/src/components/icons/Wrench.tsx
new file mode 100644
index 000000000..4b4c86ec4
--- /dev/null
+++ b/src/components/icons/Wrench.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Wrench_Stroke2_Corner2_Rounded = createSinglePathSVG({
+  path: 'M14.5 4a5.5 5.5 0 0 0-5.078 7.616 1 1 0 0 1-.216 1.092L4.37 17.543a1 1 0 0 0 0 1.414l.672.672a1 1 0 0 0 1.414 0l4.835-4.835a1 1 0 0 1 1.092-.216A5.5 5.5 0 0 0 20 9.414l-1.293 1.293a3.828 3.828 0 1 1-5.414-5.414L14.585 4 14.5 4ZM7 9.5a7.5 7.5 0 0 1 9.969-7.084 1 1 0 0 1 .378 1.651l-2.64 2.64a1.829 1.829 0 0 0 2.586 2.586l2.64-2.64a1 1 0 0 1 1.65.378 7.5 7.5 0 0 1-9.328 9.627l-4.384 4.385a3 3 0 0 1-4.242 0l-.672-.672a3 3 0 0 1 0-4.242l4.385-4.385A7.5 7.5 0 0 1 7 9.5Z',
+})
diff --git a/src/lib/app-info.web.ts b/src/lib/app-info.web.ts
index 7227e2863..742ccfe97 100644
--- a/src/lib/app-info.web.ts
+++ b/src/lib/app-info.web.ts
@@ -3,6 +3,7 @@ import {version} from '../../package.json'
 export const BUILD_ENV = process.env.EXPO_PUBLIC_ENV
 export const IS_DEV = process.env.EXPO_PUBLIC_ENV === 'development'
 export const IS_TESTFLIGHT = false
+export const IS_INTERNAL = IS_DEV
 
 // This is the commit hash that the current bundle was made from. The user can see the commit hash in the app's settings
 // along with the other version info. Useful for debugging/reporting.
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index 426665d07..e699a47d3 100644
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -11,7 +11,6 @@ export type CommonNavigatorParams = {
   ModerationMutedAccounts: undefined
   ModerationBlockedAccounts: undefined
   Settings: undefined
-  LanguageSettings: undefined
   Profile: {name: string; hideBackButton?: boolean}
   ProfileFollowers: {name: string}
   ProfileFollows: {name: string}
@@ -33,6 +32,7 @@ export type CommonNavigatorParams = {
   TermsOfService: undefined
   CommunityGuidelines: undefined
   CopyrightPolicy: undefined
+  LanguageSettings: undefined
   AppPasswords: undefined
   SavedFeeds: undefined
   PreferencesFollowingFeed: undefined
@@ -40,6 +40,10 @@ export type CommonNavigatorParams = {
   PreferencesExternalEmbeds: undefined
   AccessibilitySettings: undefined
   AppearanceSettings: undefined
+  AccountSettings: undefined
+  PrivacyAndSecuritySettings: undefined
+  ContentAndMediaSettings: undefined
+  AboutSettings: undefined
   Search: {q?: string}
   Hashtag: {tag: string; author?: string}
   MessagesConversation: {conversation: string; embed?: string}
diff --git a/src/routes.ts b/src/routes.ts
index 2ae4126ac..58e9a3abc 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -1,4 +1,4 @@
-import {Router} from 'lib/routes/router'
+import {Router} from '#/lib/routes/router'
 
 export const router = new Router({
   Home: '/',
@@ -7,12 +7,13 @@ export const router = new Router({
   Notifications: '/notifications',
   NotificationsSettings: '/notifications/settings',
   Settings: '/settings',
-  LanguageSettings: '/settings/language',
   Lists: '/lists',
+  // moderation
   Moderation: '/moderation',
   ModerationModlists: '/moderation/modlists',
   ModerationMutedAccounts: '/moderation/muted-accounts',
   ModerationBlockedAccounts: '/moderation/blocked-accounts',
+  // profiles, threads, lists
   Profile: ['/profile/:name', '/profile/:name/rss'],
   ProfileFollowers: '/profile/:name/followers',
   ProfileFollows: '/profile/:name/follows',
@@ -25,9 +26,12 @@ export const router = new Router({
   ProfileFeed: '/profile/:name/feed/:rkey',
   ProfileFeedLikedBy: '/profile/:name/feed/:rkey/liked-by',
   ProfileLabelerLikedBy: '/profile/:name/labeler/liked-by',
+  // debug
   Debug: '/sys/debug',
   DebugMod: '/sys/debug-mod',
   Log: '/sys/log',
+  // settings
+  LanguageSettings: '/settings/language',
   AppPasswords: '/settings/app-passwords',
   PreferencesFollowingFeed: '/settings/following-feed',
   PreferencesThreads: '/settings/threads',
@@ -35,15 +39,24 @@ export const router = new Router({
   AccessibilitySettings: '/settings/accessibility',
   AppearanceSettings: '/settings/appearance',
   SavedFeeds: '/settings/saved-feeds',
+  // new settings
+  AccountSettings: '/settings/account',
+  PrivacyAndSecuritySettings: '/settings/privacy-and-security',
+  ContentAndMediaSettings: '/settings/content-and-media',
+  AboutSettings: '/settings/about',
+  // support
   Support: '/support',
   PrivacyPolicy: '/support/privacy',
   TermsOfService: '/support/tos',
   CommunityGuidelines: '/support/community-guidelines',
   CopyrightPolicy: '/support/copyright',
+  // hashtags
   Hashtag: '/hashtag/:tag',
+  // DMs
   Messages: '/messages',
   MessagesSettings: '/messages/settings',
   MessagesConversation: '/messages/:conversation',
+  // starter packs
   Start: '/start/:name/:rkey',
   StarterPackEdit: '/starter-pack/edit/:rkey',
   StarterPack: '/starter-pack/:name/:rkey',
diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx
index 222774e05..d5a2daffd 100644
--- a/src/screens/Moderation/index.tsx
+++ b/src/screens/Moderation/index.tsx
@@ -7,6 +7,7 @@ import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useFocusEffect} from '@react-navigation/native'
 
+import {IS_INTERNAL} from '#/lib/app-info'
 import {getLabelingServiceTitle} from '#/lib/moderation'
 import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
 import {logger} from '#/logger'
@@ -469,18 +470,22 @@ export function ModerationScreenInner({
         </View>
       )}
 
-      <Text
-        style={[
-          a.text_md,
-          a.font_bold,
-          a.pt_2xl,
-          a.pb_md,
-          t.atoms.text_contrast_high,
-        ]}>
-        <Trans>Logged-out visibility</Trans>
-      </Text>
+      {!IS_INTERNAL && (
+        <>
+          <Text
+            style={[
+              a.text_md,
+              a.font_bold,
+              a.pt_2xl,
+              a.pb_md,
+              t.atoms.text_contrast_high,
+            ]}>
+            <Trans>Logged-out visibility</Trans>
+          </Text>
 
-      <PwiOptOut />
+          <PwiOptOut />
+        </>
+      )}
 
       <View style={{height: 200}} />
     </ScrollView>
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
new file mode 100644
index 000000000..3c445b966
--- /dev/null
+++ b/src/screens/Settings/AboutSettings.tsx
@@ -0,0 +1,78 @@
+import React from 'react'
+import {Platform} from 'react-native'
+import {setStringAsync} from 'expo-clipboard'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {appVersion, BUNDLE_DATE, bundleInfo} from '#/lib/app-info'
+import {STATUS_PAGE_URL} from '#/lib/constants'
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import * as Toast from '#/view/com/util/Toast'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {CodeLines_Stroke2_Corner2_Rounded as CodeLinesIcon} from '#/components/icons/CodeLines'
+import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
+import {Newspaper_Stroke2_Corner2_Rounded as NewspaperIcon} from '#/components/icons/Newspaper'
+import {Wrench_Stroke2_Corner2_Rounded as WrenchIcon} from '#/components/icons/Wrench'
+import * as Layout from '#/components/Layout'
+
+type Props = NativeStackScreenProps<CommonNavigatorParams, 'AboutSettings'>
+export function AboutSettingsScreen({}: Props) {
+  const {_} = useLingui()
+
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`About`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <SettingsList.LinkItem
+            to="https://bsky.social/about/support/tos"
+            label={_(msg`Terms of Service`)}>
+            <SettingsList.ItemIcon icon={NewspaperIcon} />
+            <SettingsList.ItemText>
+              <Trans>Terms of Service</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="https://bsky.social/about/support/privacy-policy"
+            label={_(msg`Privacy Policy`)}>
+            <SettingsList.ItemIcon icon={NewspaperIcon} />
+            <SettingsList.ItemText>
+              <Trans>Privacy Policy</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to={STATUS_PAGE_URL}
+            label={_(msg`Status Page`)}>
+            <SettingsList.ItemIcon icon={GlobeIcon} />
+            <SettingsList.ItemText>
+              <Trans>Status Page</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.Divider />
+          <SettingsList.LinkItem to="/sys/log" label={_(msg`System log`)}>
+            <SettingsList.ItemIcon icon={CodeLinesIcon} />
+            <SettingsList.ItemText>
+              <Trans>System log</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.PressableItem
+            label={_(msg`Version ${appVersion}`)}
+            accessibilityHint={_(msg`Copy build version to clipboard`)}
+            onPress={() => {
+              setStringAsync(
+                `Build version: ${appVersion}; Bundle info: ${bundleInfo}; Bundle date: ${BUNDLE_DATE}; Platform: ${Platform.OS}; Platform version: ${Platform.Version}`,
+              )
+              Toast.show(_(msg`Copied build version to clipboard`))
+            }}>
+            <SettingsList.ItemIcon icon={WrenchIcon} />
+            <SettingsList.ItemText>
+              <Trans>Version {appVersion}</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.BadgeText>{bundleInfo}</SettingsList.BadgeText>
+          </SettingsList.PressableItem>
+        </SettingsList.Container>
+      </Layout.Content>
+    </Layout.Screen>
+  )
+}
diff --git a/src/screens/Settings/AccessibilitySettings.tsx b/src/screens/Settings/AccessibilitySettings.tsx
new file mode 100644
index 000000000..dfe2c14a5
--- /dev/null
+++ b/src/screens/Settings/AccessibilitySettings.tsx
@@ -0,0 +1,113 @@
+import React from 'react'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {isNative} from '#/platform/detection'
+import {
+  useHapticsDisabled,
+  useRequireAltTextEnabled,
+  useSetHapticsDisabled,
+  useSetRequireAltTextEnabled,
+} from '#/state/preferences'
+import {
+  useLargeAltBadgeEnabled,
+  useSetLargeAltBadgeEnabled,
+} from '#/state/preferences/large-alt-badge'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {atoms as a} from '#/alf'
+import {Admonition} from '#/components/Admonition'
+import * as Toggle from '#/components/forms/Toggle'
+import {Accessibility_Stroke2_Corner2_Rounded as AccessibilityIcon} from '#/components/icons/Accessibility'
+import {Haptic_Stroke2_Corner2_Rounded as HapticIcon} from '#/components/icons/Haptic'
+import * as Layout from '#/components/Layout'
+import {InlineLinkText} from '#/components/Link'
+
+type Props = NativeStackScreenProps<
+  CommonNavigatorParams,
+  'AccessibilitySettings'
+>
+export function AccessibilitySettingsScreen({}: Props) {
+  const {_} = useLingui()
+
+  const requireAltTextEnabled = useRequireAltTextEnabled()
+  const setRequireAltTextEnabled = useSetRequireAltTextEnabled()
+  const hapticsDisabled = useHapticsDisabled()
+  const setHapticsDisabled = useSetHapticsDisabled()
+  const largeAltBadgeEnabled = useLargeAltBadgeEnabled()
+  const setLargeAltBadgeEnabled = useSetLargeAltBadgeEnabled()
+
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`Accessibility`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <SettingsList.Group contentContainerStyle={[a.gap_sm]}>
+            <SettingsList.ItemIcon icon={AccessibilityIcon} />
+            <SettingsList.ItemText>
+              <Trans>Alt text</Trans>
+            </SettingsList.ItemText>
+            <Toggle.Item
+              name="require_alt_text"
+              label={_(msg`Require alt text before posting`)}
+              value={requireAltTextEnabled ?? false}
+              onChange={value => setRequireAltTextEnabled(value)}
+              style={[a.w_full]}>
+              <Toggle.LabelText style={[a.flex_1]}>
+                <Trans>Require alt text before posting</Trans>
+              </Toggle.LabelText>
+              <Toggle.Platform />
+            </Toggle.Item>
+            <Toggle.Item
+              name="large_alt_badge"
+              label={_(msg`Display larger alt text badges`)}
+              value={!!largeAltBadgeEnabled}
+              onChange={value => setLargeAltBadgeEnabled(value)}
+              style={[a.w_full]}>
+              <Toggle.LabelText style={[a.flex_1]}>
+                <Trans>Display larger alt text badges</Trans>
+              </Toggle.LabelText>
+              <Toggle.Platform />
+            </Toggle.Item>
+          </SettingsList.Group>
+          {isNative && (
+            <>
+              <SettingsList.Divider />
+              <SettingsList.Group contentContainerStyle={[a.gap_sm]}>
+                <SettingsList.ItemIcon icon={HapticIcon} />
+                <SettingsList.ItemText>
+                  <Trans>Haptics</Trans>
+                </SettingsList.ItemText>
+                <Toggle.Item
+                  name="haptics"
+                  label={_(msg`Disable haptic feedback`)}
+                  value={hapticsDisabled ?? false}
+                  onChange={value => setHapticsDisabled(value)}
+                  style={[a.w_full]}>
+                  <Toggle.LabelText style={[a.flex_1]}>
+                    <Trans>Disable haptic feedback</Trans>
+                  </Toggle.LabelText>
+                  <Toggle.Platform />
+                </Toggle.Item>
+              </SettingsList.Group>
+            </>
+          )}
+          <SettingsList.Item>
+            <Admonition type="info" style={[a.flex_1]}>
+              <Trans>
+                Autoplay options have moved to the{' '}
+                <InlineLinkText
+                  to="/settings/content-and-media"
+                  label={_(msg`Content and media`)}>
+                  Content and Media settings
+                </InlineLinkText>
+                .
+              </Trans>
+            </Admonition>
+          </SettingsList.Item>
+        </SettingsList.Container>
+      </Layout.Content>
+    </Layout.Screen>
+  )
+}
diff --git a/src/screens/Settings/AccountSettings.tsx b/src/screens/Settings/AccountSettings.tsx
new file mode 100644
index 000000000..19101d2f4
--- /dev/null
+++ b/src/screens/Settings/AccountSettings.tsx
@@ -0,0 +1,180 @@
+import React from 'react'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+import {useQueryClient} from '@tanstack/react-query'
+
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {useModalControls} from '#/state/modals'
+import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
+import {useProfileQuery} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import {ExportCarDialog} from '#/view/screens/Settings/ExportCarDialog'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {atoms as a, useTheme} from '#/alf'
+import {useDialogControl} from '#/components/Dialog'
+import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
+import {At_Stroke2_Corner2_Rounded as AtIcon} from '#/components/icons/At'
+import {BirthdayCake_Stroke2_Corner2_Rounded as BirthdayCakeIcon} from '#/components/icons/BirthdayCake'
+import {Car_Stroke2_Corner2_Rounded as CarIcon} from '#/components/icons/Car'
+import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
+import {Envelope_Stroke2_Corner2_Rounded as EnvelopeIcon} from '#/components/icons/Envelope'
+import {Freeze_Stroke2_Corner2_Rounded as FreezeIcon} from '#/components/icons/Freeze'
+import {Lock_Stroke2_Corner2_Rounded as LockIcon} from '#/components/icons/Lock'
+import {PencilLine_Stroke2_Corner2_Rounded as PencilIcon} from '#/components/icons/Pencil'
+import {Trash_Stroke2_Corner2_Rounded} from '#/components/icons/Trash'
+import {Verified_Stroke2_Corner2_Rounded as VerifiedIcon} from '#/components/icons/Verified'
+import * as Layout from '#/components/Layout'
+import {DeactivateAccountDialog} from './components/DeactivateAccountDialog'
+
+type Props = NativeStackScreenProps<CommonNavigatorParams, 'AccountSettings'>
+export function AccountSettingsScreen({}: Props) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const queryClient = useQueryClient()
+  const {data: profile} = useProfileQuery({did: currentAccount?.did})
+  const {openModal} = useModalControls()
+  const birthdayControl = useDialogControl()
+  const exportCarControl = useDialogControl()
+  const deactivateAccountControl = useDialogControl()
+
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`Account`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <SettingsList.Item>
+            <SettingsList.ItemIcon icon={EnvelopeIcon} />
+            <SettingsList.ItemText>
+              <Trans>Email</Trans>
+            </SettingsList.ItemText>
+            {currentAccount && (
+              <>
+                <SettingsList.BadgeText>
+                  {currentAccount.email || <Trans>(no email)</Trans>}
+                </SettingsList.BadgeText>
+                {currentAccount.emailConfirmed ? (
+                  <CheckIcon color={t.palette.positive_500} size="sm" />
+                ) : (
+                  <SettingsList.BadgeButton
+                    label={_(msg`Verify`)}
+                    onPress={() => {}}
+                  />
+                )}
+              </>
+            )}
+          </SettingsList.Item>
+          <SettingsList.PressableItem
+            label={_(msg`Change email`)}
+            onPress={() => openModal({name: 'change-email'})}>
+            <SettingsList.ItemIcon icon={PencilIcon} />
+            <SettingsList.ItemText>
+              <Trans>Change email</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.LinkItem
+            to="/settings/privacy-and-security"
+            label={_(msg`Protect your account`)}
+            style={[
+              a.my_xs,
+              a.mx_lg,
+              a.rounded_md,
+              {backgroundColor: t.palette.primary_50},
+            ]}
+            chevronColor={t.palette.primary_500}
+            hoverStyle={[{backgroundColor: t.palette.primary_100}]}
+            contentContainerStyle={[a.rounded_md, a.px_lg]}>
+            <SettingsList.ItemIcon
+              icon={VerifiedIcon}
+              color={t.palette.primary_500}
+            />
+            <SettingsList.ItemText
+              style={[{color: t.palette.primary_500}, a.font_bold]}>
+              <Trans>Protect your account</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.Divider />
+          <SettingsList.Item>
+            <SettingsList.ItemIcon icon={BirthdayCakeIcon} />
+            <SettingsList.ItemText>
+              <Trans>Birthday</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.BadgeButton
+              label={_(msg`Edit`)}
+              onPress={() => birthdayControl.open()}
+            />
+          </SettingsList.Item>
+          <SettingsList.PressableItem
+            label={_(msg`Password`)}
+            onPress={() => openModal({name: 'change-password'})}>
+            <SettingsList.ItemIcon icon={LockIcon} />
+            <SettingsList.ItemText>
+              <Trans>Password</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.PressableItem
+            label={_(msg`Handle`)}
+            onPress={() =>
+              openModal({
+                name: 'change-handle',
+                onChanged() {
+                  if (currentAccount) {
+                    // refresh my profile
+                    queryClient.invalidateQueries({
+                      queryKey: RQKEY_PROFILE(currentAccount.did),
+                    })
+                  }
+                },
+              })
+            }>
+            <SettingsList.ItemIcon icon={AtIcon} />
+            <SettingsList.ItemText>
+              <Trans>Handle</Trans>
+            </SettingsList.ItemText>
+            {profile && (
+              <SettingsList.BadgeText>@{profile.handle}</SettingsList.BadgeText>
+            )}
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.Divider />
+          <SettingsList.PressableItem
+            label={_(msg`Export my data`)}
+            onPress={() => exportCarControl.open()}>
+            <SettingsList.ItemIcon icon={CarIcon} />
+            <SettingsList.ItemText>
+              <Trans>Export my data</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.PressableItem
+            label={_(msg`Deactivate account`)}
+            onPress={() => deactivateAccountControl.open()}
+            destructive>
+            <SettingsList.ItemIcon icon={FreezeIcon} />
+            <SettingsList.ItemText>
+              <Trans>Deactivate account</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.PressableItem
+            label={_(msg`Delete account`)}
+            onPress={() => openModal({name: 'delete-account'})}
+            destructive>
+            <SettingsList.ItemIcon icon={Trash_Stroke2_Corner2_Rounded} />
+            <SettingsList.ItemText>
+              <Trans>Delete account</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+        </SettingsList.Container>
+      </Layout.Content>
+
+      <BirthDateSettingsDialog control={birthdayControl} />
+      <ExportCarDialog control={exportCarControl} />
+      <DeactivateAccountDialog control={deactivateAccountControl} />
+    </Layout.Screen>
+  )
+}
diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx
index c317c930f..d0beb7d50 100644
--- a/src/screens/Settings/AppearanceSettings.tsx
+++ b/src/screens/Settings/AppearanceSettings.tsx
@@ -1,18 +1,15 @@
 import React, {useCallback} from 'react'
-import {View} from 'react-native'
 import Animated, {
-  FadeInDown,
-  FadeOutDown,
+  FadeInUp,
+  FadeOutUp,
   LayoutAnimationConfig,
+  LinearTransition,
 } from 'react-native-reanimated'
-import {msg, Trans} from '@lingui/macro'
+import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
 import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
 import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
-import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
-import {ScrollView} from '#/view/com/util/Views'
 import {atoms as a, native, useAlf, useTheme} from '#/alf'
 import * as ToggleButton from '#/components/forms/ToggleButton'
 import {Props as SVGIconProps} from '#/components/icons/common'
@@ -22,12 +19,11 @@ import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/T
 import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase'
 import * as Layout from '#/components/Layout'
 import {Text} from '#/components/Typography'
+import * as SettingsList from './components/SettingsList'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
 export function AppearanceSettingsScreen({}: Props) {
-  const t = useTheme()
   const {_} = useLingui()
-  const {isTabletOrMobile} = useWebMediaQueries()
   const {fonts} = useAlf()
 
   const {colorMode, darkTheme} = useThemePrefs()
@@ -77,66 +73,54 @@ export function AppearanceSettingsScreen({}: Props) {
   return (
     <LayoutAnimationConfig skipExiting skipEntering>
       <Layout.Screen testID="preferencesThreadsScreen">
-        <ScrollView
-          // @ts-ignore web only -prf
-          dataSet={{'stable-gutters': 1}}
-          contentContainerStyle={{paddingBottom: 75}}>
-          <SimpleViewHeader
-            showBackButton={isTabletOrMobile}
-            style={[t.atoms.border_contrast_medium, a.border_b]}>
-            <View style={a.flex_1}>
-              <Text style={[a.text_2xl, a.font_bold]}>
-                <Trans>Appearance</Trans>
-              </Text>
-            </View>
-          </SimpleViewHeader>
-
-          <View style={[a.gap_3xl, a.pt_xl, a.px_xl]}>
-            <View style={[a.gap_lg]}>
-              <AppearanceToggleButtonGroup
-                title={_(msg`Color mode`)}
-                icon={PhoneIcon}
-                items={[
-                  {
-                    label: _(msg`System`),
-                    name: 'system',
-                  },
-                  {
-                    label: _(msg`Light`),
-                    name: 'light',
-                  },
-                  {
-                    label: _(msg`Dark`),
-                    name: 'dark',
-                  },
-                ]}
-                values={[colorMode]}
-                onChange={onChangeAppearance}
-              />
+        <Layout.Header title={_(msg`Appearance`)} />
+        <Layout.Content>
+          <SettingsList.Container>
+            <AppearanceToggleButtonGroup
+              title={_(msg`Color mode`)}
+              icon={PhoneIcon}
+              items={[
+                {
+                  label: _(msg`System`),
+                  name: 'system',
+                },
+                {
+                  label: _(msg`Light`),
+                  name: 'light',
+                },
+                {
+                  label: _(msg`Dark`),
+                  name: 'dark',
+                },
+              ]}
+              values={[colorMode]}
+              onChange={onChangeAppearance}
+            />
 
-              {colorMode !== 'light' && (
-                <Animated.View
-                  entering={native(FadeInDown)}
-                  exiting={native(FadeOutDown)}>
-                  <AppearanceToggleButtonGroup
-                    title={_(msg`Dark theme`)}
-                    icon={MoonIcon}
-                    items={[
-                      {
-                        label: _(msg`Dim`),
-                        name: 'dim',
-                      },
-                      {
-                        label: _(msg`Dark`),
-                        name: 'dark',
-                      },
-                    ]}
-                    values={[darkTheme ?? 'dim']}
-                    onChange={onChangeDarkTheme}
-                  />
-                </Animated.View>
-              )}
+            {colorMode !== 'light' && (
+              <Animated.View
+                entering={native(FadeInUp)}
+                exiting={native(FadeOutUp)}>
+                <AppearanceToggleButtonGroup
+                  title={_(msg`Dark theme`)}
+                  icon={MoonIcon}
+                  items={[
+                    {
+                      label: _(msg`Dim`),
+                      name: 'dim',
+                    },
+                    {
+                      label: _(msg`Dark`),
+                      name: 'dark',
+                    },
+                  ]}
+                  values={[darkTheme ?? 'dim']}
+                  onChange={onChangeDarkTheme}
+                />
+              </Animated.View>
+            )}
 
+            <Animated.View layout={native(LinearTransition)}>
               <AppearanceToggleButtonGroup
                 title={_(msg`Font`)}
                 description={_(
@@ -177,9 +161,9 @@ export function AppearanceSettingsScreen({}: Props) {
                 values={[fonts.scale]}
                 onChange={onChangeFontScale}
               />
-            </View>
-          </View>
-        </ScrollView>
+            </Animated.View>
+          </SettingsList.Container>
+        </Layout.Content>
       </Layout.Screen>
     </LayoutAnimationConfig>
   )
@@ -205,29 +189,32 @@ export function AppearanceToggleButtonGroup({
 }) {
   const t = useTheme()
   return (
-    <View style={[a.gap_sm]}>
-      <View style={[a.gap_xs]}>
-        <View style={[a.flex_row, a.align_center, a.gap_md]}>
-          <Icon style={t.atoms.text} />
-          <Text style={[a.text_md, a.font_bold]}>{title}</Text>
-        </View>
+    <>
+      <SettingsList.Group contentContainerStyle={[a.gap_sm]} iconInset={false}>
+        <SettingsList.ItemIcon icon={Icon} />
+        <SettingsList.ItemText>{title}</SettingsList.ItemText>
         {description && (
           <Text
-            style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
+            style={[
+              a.text_sm,
+              a.leading_snug,
+              t.atoms.text_contrast_medium,
+              a.w_full,
+            ]}>
             {description}
           </Text>
         )}
-      </View>
-      <ToggleButton.Group label={title} values={values} onChange={onChange}>
-        {items.map(item => (
-          <ToggleButton.Button
-            key={item.name}
-            label={item.label}
-            name={item.name}>
-            <ToggleButton.ButtonText>{item.label}</ToggleButton.ButtonText>
-          </ToggleButton.Button>
-        ))}
-      </ToggleButton.Group>
-    </View>
+        <ToggleButton.Group label={title} values={values} onChange={onChange}>
+          {items.map(item => (
+            <ToggleButton.Button
+              key={item.name}
+              label={item.label}
+              name={item.name}>
+              <ToggleButton.ButtonText>{item.label}</ToggleButton.ButtonText>
+            </ToggleButton.Button>
+          ))}
+        </ToggleButton.Group>
+      </SettingsList.Group>
+    </>
   )
 }
diff --git a/src/screens/Settings/ContentAndMediaSettings.tsx b/src/screens/Settings/ContentAndMediaSettings.tsx
new file mode 100644
index 000000000..79c8a48f3
--- /dev/null
+++ b/src/screens/Settings/ContentAndMediaSettings.tsx
@@ -0,0 +1,104 @@
+import React from 'react'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {isNative} from '#/platform/detection'
+import {useAutoplayDisabled, useSetAutoplayDisabled} from '#/state/preferences'
+import {
+  useInAppBrowser,
+  useSetInAppBrowser,
+} from '#/state/preferences/in-app-browser'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import * as Toggle from '#/components/forms/Toggle'
+import {Bubbles_Stroke2_Corner2_Rounded as BubblesIcon} from '#/components/icons/Bubble'
+import {Hashtag_Stroke2_Corner0_Rounded as HashtagIcon} from '#/components/icons/Hashtag'
+import {Home_Stroke2_Corner2_Rounded as HomeIcon} from '#/components/icons/Home'
+import {Macintosh_Stroke2_Corner2_Rounded as MacintoshIcon} from '#/components/icons/Macintosh'
+import {Play_Stroke2_Corner2_Rounded as PlayIcon} from '#/components/icons/Play'
+import {Window_Stroke2_Corner2_Rounded as WindowIcon} from '#/components/icons/Window'
+import * as Layout from '#/components/Layout'
+
+type Props = NativeStackScreenProps<
+  CommonNavigatorParams,
+  'ContentAndMediaSettings'
+>
+export function ContentAndMediaSettingsScreen({}: Props) {
+  const {_} = useLingui()
+  const autoplayDisabledPref = useAutoplayDisabled()
+  const setAutoplayDisabledPref = useSetAutoplayDisabled()
+  const inAppBrowserPref = useInAppBrowser()
+  const setUseInAppBrowser = useSetInAppBrowser()
+
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`Content and Media`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <SettingsList.LinkItem
+            to="/settings/saved-feeds"
+            label={_(msg`Manage saved feeds`)}>
+            <SettingsList.ItemIcon icon={HashtagIcon} />
+            <SettingsList.ItemText>
+              <Trans>Manage saved feeds</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/threads"
+            label={_(msg`Thread preferences`)}>
+            <SettingsList.ItemIcon icon={BubblesIcon} />
+            <SettingsList.ItemText>
+              <Trans>Thread preferences</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/following-feed"
+            label={_(msg`Following feed preferences`)}>
+            <SettingsList.ItemIcon icon={HomeIcon} />
+            <SettingsList.ItemText>
+              <Trans>Following feed preferences</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/external-embeds"
+            label={_(msg`External media`)}>
+            <SettingsList.ItemIcon icon={MacintoshIcon} />
+            <SettingsList.ItemText>
+              <Trans>External media</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.Divider />
+          {isNative && (
+            <Toggle.Item
+              name="use_in_app_browser"
+              label={_(msg`Use in-app browser to open links`)}
+              value={inAppBrowserPref ?? false}
+              onChange={value => setUseInAppBrowser(value)}>
+              <SettingsList.Item>
+                <SettingsList.ItemIcon icon={WindowIcon} />
+                <SettingsList.ItemText>
+                  <Trans>Use in-app browser to open links</Trans>
+                </SettingsList.ItemText>
+                <Toggle.Platform />
+              </SettingsList.Item>
+            </Toggle.Item>
+          )}
+          <Toggle.Item
+            name="disable_autoplay"
+            label={_(msg`Disable autoplay for videos and GIFs`)}
+            value={autoplayDisabledPref}
+            onChange={value => setAutoplayDisabledPref(value)}>
+            <SettingsList.Item>
+              <SettingsList.ItemIcon icon={PlayIcon} />
+              <SettingsList.ItemText>
+                <Trans>Disable autoplay for videos and GIFs</Trans>
+              </SettingsList.ItemText>
+              <Toggle.Platform />
+            </SettingsList.Item>
+          </Toggle.Item>
+        </SettingsList.Container>
+      </Layout.Content>
+    </Layout.Screen>
+  )
+}
diff --git a/src/screens/Settings/PrivacyAndSecuritySettings.tsx b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
new file mode 100644
index 000000000..da462c90d
--- /dev/null
+++ b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
@@ -0,0 +1,91 @@
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {useAppPasswordsQuery} from '#/state/queries/app-passwords'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {atoms as a} from '#/alf'
+import * as Admonition from '#/components/Admonition'
+import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlashIcon} from '#/components/icons/EyeSlash'
+import {Key_Stroke2_Corner2_Rounded as KeyIcon} from '#/components/icons/Key'
+import {Verified_Stroke2_Corner2_Rounded as VerifiedIcon} from '#/components/icons/Verified'
+import * as Layout from '#/components/Layout'
+import {InlineLinkText} from '#/components/Link'
+import {Email2FAToggle} from './components/Email2FAToggle'
+import {PwiOptOut} from './components/PwiOptOut'
+
+type Props = NativeStackScreenProps<
+  CommonNavigatorParams,
+  'PrivacyAndSecuritySettings'
+>
+export function PrivacyAndSecuritySettingsScreen({}: Props) {
+  const {_} = useLingui()
+  const {data: appPasswords} = useAppPasswordsQuery()
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`Privacy and Security`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <SettingsList.Item>
+            <SettingsList.ItemIcon icon={VerifiedIcon} />
+            <SettingsList.ItemText>
+              <Trans>Two-factor authentication (2FA)</Trans>
+            </SettingsList.ItemText>
+            <Email2FAToggle />
+          </SettingsList.Item>
+          <SettingsList.LinkItem
+            to="/settings/app-passwords"
+            label={_(msg`App passwords`)}>
+            <SettingsList.ItemIcon icon={KeyIcon} />
+            <SettingsList.ItemText>
+              <Trans>App passwords</Trans>
+            </SettingsList.ItemText>
+            {appPasswords && appPasswords.length > 0 && (
+              <SettingsList.BadgeText>
+                {appPasswords.length}
+              </SettingsList.BadgeText>
+            )}
+          </SettingsList.LinkItem>
+          <SettingsList.Divider />
+          <SettingsList.Group>
+            <SettingsList.ItemIcon icon={EyeSlashIcon} />
+            <SettingsList.ItemText>
+              <Trans>Logged-out visibility</Trans>
+            </SettingsList.ItemText>
+            <PwiOptOut />
+          </SettingsList.Group>
+          <SettingsList.Item>
+            <Admonition.Outer type="tip" style={[a.flex_1]}>
+              <Admonition.Row>
+                <Admonition.Icon />
+                <View style={[a.flex_1, a.gap_sm]}>
+                  <Admonition.Text>
+                    <Trans>
+                      Note: Bluesky is an open and public network. This setting
+                      only limits the visibility of your content on the Bluesky
+                      app and website, and other apps may not respect this
+                      setting. Your content may still be shown to logged-out
+                      users by other apps and websites.
+                    </Trans>
+                  </Admonition.Text>
+                  <Admonition.Text>
+                    <InlineLinkText
+                      label={_(
+                        msg`Learn more about what is public on Bluesky.`,
+                      )}
+                      to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">
+                      <Trans>Learn more about what is public on Bluesky.</Trans>
+                    </InlineLinkText>
+                  </Admonition.Text>
+                </View>
+              </Admonition.Row>
+            </Admonition.Outer>
+          </SettingsList.Item>
+        </SettingsList.Container>
+      </Layout.Content>
+    </Layout.Screen>
+  )
+}
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
new file mode 100644
index 000000000..789ffb56f
--- /dev/null
+++ b/src/screens/Settings/Settings.tsx
@@ -0,0 +1,282 @@
+import React from 'react'
+import {View} from 'react-native'
+import {Linking} from 'react-native'
+import {AppBskyActorDefs, moderateProfile} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {HELP_DESK_URL} from '#/lib/constants'
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {useModerationOpts} from '#/state/preferences/moderation-opts'
+import {useProfileQuery, useProfilesQuery} from '#/state/queries/profile'
+import {useSession, useSessionApi} from '#/state/session'
+import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+import {useCloseAllActiveElements} from '#/state/util'
+import {UserAvatar} from '#/view/com/util/UserAvatar'
+import {ProfileHeaderDisplayName} from '#/screens/Profile/Header/DisplayName'
+import {ProfileHeaderHandle} from '#/screens/Profile/Header/Handle'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {atoms as a, useTheme} from '#/alf'
+import {useDialogControl} from '#/components/Dialog'
+import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount'
+import {Accessibility_Stroke2_Corner2_Rounded as AccessibilityIcon} from '#/components/icons/Accessibility'
+import {BubbleInfo_Stroke2_Corner2_Rounded as BubbleInfoIcon} from '#/components/icons/BubbleInfo'
+import {CircleQuestion_Stroke2_Corner2_Rounded as CircleQuestionIcon} from '#/components/icons/CircleQuestion'
+import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe'
+import {Lock_Stroke2_Corner2_Rounded as LockIcon} from '#/components/icons/Lock'
+import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller'
+import {
+  Person_Stroke2_Corner2_Rounded as PersonIcon,
+  PersonGroup_Stroke2_Corner2_Rounded as PersonGroupIcon,
+} from '#/components/icons/Person'
+import {RaisingHand4Finger_Stroke2_Corner2_Rounded as HandIcon} from '#/components/icons/RaisingHand'
+import {Window_Stroke2_Corner2_Rounded as WindowIcon} from '#/components/icons/Window'
+import * as Layout from '#/components/Layout'
+import * as Prompt from '#/components/Prompt'
+
+type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
+export function SettingsScreen({}: Props) {
+  const {_} = useLingui()
+  const {logoutEveryAccount} = useSessionApi()
+  const {accounts, currentAccount} = useSession()
+  const switchAccountControl = useDialogControl()
+  const signOutPromptControl = Prompt.usePromptControl()
+  const {data: profile} = useProfileQuery({did: currentAccount?.did})
+  const {setShowLoggedOut} = useLoggedOutViewControls()
+  const closeEverything = useCloseAllActiveElements()
+
+  const onAddAnotherAccount = () => {
+    setShowLoggedOut(true)
+    closeEverything()
+  }
+
+  return (
+    <Layout.Screen>
+      <Layout.Header title={_(msg`Settings`)} />
+      <Layout.Content>
+        <SettingsList.Container>
+          <View
+            style={[
+              a.px_xl,
+              a.pb_md,
+              a.w_full,
+              a.gap_2xs,
+              a.align_center,
+              {minHeight: 160},
+            ]}>
+            {profile && <ProfilePreview profile={profile} />}
+          </View>
+          <SettingsList.PressableItem
+            label={
+              accounts.length > 1
+                ? _(msg`Switch account`)
+                : _(msg`Add another account`)
+            }
+            onPress={() =>
+              accounts.length > 1
+                ? switchAccountControl.open()
+                : onAddAnotherAccount()
+            }>
+            <SettingsList.ItemIcon icon={PersonGroupIcon} />
+            <SettingsList.ItemText>
+              {accounts.length > 1 ? (
+                <Trans>Switch account</Trans>
+              ) : (
+                <Trans>Add another account</Trans>
+              )}
+            </SettingsList.ItemText>
+            {accounts.length > 1 && (
+              <AvatarStack
+                profiles={accounts
+                  .map(acc => acc.did)
+                  .filter(did => did !== currentAccount?.did)
+                  .slice(0, 5)}
+              />
+            )}
+          </SettingsList.PressableItem>
+          <SettingsList.Divider />
+          <SettingsList.LinkItem to="/settings/account" label={_(msg`Account`)}>
+            <SettingsList.ItemIcon icon={PersonIcon} />
+            <SettingsList.ItemText>
+              <Trans>Account</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/privacy-and-security"
+            label={_(msg`Privacy and security`)}>
+            <SettingsList.ItemIcon icon={LockIcon} />
+            <SettingsList.ItemText>
+              <Trans>Privacy and security</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem to="/moderation" label={_(msg`Moderation`)}>
+            <SettingsList.ItemIcon icon={HandIcon} />
+            <SettingsList.ItemText>
+              <Trans>Moderation</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/content-and-media"
+            label={_(msg`Content and media`)}>
+            <SettingsList.ItemIcon icon={WindowIcon} />
+            <SettingsList.ItemText>
+              <Trans>Content and media</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/appearance"
+            label={_(msg`Appearance`)}>
+            <SettingsList.ItemIcon icon={PaintRollerIcon} />
+            <SettingsList.ItemText>
+              <Trans>Appearance</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/accessibility"
+            label={_(msg`Accessibility`)}>
+            <SettingsList.ItemIcon icon={AccessibilityIcon} />
+            <SettingsList.ItemText>
+              <Trans>Accessibility</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.LinkItem
+            to="/settings/language"
+            label={_(msg`Languages`)}>
+            <SettingsList.ItemIcon icon={EarthIcon} />
+            <SettingsList.ItemText>
+              <Trans>Languages</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.PressableItem
+            onPress={() => Linking.openURL(HELP_DESK_URL)}
+            label={_(msg`Help`)}
+            accessibilityHint={_(msg`Open helpdesk in browser`)}>
+            <SettingsList.ItemIcon icon={CircleQuestionIcon} />
+            <SettingsList.ItemText>
+              <Trans>Help</Trans>
+            </SettingsList.ItemText>
+            <SettingsList.Chevron />
+          </SettingsList.PressableItem>
+          <SettingsList.LinkItem to="/settings/about" label={_(msg`About`)}>
+            <SettingsList.ItemIcon icon={BubbleInfoIcon} />
+            <SettingsList.ItemText>
+              <Trans>About</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.LinkItem>
+          <SettingsList.Divider />
+          <SettingsList.PressableItem
+            destructive
+            onPress={() => signOutPromptControl.open()}
+            label={_(msg`Sign out`)}>
+            <SettingsList.ItemText>
+              <Trans>Sign out</Trans>
+            </SettingsList.ItemText>
+          </SettingsList.PressableItem>
+        </SettingsList.Container>
+      </Layout.Content>
+
+      <Prompt.Basic
+        control={signOutPromptControl}
+        title={_(msg`Sign out?`)}
+        description={_(msg`You will be signed out of all your accounts.`)}
+        onConfirm={() => logoutEveryAccount('Settings')}
+        confirmButtonCta={_(msg`Sign out`)}
+        cancelButtonCta={_(msg`Cancel`)}
+        confirmButtonColor="negative"
+      />
+
+      <SwitchAccountDialog control={switchAccountControl} />
+    </Layout.Screen>
+  )
+}
+
+function ProfilePreview({
+  profile,
+}: {
+  profile: AppBskyActorDefs.ProfileViewDetailed
+}) {
+  const shadow = useProfileShadow(profile)
+  const moderationOpts = useModerationOpts()
+
+  if (!moderationOpts) return null
+
+  const moderation = moderateProfile(profile, moderationOpts)
+
+  return (
+    <>
+      <UserAvatar
+        size={80}
+        avatar={shadow.avatar}
+        moderation={moderation.ui('avatar')}
+      />
+      <ProfileHeaderDisplayName profile={shadow} moderation={moderation} />
+      <ProfileHeaderHandle profile={shadow} />
+    </>
+  )
+}
+
+const AVI_SIZE = 26
+const HALF_AVI_SIZE = AVI_SIZE / 2
+
+function AvatarStack({profiles}: {profiles: string[]}) {
+  const {data, error} = useProfilesQuery({handles: profiles})
+  const t = useTheme()
+  const moderationOpts = useModerationOpts()
+
+  if (error) {
+    console.error(error)
+    return null
+  }
+
+  const isPending = !data || !moderationOpts
+
+  const items = isPending
+    ? Array.from({length: profiles.length}).map((_, i) => ({
+        key: i,
+        profile: null,
+        moderation: null,
+      }))
+    : data.profiles.map(item => ({
+        key: item.did,
+        profile: item,
+        moderation: moderateProfile(item, moderationOpts),
+      }))
+
+  return (
+    <View
+      style={[
+        a.flex_row,
+        a.align_center,
+        a.relative,
+        {width: AVI_SIZE + (items.length - 1) * HALF_AVI_SIZE},
+      ]}>
+      {items.map((item, i) => (
+        <View
+          key={item.key}
+          style={[
+            t.atoms.bg_contrast_25,
+            a.relative,
+            {
+              width: AVI_SIZE,
+              height: AVI_SIZE,
+              left: i * -HALF_AVI_SIZE,
+              borderWidth: 1,
+              borderColor: t.atoms.bg.backgroundColor,
+              borderRadius: 999,
+              zIndex: 3 - i,
+            },
+          ]}>
+          {item.profile && (
+            <UserAvatar
+              size={AVI_SIZE - 2}
+              avatar={item.profile.avatar}
+              moderation={item.moderation.ui('avatar')}
+            />
+          )}
+        </View>
+      ))}
+    </View>
+  )
+}
diff --git a/src/screens/Settings/components/Email2FAToggle.tsx b/src/screens/Settings/components/Email2FAToggle.tsx
new file mode 100644
index 000000000..d89e5f18e
--- /dev/null
+++ b/src/screens/Settings/components/Email2FAToggle.tsx
@@ -0,0 +1,66 @@
+import React from 'react'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {useModalControls} from '#/state/modals'
+import {useAgent, useSession} from '#/state/session'
+import {DisableEmail2FADialog} from '#/view/screens/Settings/DisableEmail2FADialog'
+import {useDialogControl} from '#/components/Dialog'
+import * as Prompt from '#/components/Prompt'
+import * as SettingsList from './SettingsList'
+
+export function Email2FAToggle() {
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const {openModal} = useModalControls()
+  const disableDialogControl = useDialogControl()
+  const enableDialogControl = useDialogControl()
+  const agent = useAgent()
+
+  const enableEmailAuthFactor = React.useCallback(async () => {
+    if (currentAccount?.email) {
+      await agent.com.atproto.server.updateEmail({
+        email: currentAccount.email,
+        emailAuthFactor: true,
+      })
+      await agent.resumeSession(agent.session!)
+    }
+  }, [currentAccount, agent])
+
+  const onToggle = React.useCallback(() => {
+    if (!currentAccount) {
+      return
+    }
+    if (currentAccount.emailAuthFactor) {
+      disableDialogControl.open()
+    } else {
+      if (!currentAccount.emailConfirmed) {
+        openModal({
+          name: 'verify-email',
+          onSuccess: enableDialogControl.open,
+        })
+        return
+      }
+      enableDialogControl.open()
+    }
+  }, [currentAccount, enableDialogControl, openModal, disableDialogControl])
+
+  return (
+    <>
+      <DisableEmail2FADialog control={disableDialogControl} />
+      <Prompt.Basic
+        control={enableDialogControl}
+        title={_(msg`Enable Email 2FA`)}
+        description={_(msg`Require an email code to log in to your account.`)}
+        onConfirm={enableEmailAuthFactor}
+        confirmButtonCta={_(msg`Enable`)}
+      />
+      <SettingsList.BadgeButton
+        label={
+          currentAccount?.emailAuthFactor ? _(msg`Disable`) : _(msg`Enable`)
+        }
+        onPress={onToggle}
+      />
+    </>
+  )
+}
diff --git a/src/screens/Settings/components/PwiOptOut.tsx b/src/screens/Settings/components/PwiOptOut.tsx
new file mode 100644
index 000000000..4339ade9b
--- /dev/null
+++ b/src/screens/Settings/components/PwiOptOut.tsx
@@ -0,0 +1,100 @@
+import React from 'react'
+import {View} from 'react-native'
+import {ComAtprotoLabelDefs} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {
+  useProfileQuery,
+  useProfileUpdateMutation,
+} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import {atoms as a, useTheme} from '#/alf'
+import * as Toggle from '#/components/forms/Toggle'
+import {Text} from '#/components/Typography'
+
+export function PwiOptOut() {
+  const t = useTheme()
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const {data: profile} = useProfileQuery({did: currentAccount?.did})
+  const updateProfile = useProfileUpdateMutation()
+
+  const isOptedOut =
+    profile?.labels?.some(l => l.val === '!no-unauthenticated') || false
+  const canToggle = profile && !updateProfile.isPending
+
+  const onToggleOptOut = React.useCallback(() => {
+    if (!profile) {
+      return
+    }
+    let wasAdded = false
+    updateProfile.mutate({
+      profile,
+      updates: existing => {
+        // create labels attr if needed
+        existing.labels = ComAtprotoLabelDefs.isSelfLabels(existing.labels)
+          ? existing.labels
+          : {
+              $type: 'com.atproto.label.defs#selfLabels',
+              values: [],
+            }
+
+        // toggle the label
+        const hasLabel = existing.labels.values.some(
+          l => l.val === '!no-unauthenticated',
+        )
+        if (hasLabel) {
+          wasAdded = false
+          existing.labels.values = existing.labels.values.filter(
+            l => l.val !== '!no-unauthenticated',
+          )
+        } else {
+          wasAdded = true
+          existing.labels.values.push({val: '!no-unauthenticated'})
+        }
+
+        // delete if no longer needed
+        if (existing.labels.values.length === 0) {
+          delete existing.labels
+        }
+        return existing
+      },
+      checkCommitted: res => {
+        const exists = !!res.data.labels?.some(
+          l => l.val === '!no-unauthenticated',
+        )
+        return exists === wasAdded
+      },
+    })
+  }, [updateProfile, profile])
+
+  return (
+    <View style={[a.flex_1, a.gap_sm]}>
+      <Toggle.Item
+        name="logged_out_visibility"
+        disabled={!canToggle || updateProfile.isPending}
+        value={isOptedOut}
+        onChange={onToggleOptOut}
+        label={_(
+          msg`Discourage apps from showing my account to logged-out users`,
+        )}
+        style={[a.w_full]}>
+        <Toggle.LabelText style={[a.flex_1]}>
+          <Trans>
+            Discourage apps from showing my account to logged-out users
+          </Trans>
+        </Toggle.LabelText>
+        <Toggle.Platform />
+      </Toggle.Item>
+
+      <Text style={[a.leading_snug, t.atoms.text_contrast_high]}>
+        <Trans>
+          Bluesky will not show your profile and posts to logged-out users.
+          Other apps may not honor this request. This does not make your account
+          private.
+        </Trans>
+      </Text>
+    </View>
+  )
+}
diff --git a/src/screens/Settings/components/SettingsList.tsx b/src/screens/Settings/components/SettingsList.tsx
new file mode 100644
index 000000000..86f8040af
--- /dev/null
+++ b/src/screens/Settings/components/SettingsList.tsx
@@ -0,0 +1,300 @@
+import React, {useContext, useMemo} from 'react'
+import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'
+
+import {HITSLOP_10} from '#/lib/constants'
+import {atoms as a, useTheme} from '#/alf'
+import * as Button from '#/components/Button'
+import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
+import {Link, LinkProps} from '#/components/Link'
+import {createPortalGroup} from '#/components/Portal'
+import {Text} from '#/components/Typography'
+
+const ItemContext = React.createContext({
+  destructive: false,
+  withinGroup: false,
+})
+
+const Portal = createPortalGroup()
+
+export function Container({children}: {children: React.ReactNode}) {
+  return <View style={[a.flex_1, a.py_lg]}>{children}</View>
+}
+
+/**
+ * This uses `Portal` magic ✨ to render the icons and title correctly. ItemIcon and ItemText components
+ * get teleported to the top row, leaving the rest of the children in the bottom row.
+ */
+export function Group({
+  children,
+  destructive = false,
+  iconInset = true,
+  style,
+  contentContainerStyle,
+}: {
+  children: React.ReactNode
+  destructive?: boolean
+  iconInset?: boolean
+  style?: StyleProp<ViewStyle>
+  contentContainerStyle?: StyleProp<ViewStyle>
+}) {
+  const context = useMemo(
+    () => ({destructive, withinGroup: true}),
+    [destructive],
+  )
+  return (
+    <View style={[a.w_full, style]}>
+      <Portal.Provider>
+        <ItemContext.Provider value={context}>
+          <Item style={[a.pb_2xs, {minHeight: 42}]}>
+            <Portal.Outlet />
+          </Item>
+          <Item
+            style={[
+              a.flex_col,
+              a.pt_2xs,
+              a.align_start,
+              a.gap_0,
+              contentContainerStyle,
+            ]}
+            iconInset={iconInset}>
+            {children}
+          </Item>
+        </ItemContext.Provider>
+      </Portal.Provider>
+    </View>
+  )
+}
+
+export function Item({
+  children,
+  destructive,
+  iconInset = false,
+  style,
+}: {
+  children?: React.ReactNode
+  destructive?: boolean
+  /**
+   * Adds left padding so that the content will be aligned with other Items that contain icons
+   * @default false
+   */
+  iconInset?: boolean
+  style?: StyleProp<ViewStyle>
+}) {
+  const context = useContext(ItemContext)
+  const childContext = useMemo(() => {
+    if (typeof destructive !== 'boolean') return context
+    return {...context, destructive}
+  }, [context, destructive])
+  return (
+    <View
+      style={[
+        a.px_xl,
+        a.py_sm,
+        a.align_center,
+        a.gap_md,
+        a.w_full,
+        a.flex_row,
+        {minHeight: 48},
+        iconInset && {
+          paddingLeft:
+            // existing padding
+            a.pl_xl.paddingLeft +
+            // icon
+            28 +
+            // gap
+            a.gap_md.gap,
+        },
+        style,
+      ]}>
+      <ItemContext.Provider value={childContext}>
+        {children}
+      </ItemContext.Provider>
+    </View>
+  )
+}
+
+export function LinkItem({
+  children,
+  destructive = false,
+  contentContainerStyle,
+  chevronColor,
+  ...props
+}: LinkProps & {
+  contentContainerStyle?: StyleProp<ViewStyle>
+  destructive?: boolean
+  chevronColor?: string
+}) {
+  const t = useTheme()
+
+  return (
+    <Link color="secondary" {...props}>
+      {args => (
+        <Item
+          destructive={destructive}
+          style={[
+            (args.hovered || args.pressed) && [t.atoms.bg_contrast_25],
+            contentContainerStyle,
+          ]}>
+          {typeof children === 'function' ? children(args) : children}
+          <Chevron color={chevronColor} />
+        </Item>
+      )}
+    </Link>
+  )
+}
+
+export function PressableItem({
+  children,
+  destructive = false,
+  contentContainerStyle,
+  hoverStyle,
+  ...props
+}: Button.ButtonProps & {
+  contentContainerStyle?: StyleProp<ViewStyle>
+  destructive?: boolean
+}) {
+  const t = useTheme()
+  return (
+    <Button.Button {...props}>
+      {args => (
+        <Item
+          destructive={destructive}
+          style={[
+            (args.hovered || args.pressed) && [
+              t.atoms.bg_contrast_25,
+              hoverStyle,
+            ],
+            contentContainerStyle,
+          ]}>
+          {typeof children === 'function' ? children(args) : children}
+        </Item>
+      )}
+    </Button.Button>
+  )
+}
+
+export function ItemIcon({
+  icon: Comp,
+  size = 'xl',
+  color: colorProp,
+}: Omit<React.ComponentProps<typeof Button.ButtonIcon>, 'position'> & {
+  color?: string
+}) {
+  const t = useTheme()
+  const {destructive, withinGroup} = useContext(ItemContext)
+
+  /*
+   * Copied here from icons/common.tsx so we can tweak if we need to, but
+   * also so that we can calculate transforms.
+   */
+  const iconSize = {
+    xs: 12,
+    sm: 16,
+    md: 20,
+    lg: 24,
+    xl: 28,
+    '2xl': 32,
+  }[size]
+
+  const color =
+    colorProp ?? (destructive ? t.palette.negative_500 : t.atoms.text.color)
+
+  const content = (
+    <View style={[a.z_20, {width: iconSize, height: iconSize}]}>
+      <Comp width={iconSize} style={[{color}]} />
+    </View>
+  )
+
+  if (withinGroup) {
+    return <Portal.Portal>{content}</Portal.Portal>
+  } else {
+    return content
+  }
+}
+
+export function ItemText({
+  // eslint-disable-next-line react/prop-types
+  style,
+  ...props
+}: React.ComponentProps<typeof Button.ButtonText>) {
+  const t = useTheme()
+  const {destructive, withinGroup} = useContext(ItemContext)
+
+  const content = (
+    <Button.ButtonText
+      style={[
+        a.text_md,
+        a.font_normal,
+        a.text_left,
+        a.flex_1,
+        destructive ? {color: t.palette.negative_500} : t.atoms.text,
+        style,
+      ]}
+      {...props}
+    />
+  )
+
+  if (withinGroup) {
+    return <Portal.Portal>{content}</Portal.Portal>
+  } else {
+    return content
+  }
+}
+
+export function Divider() {
+  const t = useTheme()
+  return (
+    <View
+      style={[a.border_t, t.atoms.border_contrast_medium, a.w_full, a.my_sm]}
+    />
+  )
+}
+
+export function Chevron({color: colorProp}: {color?: string}) {
+  const {destructive} = useContext(ItemContext)
+  const t = useTheme()
+  const color =
+    colorProp ?? (destructive ? t.palette.negative_500 : t.palette.contrast_500)
+  return <ItemIcon icon={ChevronRightIcon} size="md" color={color} />
+}
+
+export function BadgeText({children}: {children: React.ReactNode}) {
+  const t = useTheme()
+  return (
+    <Text
+      style={[
+        t.atoms.text_contrast_low,
+        a.text_md,
+        a.text_right,
+        a.leading_snug,
+      ]}
+      numberOfLines={1}>
+      {children}
+    </Text>
+  )
+}
+
+export function BadgeButton({
+  label,
+  onPress,
+}: {
+  label: string
+  onPress: (evt: GestureResponderEvent) => void
+}) {
+  const t = useTheme()
+  return (
+    <Button.Button label={label} onPress={onPress} hitSlop={HITSLOP_10}>
+      {({pressed}) => (
+        <Button.ButtonText
+          style={[
+            a.text_md,
+            a.font_normal,
+            a.text_right,
+            {color: pressed ? t.palette.contrast_300 : t.palette.primary_500},
+          ]}>
+          {label}
+        </Button.ButtonText>
+      )}
+    </Button.Button>
+  )
+}
diff --git a/src/view/com/modals/DeleteAccount.tsx b/src/view/com/modals/DeleteAccount.tsx
index 6dd248ca7..b865d7bbf 100644
--- a/src/view/com/modals/DeleteAccount.tsx
+++ b/src/view/com/modals/DeleteAccount.tsx
@@ -10,15 +10,15 @@ import {LinearGradient} from 'expo-linear-gradient'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
+import {usePalette} from '#/lib/hooks/usePalette'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {cleanError} from '#/lib/strings/errors'
+import {colors, gradients, s} from '#/lib/styles'
+import {useTheme} from '#/lib/ThemeContext'
+import {isAndroid, isWeb} from '#/platform/detection'
 import {useModalControls} from '#/state/modals'
 import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
 import {useAgent, useSession, useSessionApi} from '#/state/session'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {cleanError} from 'lib/strings/errors'
-import {colors, gradients, s} from 'lib/styles'
-import {useTheme} from 'lib/ThemeContext'
-import {isAndroid, isWeb} from 'platform/detection'
 import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
 import {atoms as a, useTheme as useNewTheme} from '#/alf'
 import {useDialogControl} from '#/components/Dialog'
@@ -210,6 +210,7 @@ export function Component({}: {}) {
                     to="#"
                     onPress={e => {
                       e.preventDefault()
+                      closeModal()
                       deactivateAccountControl.open()
                       return false
                     }}>
diff --git a/src/view/screens/AccessibilitySettings.tsx b/src/view/screens/AccessibilitySettings.tsx
index bf9f5fcb5..4dd5aa97b 100644
--- a/src/view/screens/AccessibilitySettings.tsx
+++ b/src/view/screens/AccessibilitySettings.tsx
@@ -4,6 +4,7 @@ import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useFocusEffect} from '@react-navigation/native'
 
+import {IS_INTERNAL} from '#/lib/app-info'
 import {usePalette} from '#/lib/hooks/usePalette'
 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
 import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
@@ -26,6 +27,7 @@ import {ToggleButton} from '#/view/com/util/forms/ToggleButton'
 import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
 import {Text} from '#/view/com/util/text/Text'
 import {ScrollView} from '#/view/com/util/Views'
+import {AccessibilitySettingsScreen as NewAccessibilitySettingsScreen} from '#/screens/Settings/AccessibilitySettings'
 import {atoms as a} from '#/alf'
 import * as Layout from '#/components/Layout'
 
@@ -33,7 +35,15 @@ type Props = NativeStackScreenProps<
   CommonNavigatorParams,
   'AccessibilitySettings'
 >
-export function AccessibilitySettingsScreen({}: Props) {
+export function AccessibilitySettingsScreen(props: Props) {
+  return IS_INTERNAL ? (
+    <NewAccessibilitySettingsScreen {...props} />
+  ) : (
+    <LegacyAccessibilitySettingsScreen {...props} />
+  )
+}
+
+function LegacyAccessibilitySettingsScreen({}: Props) {
   const pal = usePalette('default')
   const setMinimalShellMode = useSetMinimalShellMode()
   const {isMobile, isTabletOrMobile} = useWebMediaQueries()
diff --git a/src/view/screens/Settings/DisableEmail2FADialog.tsx b/src/view/screens/Settings/DisableEmail2FADialog.tsx
index e4341fcd2..e3d088517 100644
--- a/src/view/screens/Settings/DisableEmail2FADialog.tsx
+++ b/src/view/screens/Settings/DisableEmail2FADialog.tsx
@@ -108,7 +108,11 @@ export function DisableEmail2FADialog({
           {error ? <ErrorMessage message={error} /> : undefined}
 
           {stage === Stages.Email ? (
-            <View style={gtMobile && [a.flex_row, a.justify_end, a.gap_md]}>
+            <View
+              style={[
+                a.gap_sm,
+                gtMobile && [a.flex_row, a.justify_end, a.gap_md],
+              ]}>
               <Button
                 testID="sendEmailButton"
                 variant="solid"
@@ -157,7 +161,11 @@ export function DisableEmail2FADialog({
                   />
                 </TextField.Root>
               </View>
-              <View style={gtMobile && [a.flex_row, a.justify_end]}>
+              <View
+                style={[
+                  a.gap_sm,
+                  gtMobile && [a.flex_row, a.justify_end, a.gap_md],
+                ]}>
                 <Button
                   testID="resendCodeBtn"
                   variant="ghost"
diff --git a/src/view/screens/Settings/ExportCarDialog.tsx b/src/view/screens/Settings/ExportCarDialog.tsx
index 1d8d26471..2de3895d3 100644
--- a/src/view/screens/Settings/ExportCarDialog.tsx
+++ b/src/view/screens/Settings/ExportCarDialog.tsx
@@ -10,6 +10,7 @@ import * as Toast from '#/view/com/util/Toast'
 import {atoms as a, useTheme} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import * as Dialog from '#/components/Dialog'
+import {Download_Stroke2_Corner0_Rounded as DownloadIcon} from '#/components/icons/Download'
 import {InlineLinkText} from '#/components/Link'
 import {Loader} from '#/components/Loader'
 import {Text} from '#/components/Typography'
@@ -76,6 +77,7 @@ export function ExportCarDialog({
             label={_(msg`Download CAR file`)}
             disabled={loading}
             onPress={download}>
+            <ButtonIcon icon={DownloadIcon} />
             <ButtonText>
               <Trans>Download CAR file</Trans>
             </ButtonText>
diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx
index ce21a043b..7ec7b5dce 100644
--- a/src/view/screens/Settings/index.tsx
+++ b/src/view/screens/Settings/index.tsx
@@ -18,7 +18,7 @@ import {useLingui} from '@lingui/react'
 import {useFocusEffect, useNavigation} from '@react-navigation/native'
 import {useQueryClient} from '@tanstack/react-query'
 
-import {appVersion, BUNDLE_DATE, bundleInfo} from '#/lib/app-info'
+import {appVersion, BUNDLE_DATE, bundleInfo, IS_INTERNAL} from '#/lib/app-info'
 import {STATUS_PAGE_URL} from '#/lib/constants'
 import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
 import {useCustomPalette} from '#/lib/hooks/useCustomPalette'
@@ -53,6 +53,7 @@ import * as Toast from '#/view/com/util/Toast'
 import {UserAvatar} from '#/view/com/util/UserAvatar'
 import {ScrollView} from '#/view/com/util/Views'
 import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
+import {SettingsScreen as NewSettingsScreen} from '#/screens/Settings/Settings'
 import {atoms as a, useTheme} from '#/alf'
 import {useDialogControl} from '#/components/Dialog'
 import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
@@ -137,7 +138,15 @@ function SettingsAccountCard({
 }
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
-export function SettingsScreen({}: Props) {
+export function SettingsScreen(props: Props) {
+  return IS_INTERNAL ? (
+    <NewSettingsScreen {...props} />
+  ) : (
+    <LegacySettingsScreen {...props} />
+  )
+}
+
+function LegacySettingsScreen({}: Props) {
   const queryClient = useQueryClient()
   const pal = usePalette('default')
   const {_} = useLingui()
diff --git a/src/view/screens/Storybook/Settings.tsx b/src/view/screens/Storybook/Settings.tsx
new file mode 100644
index 000000000..6bc293c73
--- /dev/null
+++ b/src/view/screens/Storybook/Settings.tsx
@@ -0,0 +1,134 @@
+import React from 'react'
+import {View} from 'react-native'
+
+import * as Toast from '#/view/com/util/Toast'
+import * as SettingsList from '#/screens/Settings/components/SettingsList'
+import {atoms as a, useTheme} from '#/alf'
+import {Alien_Stroke2_Corner0_Rounded as AlienIcon} from '#/components/icons/Alien'
+import {BirthdayCake_Stroke2_Corner2_Rounded as BirthdayCakeIcon} from '#/components/icons/BirthdayCake'
+import {BubbleInfo_Stroke2_Corner2_Rounded as BubbleInfoIcon} from '#/components/icons/BubbleInfo'
+import {CircleQuestion_Stroke2_Corner2_Rounded as CircleQuestionIcon} from '#/components/icons/CircleQuestion'
+import {Envelope_Stroke2_Corner2_Rounded as EnvelopeIcon} from '#/components/icons/Envelope'
+import {Explosion_Stroke2_Corner0_Rounded as ExplosionIcon} from '#/components/icons/Explosion'
+import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe'
+import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller'
+import {Person_Stroke2_Corner2_Rounded as PersonIcon} from '#/components/icons/Person'
+import {Pizza_Stroke2_Corner0_Rounded as PizzaIcon} from '#/components/icons/Pizza'
+import {RaisingHand4Finger_Stroke2_Corner2_Rounded as HandIcon} from '#/components/icons/RaisingHand'
+import {Verified_Stroke2_Corner2_Rounded as VerifiedIcon} from '#/components/icons/Verified'
+import {Window_Stroke2_Corner2_Rounded as WindowIcon} from '#/components/icons/Window'
+import {Text} from '#/components/Typography'
+
+export function Settings() {
+  const t = useTheme()
+  return (
+    <View style={{marginLeft: -20, marginRight: -20}}>
+      <Text style={{marginLeft: 20, paddingBottom: 12}}>Settings</Text>
+      <SettingsList.LinkItem to="/settings" label="Account">
+        <SettingsList.ItemIcon icon={PersonIcon} />
+        <SettingsList.ItemText>Account</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="Privacy and security">
+        <SettingsList.ItemIcon icon={PaintRollerIcon} />
+        <SettingsList.ItemText>Privacy and security</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="Moderation">
+        <SettingsList.ItemIcon icon={HandIcon} />
+        <SettingsList.ItemText>Moderation</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="Content and media">
+        <SettingsList.ItemIcon icon={WindowIcon} />
+        <SettingsList.ItemText>Content and media</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem
+        to="/settings"
+        label="Accessibility and appearance">
+        <SettingsList.ItemIcon icon={PaintRollerIcon} />
+        <SettingsList.ItemText>
+          Accessibilty and appearance
+        </SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="Languages">
+        <SettingsList.ItemIcon icon={EarthIcon} />
+        <SettingsList.ItemText>Languages</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="Help">
+        <SettingsList.ItemIcon icon={CircleQuestionIcon} />
+        <SettingsList.ItemText>Help</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.LinkItem to="/settings" label="About">
+        <SettingsList.ItemIcon icon={BubbleInfoIcon} />
+        <SettingsList.ItemText>About</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.Divider />
+      <SettingsList.PressableItem
+        destructive
+        onPress={() => Toast.show('Sign out pressed')}
+        label="Sign out">
+        <SettingsList.ItemText>Sign out</SettingsList.ItemText>
+      </SettingsList.PressableItem>
+      <SettingsList.Item style={[a.mt_xl]}>
+        <SettingsList.ItemIcon icon={PizzaIcon} />
+        <SettingsList.ItemText>Not pressable</SettingsList.ItemText>
+      </SettingsList.Item>
+      <SettingsList.PressableItem
+        onPress={() => Toast.show('Pressable pressed')}
+        label="Pressable">
+        <SettingsList.ItemIcon icon={AlienIcon} />
+        <SettingsList.ItemText>Pressable</SettingsList.ItemText>
+      </SettingsList.PressableItem>
+      <SettingsList.LinkItem
+        to="/settings"
+        label="Destructive link"
+        destructive>
+        <SettingsList.ItemIcon icon={ExplosionIcon} />
+        <SettingsList.ItemText>Destructive link</SettingsList.ItemText>
+      </SettingsList.LinkItem>
+      <SettingsList.PressableItem
+        label="Email"
+        onPress={() => Toast.show('Email change dialog goes here')}>
+        <SettingsList.ItemIcon icon={EnvelopeIcon} />
+        <SettingsList.ItemText>Email</SettingsList.ItemText>
+        <SettingsList.BadgeText>hello@example.com</SettingsList.BadgeText>
+      </SettingsList.PressableItem>
+      <SettingsList.PressableItem
+        onPress={() => Toast.show('Pressable pressed')}
+        label="Protect your account"
+        style={[
+          a.my_sm,
+          a.mx_lg,
+          a.rounded_md,
+          {backgroundColor: t.palette.primary_50},
+        ]}
+        hoverStyle={[{backgroundColor: t.palette.primary_100}]}
+        contentContainerStyle={[a.rounded_md, a.px_lg]}>
+        <SettingsList.ItemIcon
+          icon={VerifiedIcon}
+          color={t.palette.primary_500}
+        />
+        <SettingsList.ItemText
+          style={[{color: t.palette.primary_500}, a.font_bold]}>
+          Protect your account
+        </SettingsList.ItemText>
+        <SettingsList.Chevron color={t.palette.primary_500} />
+      </SettingsList.PressableItem>
+      <SettingsList.Divider />
+      <SettingsList.Item>
+        <SettingsList.ItemIcon icon={BirthdayCakeIcon} />
+        <SettingsList.ItemText>Birthday</SettingsList.ItemText>
+        <SettingsList.BadgeButton
+          label="Edit"
+          onPress={() => Toast.show('Show edit birthday dialog')}
+        />
+      </SettingsList.Item>
+      <SettingsList.LinkItem to="/settings" label="Long test">
+        <SettingsList.ItemIcon icon={ExplosionIcon} />
+        <SettingsList.ItemText>
+          long long long long long long long long long long long long long long
+          long long long long long long long long long long long long long long
+          long long long long long long long long long
+        </SettingsList.ItemText>
+      </SettingsList.LinkItem>
+    </View>
+  )
+}
diff --git a/src/view/screens/Storybook/index.tsx b/src/view/screens/Storybook/index.tsx
index f7ac11ffc..de3d46533 100644
--- a/src/view/screens/Storybook/index.tsx
+++ b/src/view/screens/Storybook/index.tsx
@@ -18,6 +18,7 @@ import {Forms} from './Forms'
 import {Icons} from './Icons'
 import {Links} from './Links'
 import {Menus} from './Menus'
+import {Settings} from './Settings'
 import {Shadows} from './Shadows'
 import {Spacing} from './Spacing'
 import {Theming} from './Theming'
@@ -101,6 +102,8 @@ function StorybookInner() {
 
             <Admonitions />
 
+            <Settings />
+
             <ThemeProvider theme="light">
               <Theming />
             </ThemeProvider>