# UI Phase 0 — Design language & theme foundation

> **Mission:** kill the default-MUI/starter look at the token + theme layer in one pass and establish the
> brand's visual system — a `theme.components` pass, a semantic palette, a real Persian type scale, one icon
> family, a designed Balinyaar logomark, extended tokens (elevation/motion/focus/rating/trust/money), and a
> purge of starter residue — so every later phase composes on a system that already looks designed. **No page
> redesigns here**: this phase changes what every screen inherits, not any screen's layout.
>
> **Track:** frontend · **Depends on:** — (first phase of the [UI chain](README.md)) · **Unlocks:**
> [Phase 1](ui-phase-1-primitives-and-states.md), [Phase 2](ui-phase-2-shells-and-navigation.md), and every
> later phase — they all inherit the de-startered look built here.
> **Before you start, read [../../phases/_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md)
> and invoke the frontend-designer skill — both are mandatory.**

## 1. Context — where this sits

Balinyaar's feature layer is unusually disciplined — 331 `var(--bal-*)` usages across 103 files, effectively
zero hard-coded hexes, correct RTL/Shamsi/BigInt-money plumbing — but the *visual system underneath it is
still the karpolan react-mui starter*. Diagnosed root causes (all verified in code):

1. **`createTheme` has zero `components` overrides** — `client/src/theme/theme.ts:19-36` passes only
   `cssVariables`/`colorSchemes`/`typography`/`shape`/`direction`; every Button/Card/TextField/AppBar/Chip/
   Dialog/Alert renders stock Material with recolored primaries. The single biggest cause of the starter feel.
2. **No semantic palette** — `theme/colors.ts` defines only primary/secondary/background/text/divider, so
   inline `<Alert severity>` surfaces render stock MUI green/red while toasts (`lib/toast/NotistackProvider.tsx`)
   use the brand `--bal-*` feedback tokens — two feedback systems for one semantic state. Worse, a bare
   `<AppAlert>` defaults to a **filled stock-red error** (`components/config.ts:10-11`).
3. **Persian typography is Roboto metrics** — `TYPOGRAPHY_RTL` (`theme/typography.ts:43-52`) sets only
   `fontFamily` + weights: non-zero letter-spacing on a joined script, tight heading leading, and a requested
   weight **600** (h6, button) that the Mikhak loader (`app/[locale]/layout.tsx:39-48`, weights 400/500/700)
   renders as full Bold — repeated in 68 `fontWeight: 600` sx usages across 42 files (verified). Space
   Grotesk (`--font-space-grotesk`) is declared for EN but never loaded.
4. **The brand mark is the starter's Twemoji cartoon pencil** — `AppIcon/config.ts:115` registers
   `logo: PencilIcon`; `components/auth/BrandMark.tsx:21` renders it at 56px on every auth screen and passes
   `color="var(--bal-primary)"`, which the hard-coded fills (`#EA596E`, `#FFCC4D`, …) silently ignore.
5. **The icon system is broken and incoherent** — `AppIcon.tsx:38-46` passes `size` as SVG `width`/`height`
   *attributes*, which MUI `SvgIcon`'s `1em` class CSS overrides, so every `size={14..56}` call-site renders
   24px. The ~87-entry registry mixes filled starter icons against `*Outlined` feature icons, carries eight
   dead entries (zero usages, verified), and has **no back/chevron icon at all** (only `expand`).
6. **Starter residue everywhere else:** `AppButton`'s `DEFAULT_SX_VALUES = { margin: 1 }` neutralized by
   **213 `m: 0` occurrences across 82 files**; dead `theme/light.ts`/`dark.ts` exported from `theme/index.ts`
   incl. `LIGHT_THEME as default` (nothing else imports them); the 17-line starter `globals.css`; the wrong
   `CONTENT_MIN_WIDTH` comment (`components/config.ts:5`); unused `AppImage` (all verified); a cookie-less
   OS-dark first visit paints light first (`lib/cookies/server.ts:46` returns `'light'`, no pre-paint script
   exists — acknowledged doc drift in `client/CLAUDE.md`); `viewport.themeColor` hard-coded to light teal
   (`layout.tsx:50-52`); `public/site.webmanifest` still ships starter `#000000`/`#ffffff` and references
   `img/favicon/*.png` files that don't exist.

**What already exists (do not rebuild):**

- The **two-layer token system** — `theme/tokens.css` (`--bal-*`, scheme-keyed on `data-mui-color-scheme`)
  mirrored by `theme/colors.ts`, sync rule in both headers. Extend it; never bypass it.
- The **MUI v9 RTL dual-theme setup** (`APP_THEME_LTR`/`APP_THEME_RTL` built once at module load,
  `stylis-plugin-rtl` Emotion cache in `ThemeProvider.tsx`) and the **cookie-SSR color-scheme sync**
  (`getThemeMode()` → `data-mui-color-scheme` stamped server-side; explicit `colorSchemeSelector`).
- **Per-locale font loading** (Mikhak via `next/font/local`, `preload: false`, attached only on `fa`),
  the **AppIcon string registry** with its snake_case domain names (the exact indirection that makes this
  phase's icon swap a config-level change), **brand-styled notistack toasts**, and the correct global
  decisions already made: `shape.borderRadius: 10`, `textTransform: 'none'`, the lifted dark-teal palette.

## 2. Required reading (do this first)

- The audits: [audit/theme-and-brand.md](audit/theme-and-brand.md),
  [audit/component-primitives.md](audit/component-primitives.md),
  [audit/cross-cutting-ux.md](audit/cross-cutting-ux.md) — the full file/line evidence and the
  **Keep (do not regress)** lists this phase must honor.
- [../../../.claude/skills/frontend-designer/SKILL.md](../../../.claude/skills/frontend-designer/SKILL.md) —
  the design contract you are extending (invoke the skill; this phase also *updates* it — see 3.10).
- `client/CLAUDE.md` — "Golden rules", "Theme System" (the cookie/no-flash machinery and the MUI v9 traps:
  explicit `colorSchemeSelector`, never `InitColorSchemeScript`, never `storageWindow`), and "Fonts".
- Code, in this order: all of `client/src/theme/`, `components/config.ts`, `components/common/AppIcon/`
  (+ `icons/PencilIcon.tsx`), `components/common/AppButton/AppButton.tsx`, `components/auth/BrandMark.tsx`,
  `lib/toast/NotistackProvider.tsx`, `app/[locale]/layout.tsx`, `app/globals.css`, `lib/cookies/server.ts`
  (`getThemeMode`), `public/site.webmanifest`.
- `product/overview/platform-summary.md` — the tone (trust-first, calm, clinical-but-human) every visual
  decision here must serve.

## 3. Scope — build this

### 3.1 The `theme.components` pass (the highest-leverage change in the app)

Add a `components` block inside `createAppTheme` (`theme/theme.ts`) that encodes the brand once. Reference
colors as `var(--bal-*)` tokens (or `theme.vars.palette.*`) so every override is scheme-correct — never a
hex. Overrides must be direction-safe (logical properties only — both theme directions share them). Cover at
least: **MuiButton** (`disableElevation`, comfortable `paddingInline`, weight per the 3.3 decision, keep
`textTransform: 'none'`); **MuiPaper/MuiCard** (hairline `1px solid var(--bal-divider)` border + soft
**teal-tinted** shadows from the 3.4 elevation tokens instead of MUI's grey stack on warm cream);
**MuiAppBar** (paper/cream surface + hairline bottom divider, **not** solid primary — shells are rebuilt in
phase 2, this stops the default bar screaming "starter" now); **MuiOutlinedInput/MuiTextField** (house
radius, calmer resting border, brand focus ring); **MuiChip / MuiToggleButton(Group) / MuiListItemButton**
(soft `--bal-primary-soft`/`--bal-secondary-soft` tonal fills; `selected` = primary-soft + primary text —
tokens already exist in both schemes); **MuiDialog** (radius 16, sane mobile margins); **MuiTabs** (thicker
indicator, comfortable min-height); **MuiAlert** (severities from the 3.2 semantic palette so inline alerts
match toasts); **MuiStepper/MuiStepIcon** (brand-colored active/completed steps — the trust flows live on
these); **MuiSkeleton** (warm tint per scheme); **MuiTooltip** (ink/cream inversion, house radius); and
**MuiCssBaseline** carrying a global `:focus-visible` treatment off `--bal-focus-ring` (2px ring + offset —
today exactly **one** `:focus-visible` style exists in the app, in `NurseResultCard.tsx`; after this,
keyboard focus is uniform everywhere).

### 3.2 Semantic palette — one feedback language

- Add `success`/`error`/`warning`/`info` (main + contrastText, light **and** dark) to
  `LIGHT_PALETTE`/`DARK_PALETTE` in `colors.ts`, sourced from the existing `--bal-*` feedback values in
  `tokens.css` (light: `#1f6b50`/`#a8392a`/`#8a6418`/`#1d4a40`; dark: the lifted set). Keep the two files
  mirror-synced. Alert, `Chip color="success"`, Badge, LinearProgress become brand-harmonized automatically.
- Fix the `AppAlert` footgun in `components/config.ts`: first make severity explicit at any call-site that
  genuinely means error, then change the defaults to a calm baseline (`severity="info"`,
  `variant="standard"` on the new tinted severities — decide and document in the config file).

### 3.3 A real Persian type scale

In `theme/typography.ts` `TYPOGRAPHY_RTL`:

- **`letterSpacing: 0` on every variant** — Persian is a joined script; tracking breaks Mikhak's glyph
  connections (MUI's Latin defaults set non-zero spacing on body1/body2/button/caption/overline).
- **Body line-height ≥ 1.7; headings ~1.4–1.5** (room for Persian ascenders/descenders) plus **responsive
  heading sizes** (MUI's default 6rem h1 is unusable; grep confirms zero h1/h2 usages today).
- **Resolve the weight-600 problem.** Mikhak loads 400/500/700 only, so every requested 600 silently renders
  700. Decide: (a) adopt **500/700 semantics** — map the theme's h6/button and the 68 `fontWeight: 600` sx
  usages across 42 files to loaded weights (recommended: 700 headings/buttons, 500 in-text emphasis; the
  sweep is mechanical) — or (b) add a genuine Mikhak DemiBold face to `src/app/fonts/` + the loader. Pick
  one, do it fully, **document the decision in `typography.ts` and the skill (3.10)**.
- **Settle the EN display font**: wire Space Grotesk via `next/font` in `app/[locale]/layout.tsx` (attached
  only on `en`, mirroring the Mikhak pattern, `preload: false`) **or** delete the dead
  `BRAND_FONT_VARIABLE_EN` variable and its fallback stack. Decide and do it — no vaporware comment left.

### 3.4 Token extension — beyond color

Extend `theme/tokens.css` (**both scheme blocks — always**), mirroring palette-level values in `colors.ts`:

- **Elevation/shadow tokens** — 2–3 steps of teal-tinted shadows (light: `rgba(29,74,64,α)` stacks; dark:
  black-teal), consumed by 3.1's Paper/Card/Dialog overrides.
- **Radius scale** — document the house scale (e.g. 4 / 10 / 16: controls / cards / dialogs) as tokens or
  constants next to `shape.borderRadius`; stop inventing radii per component.
- **Motion tokens** — durations (e.g. 120/200/300ms) + easings; defined once here, consumed by phase 12 —
  and **`--bal-focus-ring`**, the global focus ring used by 3.1's `:focus-visible` treatment.
- **`--bal-rating` + `--bal-rating-empty`** — proper star gold + empty color per scheme (`RatingInput`'s
  muddy `--bal-warning` stars are fixed in **phase 1** using these); **`--bal-money-emphasis`** — a
  contrast-safe money-text emphasis per scheme, retiring terracotta as a small-text money color (`#d98c6a`
  on white fails AA contrast); **`--bal-trust` + `--bal-trust-soft`** — a distinct trust identity for
  verified marks (not generic primary/success), consumed by phases 1/4/8. All define-only here.
- Update the stale `tokens.css` header (it cites `product/balinyaar.html`, which no longer exists in the
  repo) to point at the frontend-designer skill as the brand source of truth (3.10).

### 3.5 A real brand mark — kill the pencil

- Design the Balinyaar logomark per the identity the skill records (the `product/balinyaar.html` seed deck
  is **gone from the repo** — recreate from the tokens + the skill's description): deep-teal ground, cream
  lowercase glyph, a single terracotta dot. Build two SVGs under `AppIcon/icons/`: a **monochrome
  `currentColor` logomark** (so `color="var(--bal-primary)"` finally works and it recolors in dark mode for
  free) and a **full lockup** (mark + wordmark) for `BrandMark`/auth.
- Register the mark as `ICONS.logo` (replacing `PencilIcon`), update `components/auth/BrandMark.tsx` to the
  new lockup, **delete `icons/PencilIcon.tsx`** (the last hard-coded-hex SVG in the app), regenerate
  `src/app/favicon.ico` from the mark, and fix `public/site.webmanifest` — brand
  `theme_color`/`background_color` (currently starter `#000000`/`#ffffff`) and icon entries that actually
  exist (generate the referenced PNG sizes from the mark, or trim the manifest — today it points at missing
  `img/favicon/*.png`).

### 3.6 Icon system — one family, working sizes, full vocabulary

- **Fix the size bug** in `AppIcon.tsx`: drive `fontSize` via `style`/`sx` instead of `width`/`height`
  attributes (which MUI `SvgIcon`'s `1em` class CSS beats) — all ~40 existing `size={14..56}` call-sites
  start working simultaneously. Stop spreading the invalid `size` attribute onto the DOM `<svg>`; make the
  unknown-name `console.warn` **dev-only**; keep the custom-SVG path (the new logomark) scaling correctly.
- **Normalize the ~87-entry registry to ONE visual family** — recommend the **Rounded** variants of
  `@mui/icons-material` (zero new deps, warmer than the current filled/outlined mix, fits
  clinical-but-human). A `config.ts`-only sweep thanks to the registry indirection. Delete the eight dead
  starter entries (`daynight`/`night`/`day`/`visibilityon`/`visibilityoff`/`signup`/`login`/`settings` —
  re-verify zero usages before each delete).
- **Add the missing vocabulary**: `back`/`chevron_start` (no directional nav glyph exists today except
  `expand`), `share`, `copy`, `phone` (non-emergency call), `navigate` (directions), `sort`, `attachment`,
  `star_half`. Note `camera`, `calendar`, `wallet`, and `tune` (filter) are **already registered** — they
  only need family normalization. **Directional icons auto-mirror**: register `back`/`chevron_start` as the
  LTR glyph, mirrored via a `[dir="rtl"]` `scaleX(-1)` rule applied by `AppIcon` for a declared
  `DIRECTIONAL_ICONS` set in `config.ts`; write the rule into the registry comment and the skill — later
  phases must not hand-roll flips.

### 3.7 AppButton de-startering

- Remove `DEFAULT_SX_VALUES` (`margin: 1`), then sweep the now-no-op **213 `sx={{ m: 0 }}` neutralizations
  across 82 files** (mechanical: remove the key; drop `sx` when it becomes empty). Outer spacing becomes the
  parent's job (Stack/Box gaps). Clean the starter prop cruft: duplicate `label`/`text` props (pick one,
  migrate the loser's call-sites), the false "Box around to specify margins" JSDoc, the `// Missing props`
  comment, and the `underline` spread onto non-link buttons (`AppButton.tsx:85`).
- **Do not break the public API**: `to`/`href` auto-link composition, icon-name `startIcon`/`endIcon`, and
  non-MUI-color-becomes-text-color all stay (238 call-sites). Leave the `color='inherit'` default as-is —
  changing every unspecified button's color is a per-screen decision for later phases. Update
  `AppButton.test.tsx` (and every other touched shared component's test) in the same change.

### 3.8 Starter residue purge

- Delete `theme/light.ts` + `theme/dark.ts` and their exports from `theme/index.ts` (verified: nothing else
  imports them; drop `LIGHT_THEME`/`DARK_THEME`/`APP_THEME`/`LIGHT_THEME as default` — keep `ThemeProvider`,
  `getDirection`, `APP_THEME_LTR/RTL`). Delete the deprecated `TYPOGRAPHY` alias in `typography.ts` once
  nothing imports it.
- Rewrite `app/globals.css` as an intentional minimal base: keep the `box-sizing` reset, **drop
  `max-height: 100vh`** and the `max-width: 100vw; overflow-x: hidden` mask, add `::selection` in brand
  colors, and a commented decision on the `a { color: inherit }` reset (AppLink owns link affordance).
- Fix the wrong `CONTENT_MIN_WIDTH` comment (`components/config.ts:5`), prune the starter
  commented-out-alternatives style there into owned decisions, and delete the unused `AppImage` + its test +
  barrel export (verified: zero product usages).

### 3.9 No-flash color-scheme boot

- A cookie-less OS-dark first visit paints light, then flips. Implement the **`ColorSchemeScript`** that
  `client/CLAUDE.md`'s Theme System section already documents (acknowledged doc drift): an inline `<head>`
  script in `app/[locale]/layout.tsx` that reads the `color-scheme` cookie, falls back to
  `matchMedia('(prefers-color-scheme: dark)')` when absent, and sets `data-mui-color-scheme` before first
  paint — plus the documented `Storage.prototype` patch. **Never** MUI's `InitColorSchemeScript` (banned in
  `client/CLAUDE.md` — it reads localStorage, which diverges from the cookie). The script must agree with
  `getThemeMode()` on every path. Switch `viewport.themeColor` (`layout.tsx:50-52`) to the media-query array
  form: light → `BRAND.teal`, `(prefers-color-scheme: dark)` → `BRAND.tealDeep`, so browser chrome matches
  the page in both schemes.

### 3.10 Keep the design contract honest

Update `.claude/skills/frontend-designer/SKILL.md` **in the same change**: the new tokens (elevation, radius,
motion, focus, rating, trust, money-emphasis) and when to use each; the type-scale + weight decision (its
"buttons weight 600" rule becomes wrong the moment 3.3 lands); the normalized icon family + directional-
mirroring rule + a corrected registered-icons note (it lists ~19 of ~87 icons and still names the dead
starter entries); and the brand-mark construction as the written source of truth now that
`product/balinyaar.html` is gone (fix its reference; `tokens.css`'s header points here too).

**(DEFERRED → phase 1):** all new shared primitives — EmptyState/ErrorState, PageHeader, `<Money>`, Jalali
picker, StatusChip v2, RatingInput's star fix, skeleton twins, route-level `loading.tsx`/`error.tsx`/404.
**(DEFERRED → phase 2):** the shells — TopBar/SideBar chrome, `UserInfo`, nav grouping, SSR mobile-first flash.
**(DEFERRED → phase 12):** the app-wide motion pass that consumes 3.4's motion tokens.

## 4. Mocks & seams in this phase

None. This phase is pure client theming — no service seams, no mock flags. The chain's REQ posture: if a
backend gap surfaces (none is expected here), append a REQ entry to
[../../shared-working-context/frontend/requests/for-backend.md](../../shared-working-context/frontend/requests/for-backend.md)
— REQ-001…038 are taken (verified: the tracker ends at REQ-038); number onward from **REQ-039**. UI stays
mock-tolerant behind the existing `services/{domain}` seams; never edit `server/`.

## 5. Critical rules you must not get wrong

- **The two-layer token sync is law** — every color added/changed lands in `tokens.css` (**both** scheme
  blocks) *and* `colors.ts` in the same commit — and **zero hex regressions**: feature code has effectively
  no hard-coded hexes; the theme pass must not reintroduce literals outside the theme layer (deleting
  `PencilIcon` removes the last offender).
- **Do not break the RTL dual-theme/Emotion setup** (`APP_THEME_LTR`/`APP_THEME_RTL` stay module-load-built;
  `createTheme()` never in a component; overrides use logical properties only) **or the cookie-SSR
  color-scheme sync** (`colorSchemeSelector` stays the explicit `'data-mui-color-scheme'`, never `'data'`;
  no `InitColorSchemeScript`; no `storageWindow`).
- **The AppIcon registry indirection stays** — keep the `<AppIcon icon="name">` API, the snake_case domain
  names, the lowercase keys. **AppButton's composition API stays** (`to`/`href` → AppLink, icon-name
  `startIcon`/`endIcon`) — 238 call-sites; the margin removal is a default change, not an API change.
- **Keep the keep-lists.** Notably: `textTransform: 'none'`, radius 10 as the house radius, the lifted
  dark-teal palette, per-locale font loading (Mikhak never ships to `/en`; mirror that discipline if you
  wire Space Grotesk), and `NotistackProvider` staying token-driven.
- **Design-contract non-negotiables:** any new user-facing string (e.g. the logo's aria/alt) goes in
  **both** `messages/en.json` and `messages/fa.json`; MUI v9 API only; co-located `*.test.tsx` updated for
  every touched shared component (`AppIcon`, `AppButton`, `BrandMark`, …); fetch/cookies rules untouched.
- **Update the skill and `client/CLAUDE.md` in the same change** (3.10 + §8) — stale design docs are how the
  next phase reintroduces the starter.

## 6. Definition of Done

On top of the shared [definition-of-done.md](../../phases/_shared/definition-of-done.md):

- [ ] `npm run check` green; `npm run test:ci` green incl. updated tests for every touched shared component;
      `en.json`/`fa.json` in sync.
- [ ] `theme.ts` has a `components` block covering at least the 3.1 list; no override uses a raw hex or a
      physical direction property.
- [ ] `LIGHT_PALETTE`/`DARK_PALETTE` define success/error/warning/info; an inline
      `<Alert severity="success">` and a success toast are visibly the same color family in both schemes.
- [ ] `TYPOGRAPHY_RTL` has `letterSpacing: 0` everywhere + the new line-heights and responsive heading
      sizes; no `fontWeight: 600` remains in `src` (or a real 600 face is loaded — per the documented
      decision); the Space Grotesk question is resolved (wired or deleted).
- [ ] `tokens.css` (both blocks) carries elevation, radius, motion, focus-ring, rating, money-emphasis, and
      trust tokens, mirrored in `colors.ts` where palette-level.
- [ ] `ICONS.logo` is the new Balinyaar mark; `PencilIcon.tsx`, `light.ts`, `dark.ts`, `AppImage`, and the
      eight dead icon entries are deleted; favicon + webmanifest rebuilt with brand colors and only real
      files referenced.
- [ ] `<AppIcon icon="verified" size={48} />` actually renders 48px; the registry is one visual family;
      `back`/`chevron_start` mirror correctly under `dir="rtl"`; `AppButton` has no default margin and
      **zero** `sx={{ m: 0 }}` neutralizations remain in `src`.
- [ ] First visit with OS dark preference and no cookie paints dark with no light flash; browser
      `theme-color` matches the scheme in both modes.
- [ ] Visual verification on the four axes — `/fa` + `/en` × light + dark — and mobile + desktop widths on
      the §7 walk, with screenshots in the report; skill + `client/CLAUDE.md` updated to match reality.

## 7. How to test (what a human can verify after this phase)

1. Open `/fa/login` (private window, OS set to dark, no cookies): the page paints **dark immediately** — no
   light flash — and shows the **new Balinyaar logomark** (not a pencil), recoloring correctly in dark mode.
2. On the customer home `/fa`: buttons are flat (no elevation) with comfortable padding; cards show hairline
   borders + soft teal-tinted shadows (not grey); chips are soft-tinted; nothing renders stock-MUI grey/blue.
   Tab through: every focusable element shows the same 2px brand focus ring.
3. Visit `/fa/nurse` and `/fa/admin` (seeded accounts): the top bar is a cream/paper surface with a hairline
   divider — **not** a solid teal slab; sidebar icons are one visual family (all Rounded); the selected nav
   item uses the soft-primary fill.
4. Trigger an inline alert (e.g. a form error state) and a toast on the same screen: both use the same
   brand-harmonized semantic colors, in light and dark.
5. On `/fa`, inspect Persian text: no letter-spacing gaps inside joined words, headings don't clip
   ascenders, bold shows a real weight hierarchy (not everything Bold). Compare `/en`: headings render per
   the 3.3 decision (Space Grotesk or the documented system stack).
6. Check the browser tab: the favicon is the new mark; toggling OS dark mode flips the browser chrome
   (`theme-color`) to the deep-teal value.

## 8. Hand off & document (close the phase)

- Update `client/CLAUDE.md`: the Theme System section (`ColorSchemeScript` now real — remove the doc-drift
  note; new tokens; the weight decision), the theme entries in "Project Structure" (`light.ts`/`dark.ts`
  removed, new icon assets), and the Fonts table if Space Grotesk was wired. Update
  `.claude/skills/frontend-designer/SKILL.md` per 3.10 (part of the phase, not optional).
- Write the frontend report at `dev/shared-working-context/reports/ui-phase-0-report.md`: what changed at the
  theme layer, the type-scale + weight + icon-family decisions, before/after screenshots on the four axes,
  what later phases must know (new token names, directional-icon rule), and any REQs filed (expected: none).
- Save a memory note per operating-rules §8: the theme/token/icon/brand decisions, the size-bug fix, the
  margin-default removal, and the no-flash boot mechanism — later UI phases build on all of them.
