diff --git a/docs/release/v0.3-tactile-quietware.md b/docs/release/v0.3-tactile-quietware.md
index 19c77df..a7d79b5 100644
--- a/docs/release/v0.3-tactile-quietware.md
+++ b/docs/release/v0.3-tactile-quietware.md
@@ -220,6 +220,30 @@ Architectural finding: both `LumotiaStatusPill` and `LumotiaNotice` already ship
- All four tones (info / caution / danger / success) verified by the pattern itself: the visible signal is the left bar, which is `border-l-{tone}` at 100% opacity, working at any colour intensity. Caution remains a fill-only convention on cream by design.
+### Phase 4a — Settings tab shell. Landed 2026-05-15.
+
+Architectural finding: the v0.2 `SettingsPage.svelte` (2 891 LOC after Phase 4a) was already structured as eight numbered sections that align almost 1:1 with the v0.3 plan. The cheapest path to "one section visible at a time" is therefore a tab-shell overlay rather than a file split. Behaviour stays identical to v0.2 when quietware is off; with quietware on, only the active tab's section renders.
+
+- New `LumotiaSettingsTabs.svelte` ([src/lib/ui/LumotiaSettingsTabs.svelte](../../src/lib/ui/LumotiaSettingsTabs.svelte)) implements the WAI-ARIA tabs (automatic activation) pattern. Arrow / Home / End keyboard navigation. Active tab uses the brand left-bar accent (`border-l-caution`) so it carries the same grammar as the LumotiaNotice refactor.
+
+- `SettingsPage.svelte` gained a small reactive `isQuietware` flag, a `MutationObserver` watching ``, and 8 `{#if !isQuietware || activeTab === 'X'}` wrappers around the existing eight top-level `` sections. The settings tree is unchanged; only its top-level rendering gate changed.
+
+- v0.2 fallback behaviour: when `data-design="quietware"` is absent the tablist does not render and all eight sections stack as before. The search filter still spans every section in v0.2 mode. This is the smallest-possible behavioural change for "one section at a time".
+
+- Tab IDs in this commit map 1:1 to existing v0.2 section names (start-here / transcription / models / tasks / accessibility / privacy / advanced / help). Phase 4b will restructure to match the plan's canonical names (carving Output from Advanced and Vocabulary from Transcription).
+
+### Phase 4b — Settings restructure to canonical tab names. Pending.
+
+- Carve **Output** out of `7. Advanced > Output & Capture` (currently at line 2398 in the pre-Phase-4a file) into its own top-level tab.
+- Carve **Vocabulary** out of `2. Transcription > Custom vocabulary` (line 1472 in the pre-Phase-4a file) into its own top-level tab.
+- Rename the remainder of `2. Transcription` to be subsumed by **Output** + **Vocabulary**, or kept as legacy section pending content audit.
+- Final tab order: Start Here / Models / Output / Vocabulary / Tasks / Accessibility / Privacy / Advanced (eight tabs, matching the plan).
+
+### Phase 4c — Texture-opacity slider + high-contrast toggle. Pending.
+
+- Texture-opacity slider in the Accessibility tab, range 0.05–0.08, default 0.06. Persists to existing preferences store.
+- High-contrast toggle in the Accessibility tab. Toggle overrides system `prefers-contrast: more` query when set.
+
---
## Regression diary
diff --git a/src/lib/pages/SettingsPage.svelte b/src/lib/pages/SettingsPage.svelte
index 96a4b5f..e794b5d 100644
--- a/src/lib/pages/SettingsPage.svelte
+++ b/src/lib/pages/SettingsPage.svelte
@@ -10,6 +10,7 @@
import HotkeyRecorder from "$lib/components/HotkeyRecorder.svelte";
import ImplementationRulesEditor from "$lib/components/ImplementationRulesEditor.svelte";
import SettingsGroup from "$lib/ui/LumotiaSettingsGroup.svelte";
+ import SettingsTabs from "$lib/ui/LumotiaSettingsTabs.svelte";
import StatusPill from "$lib/ui/LumotiaStatusPill.svelte";
import ZonePicker from "$lib/components/ZonePicker.svelte";
import AccessibilityControls from "$lib/components/AccessibilityControls.svelte";
@@ -1212,6 +1213,42 @@
saveTemplates();
editingTemplate = -1;
}
+
+ // v0.3 Phase 4a — sectional tab nav. The full 2 791 LOC settings tree
+ // stays in this file; quietware mode shows ONE top-level section at a
+ // time via {#if !isQuietware || activeTab === 'X'} wrappers below.
+ // Without quietware (data-design unset on ) the page renders
+ // exactly as v0.2 did (all eight sections stacked, search filter
+ // active across all of them).
+ let isQuietware = $state(false);
+ let activeTab = $state("start-here");
+ const settingsTabs = [
+ { id: "start-here", label: "Start Here" },
+ { id: "transcription", label: "Transcription" },
+ { id: "models", label: "Models" },
+ { id: "tasks", label: "Tasks" },
+ { id: "accessibility", label: "Accessibility" },
+ { id: "privacy", label: "Privacy" },
+ { id: "advanced", label: "Advanced" },
+ { id: "help", label: "Help" },
+ ];
+ let quietwareObserver: MutationObserver | null = null;
+ onMount(() => {
+ if (typeof document === "undefined") return;
+ const sync = () => {
+ isQuietware = document.documentElement.dataset.design === "quietware";
+ };
+ sync();
+ quietwareObserver = new MutationObserver(sync);
+ quietwareObserver.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ["data-design"],
+ });
+ });
+ onDestroy(() => {
+ quietwareObserver?.disconnect();
+ quietwareObserver = null;
+ });