# Settings navigation redesign — 31 flat pages become 15 categories

Approved 2026-08-23. The sidebar's 31 entries collapse into 15 categories. Multi-subject
categories render an Appearance-style tab strip above the page; single-subject categories
render no strip. Every old page id keeps working forever.

## The taxonomy

| Category (sidebar) | Tabs (leaf page ids) |
|---|---|
| Home | — (`home`) |
| Appearance | — (`appearance`, keeps its internal tabs) |
| Desktop & Dock | — (`desktop`) |
| Displays | — (`displays`) |
| Sound | — (`sound`, Dictation card moves out) |
| Notifications & Focus | — (`notifications`) |
| Input | Keyboard (`shortcuts`), Mouse & Touchpad (`mouse`), Dictation (`dictation`, new) |
| Network & Sharing | Connections (`connectivity`), Firewall (`firewall`), Sharing (`sharing`), Printers (`printers`) |
| Home & Phone | — (`home-phone`) |
| Applications | Applications (`applications`), Gaming (`gaming`), Screen Intelligence (`screen-intelligence`) |
| Users & Accounts | Users (`users`), Online Accounts (`accounts`) |
| Privacy & Security | Privacy (`privacy`), SSH Keys (`ssh-keys`) |
| Power & Lock | — (`power`) |
| Accessibility | — (`accessibility`) |
| System | About (`about`), Software Update (`updates`), System Health (`services`), Storage (`storage`), Snapshots (`snapshots`), Containers (`containers`), Date & Time (`datetime`), Region & Language (`region`), Manual (`manual`) |

## Architecture

`ShellState.settingsPage` keeps holding the **leaf** id, exactly as today. The page loader's
switch, the ownership contracts, HomePage's direct jumps, and every external caller are
untouched by the grouping itself. What changes:

- **`services/SettingsRoutes.qml` (new singleton)** — the one source of truth for the
  taxonomy: `categories` (page, label, icon, tabs), `resolve(id)` (category id → first
  available leaf; leaf → itself; unknown → home), `categoryOf(leaf)`, `tabsFor(leaf)`
  (availability-filtered), `breadcrumb(leaf)`, `pageAvailable(leaf)` (the podman/snapper
  gating, moved from the sidebar).
- **`SettingsSidebar.qml`** — destinations come from `SettingsRoutes.categories`; a row
  highlights when it is the category owning the current leaf; clicking requests the
  category id and `ShellState` resolves it.
- **`ShellState.showSettings`** — the literal allow-list becomes `SettingsRoutes.resolve()`.
- **`SettingsShell.qml`** — renders a `SettingsTabs` strip between the titlebar and the
  page loader whenever the current leaf's category has more than one available tab.
  Tab click is just `openSettings(leafId)`. Adds the `dictation` case.
- **`SettingsTabs.qml`** — the strip becomes horizontally scrollable when it overflows
  (System has nine tabs; the window tiles down to 900px).
- **`SoundPage.qml` / `DictationPage.qml` (new)** — the Dictation card moves to its own
  page under Input, with a labeled handoff noting the microphone is chosen in Sound.
- **`SettingsSearch.qml`** — dictation `extraEntries` re-route to `dictation`; the search
  result subtitle becomes the `SettingsRoutes` breadcrumb ("System › Storage"). Because
  results carry leaf ids, search now lands on the exact tab with no further work.

## Blast radius owned by this change

- `scripts/panama-settings-commands` regex-parses the sidebar's destinations array; it
  moves to parsing `SettingsRoutes.qml` and regenerates the Vicinae commands (one per
  leaf; `home` still skipped; `dictation` gained). `scripts/panama-settings-docs` checked
  the same way.
- Test contracts updated in the same change, not after: `settings-nav-contract`,
  `settings-jump-contract`, `search-routing-contract`, `gnome-handoff-contract`,
  `manual-contract`, `sound-page-contract`, plus any contract found grepping page ids.
- `modules/settings/README.md` gains the navigation model; manual chapter
  `05-making-it-yours.md` updated where it describes the sidebar.

## Non-goals

- No per-tab deep links into Appearance's internal tabs from search (unchanged today).
- No persistence of last-selected tab per category (Appearance already resets; same rule).
- No renaming of leaf ids, files, or schema groups.
