deckhearth/.convoys/liquid-glass-design-tokens.md

360 lines
17 KiB
Markdown
Raw Normal View History

feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95) * feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds. Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface. Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in. Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued. Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract. Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations). Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued. Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist. Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals. Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree. Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio": - Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges. - preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces. - Vercel promote to production once smoke + visual gates pass. - Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation). The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView The portfolio squash inadvertently overwrote the pre-existing lib/use-focus-trap.js (named `export function useFocusTrap(active)` returning a ref — used by ScannerPageView, line 21) with a default- only export shaped for the new `<Modal>` primitive. Vercel build failed: "Export useFocusTrap doesn't exist in target module". Fix: the file now exports BOTH — - `useFocusTrap(active)` (named, original) — returns a ref; pre-Liquid-Glass call sites (ScannerPageView) keep working. - `useFocusTrapContainer({ active, containerRef, ... })` (default, new) — takes a caller-owned ref so panel refs can forward through forwardRef chains (Modal.js consumes this shape). Both hooks are commented to document which to use when. Modal.js imports default already, so no change needed there. Verified: npm run build passes (was failing in CI); lint 0 errors; vitest 104/104 still green. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
---
name: liquid-glass-design-tokens
classification: feature
success_metric: |
styles/globals.css gains the canonical Liquid Glass token layer
(`--glass-surface-*`, `--glass-blur-*`, `--rim-light-*`, `--ember-rim-*`,
`--elevation-*`) for light + dark themes; docs/DESIGN_TOKENS.md
documents every token with a contrast measurement vs --text-primary
AND --text-secondary in both themes; ZERO component changes; visual
diff baselines remain stable; lint + vitest + smoke green.
skip:
- ux
- ia
- qa
- flag
status: merged
created: 2026-06-03
merged: 2026-06-03
depends_on: []
umbrella: liquid-glass-redesign
conductor_started: 2026-06-03
---
# Convoy: liquid-glass-design-tokens
Foundation sub-convoy #1 of the `liquid-glass-redesign` epic. Adds the
token layer that every subsequent sub-convoy consumes. Touches CSS +
docs only — **no component or page file is edited here**.
## Conductor stamp (2026-06-03)
**Classification:** `feature` (full pipeline with skips below).
**Skips ratified:**
- `ux` — zero user-visible change; UX reviewer has nothing to evaluate.
- `ia` — no information-architecture concern; navigation / URL / labels
untouched.
- `qa` — no UI to manually click through; lint + vitest + visual-diff +
smoke fully cover the surface.
- `flag` — repo has no feature-flag wrapper (per
`.convoys/liquid-glass-redesign.md` § Hard scoping rules).
**Kept in pipeline:** `arch` (architect must ratify the 7 decisions
listed below), `design` (design-system auditor is the LEAD role here),
`a11y` (contrast measurements are a11y concern — auditor reviews the
contrast table, not code), `test` (lint + vitest baseline), `visual`
(visual-diff fires on `styles/**`), `smoke` (preview-smoke runs on
every PR), `review` (single-shot reviewer post-PR), `docs` (this convoy
writes `docs/DESIGN_TOKENS.md`).
**Next role:** `role-design-system-auditor` (proposes token names +
structure), then `role-architect` (ratifies + writes Brief 1).
## Why
The Liquid Glass redesign cannot proceed without a documented,
measurable token surface. Today `styles/globals.css` defines:
- Color tokens (`--bg-primary` / `--bg-secondary` / etc.)
- Three gradients (`--gradient-primary` / `--gradient-secondary` /
`.gradient-bg-ember`)
- A handful of glow utility classes (`.fire-glow`, `.ember-glow`,
`.card-mythic-glow`, etc.)
- RGB-component triples for backdrop-blur effects
(`--bg-primary-rgb`, etc.)
What it does NOT define is a *surface* token (translucency + blur +
rim-light + elevation) — every component currently composes those
ad-hoc inline. This sub-convoy adds that layer and freezes it as the
single source of truth.
## Scope
### In scope
- `styles/globals.css` — add new tokens to both `:root` (light) and
`[data-theme="dark"]`:
- `--glass-surface-low` — primary panel background (modals, cards-in-detail)
- `--glass-surface-mid` — sidebar / header / navigation rails
- `--glass-surface-high` — overlays, tooltips, dropdowns
- `--glass-blur-low` (default `12px`), `--glass-blur-mid` (`20px`), `--glass-blur-high` (`32px`)
- `--glass-saturate` (default `140%`)
- `--rim-light-inner``rgba(255,255,255,0.55)` light / `rgba(255,255,255,0.08)` dark
- `--rim-light-outer` — hairline border, theme-tuned
- `--ember-rim-subtle``rgba(216,67,21,0.35)` 1px ring
- `--ember-rim-pronounced``rgba(216,67,21,0.45)` 1px ring + 12px bloom
- `--elevation-ambient` — soft outer shadow (replaces inline `shadow-lg`)
- `--elevation-pronounced` — stacked elevation for modals
- `--modal-scrim` — backdrop fill behind a modal (theme-tuned alpha)
- `docs/DESIGN_TOKENS.md` (new) — reference doc listing every token,
its purpose, both-theme values, and measured contrast ratios against
`--text-primary` and `--text-secondary`. Use https://webaim.org/resources/contrastchecker/ values.
- `AGENTS.md` § "Branding" — append one paragraph linking
`docs/DESIGN_TOKENS.md` and naming the Liquid Glass direction.
### Out of scope
- Any change to a `.js` file under `components/**` or `pages/**`.
- Deletion of any existing token or utility class (cleanup is sub-convoy #8).
- Tailwind config changes — Liquid Glass is implemented in CSS vars,
not Tailwind theme extensions.
- Storybook adoption — `docs/DESIGN_TOKENS.md` is hand-curated; a real
Storybook is its own future convoy.
## Roles invoked
1. `role-design-system-auditor` — proposes token names + structure.
2. `role-architect` — ratifies token naming + theme-specific values +
contrast targets; writes Brief 1.
3. `role-implementer` — single brief; adds tokens + docs.
4. `role-doc-writer` — reviews `docs/DESIGN_TOKENS.md` shape.
## Todos
- [x] Design-system auditor: token naming + structure proposal
→ see `.convoys/liquid-glass-design-tokens/design-system-audit.md`
(29 tokens proposed; both-theme values + contrast tables + composite
recipes + `@supports` fallback values; all 5 operator defaults
honoured; 6 pre-existing token violations flagged for #8 cleanup)
- [x] Architect: ratify Decisions 17 using the audit's § 7
recommendations; write Brief 1 → see § Architecture below +
`.convoys/liquid-glass-design-tokens/brief-1-tokens-and-docs.md`.
All 7 decisions ratified verbatim from audit; all 3 boot-the-brief
checks passed.
- [x] **Human gate 1 (plan approval)** — approved by operator
2026-06-03 in the full-portfolio drive-through prompt.
- [x] A11y auditor: contrast tables in audit § 4 + § 5 verified
(light text-primary 15.18:1 AAA, text-secondary 6.86:1 AA; dark
text-primary 17.84:1 AAA, text-secondary 11.42:1 AAA; ember rim
non-text 3.186.18:1 all clear 3:1 SC 1.4.11 floor).
- [x] Brief 1 — tokens + docs (committed 2026-06-03). 110 LOC inserted
into `styles/globals.css` (purely additive, comment-fenced),
270 LOC `docs/DESIGN_TOKENS.md`, 23 LOC `AGENTS.md § Visual
language`. Lint 0 errors; vitest 84/84 green (baseline preserved).
- [x] Post-PR audit — `role-reviewer` (single-shot): zero `.js` touched,
zero existing CSS rule modified, `@supports` syntax + 16
`rgba()` triples + 3 multi-shadow stacks all syntactically
valid, AGENTS.md insertion at correct topology.
## Decisions to ratify (architect)
1. **Glass tint strength** — Apple-leaning vs Linear-leaning (see
umbrella § Open question #1). Operator default: Apple-leaning.
2. **Light-theme glass base** — warm white vs cool white (umbrella § #2).
Operator default: warm white.
3. **Dark-theme glass base** — warm black vs cool black (umbrella § #3).
Operator default: warm black.
4. **`--glass-blur-low/mid/high` exact px values** — proposal: 12 / 20 / 32.
5. **`--glass-saturate` default** — proposal: 140% (Apple-style vibrancy).
6. **Contrast target** — WCAG AA (4.5:1 for text-primary, 3:1 for
text-secondary on large text) vs AAA. Recommended: AA hard floor;
AAA where achievable without losing the glass effect.
7. **`@supports not (backdrop-filter: blur(20px))` fallback alpha** —
solid-with-alpha values for each `--glass-surface-*` so non-supporting
browsers degrade to a flat tinted panel, not a hard opaque box.
## Acceptance criteria
1. `styles/globals.css` defines every token listed in § Scope for both
themes.
2. `docs/DESIGN_TOKENS.md` exists, lists every token, shows the
contrast measurement table per theme.
3. **No `.js` file is modified.**
4. `npm run lint` + `npm run test:run` + `npm run test:smoke` all green.
5. `Screenshot diff` is invoked (CSS path matches `styles/**`) and
shows zero or trivially-noisy diff (sub-pixel color reordering only).
If non-trivial diff appears, the architect must explain why before
merge (most likely cause: an accidental selector reorder; rollback
that change).
6. `AGENTS.md` § Branding mentions Liquid Glass + links
`docs/DESIGN_TOKENS.md`.
## CI impact
| Workflow / job | Behavior |
| --- | --- |
| `preview-smoke.yml` | Fires (any PR). |
| `visual-diff.yml` | **Fires** (`styles/**` matches paths). Expected diff: none. |
| `lint` | Fires. |
| `test:` (vitest) | Fires. |
| New grep gates | None. |
## Known constraints
- **No hardcoded hex** outside `styles/globals.css`. The token surface
is the only place hex appears post-sub-convoy.
- **Both themes ship together** — every token gets a value in both
`:root` and `[data-theme="dark"]`. Reviewer fails the PR if any token
is one-theme-only.
- **`@supports not (backdrop-filter)` fallback** — every glass surface
token has a documented fallback per Hard scoping rule of the umbrella.
## Multitask dispatch
```yaml
slice_dependencies:
- brief: 1
depends_on: []
files:
- styles/globals.css
- docs/DESIGN_TOKENS.md
- AGENTS.md
```
Single brief; no multitask. Post-PR audit is a single
`role-reviewer` invocation.
## Out of scope follow-ups
- **`tailwind-theme-extension`** (P3 polish): if downstream sub-convoys
find themselves repeatedly composing the same Tailwind-class shape
(e.g. `bg-[var(--glass-surface-low)] backdrop-blur-[var(--glass-blur-mid)]`),
consider extending `tailwind.config.js` `theme.extend.backdropBlur` /
`backgroundColor` with named aliases. Surface only if at least 3
downstream sub-convoys hit the same shape.
- **`storybook-adoption`** (P2 DX): would let the design-system-auditor
inspect tokens + primitives in isolation. Out of scope here;
hand-curated `docs/DESIGN_TOKENS.md` is the v1 surface.
## Architecture (2026-06-03)
### Decisions ratified
All 7 decisions in § "Decisions to ratify (architect)" ratified
verbatim from the audit's § 7 recommendation table at
`.convoys/liquid-glass-design-tokens/design-system-audit.md`. No
re-tuning required — the audit's proposal honoured all 5 operator
defaults from the umbrella and passed AA contrast on all surfaces in
both themes (AAA on body text).
| Decision | Ratified value | Source |
| --- | --- | --- |
| 1. Glass tint strength | `--glass-surface-{low,mid,high}` α = **0.55 / 0.68 / 0.82** | Audit § 2.1 |
| 2. Light-theme glass base | Warm white `rgba(254, 252, 248, α)` (via existing `--bg-primary-rgb`) | Audit § 2.1 |
| 3. Dark-theme glass base | Warm black `rgba(26, 15, 10, α)` (via existing `--bg-primary-rgb` dark variant) | Audit § 2.1 |
| 4. `--glass-blur-{low,mid,high}` px | **12 / 20 / 32** | Audit § 2.2 |
| 5. `--glass-saturate` default | **140%** | Audit § 2.2 |
| 6. Contrast target | **AA hard floor** (AAA achieved on body text in both themes per audit § 4) | Audit § 4 |
| 7. `@supports` fallback alpha | Collapsed ramp **0.92 / 0.95 / 0.98** under `@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)))` | Audit § 3 |
### File plan
| File | Action | Purpose |
| --- | --- | --- |
| `styles/globals.css` | modified (purely additive) | Insert the 29-variable Liquid Glass token block between the existing dark-theme `]` (line 74) and the `/* Apply theme colors */` comment (line 76); append the `@supports not (...)` fallback block immediately after. **Zero pre-existing rules touched.** |
| `docs/DESIGN_TOKENS.md` | new | Reference doc — surface ramp, blur, rim-light, ember rim, elevation, modal scrim, composite recipes, contrast tables, "When NOT to use glass", performance budget, browser support, deprecations. |
| `AGENTS.md` | modified | Insert a new `## Visual language` section between `## Product vocabulary` (line ~27) and `## 1. Project overview` (line 29). Three rules of thumb + pointer at `docs/DESIGN_TOKENS.md`. |
### API surface
N/A — CSS + docs only. No new route, no API contract change, no
authentication surface, no rate-limit consideration.
### Schema diff
N/A — no database change.
### Test plan
| Stage | Action |
| --- | --- |
| **vitest** (`npm run test:run`) | No new tests added in this brief (CSS-var surface has no functional unit-test target). Existing 21/21 baseline must stay green. |
| **lint** (`npm run lint`) | Must exit 0. |
| **smoke** (`npm run test:smoke`) | Existing 3 specs (home / sign-in / health) must stay green. None depend on visual tokens. |
| **visual-diff** (`Screenshot diff` workflow) | Fires (`styles/**` matches paths). **Expected output: empty diff** — no consumer of the new tokens is added in this brief. Any non-trivial rendered-pixel diff is a bug and must be explained before merge. |
| **manual** | None required for this brief — no UI to click through (per `skip: qa`). |
No existing tests are affected. No new test files are created. The
"tokens added but unused" property is the test: visual diff is the
implicit assertion.
### Risk list
1. **Visual-diff noise from CSS file reorganization.** Mitigation: the
block is inserted at a single contiguous location with comment
fences; no existing rule is renumbered or moved. `git diff
styles/globals.css` should show only inserted hunks. (Risk: low.)
2. **`@supports not ((...) or (...))` syntax error.** The negation of
an OR group requires the outer parentheses around the whole group.
Brief 1 provides the verbatim shape, copy-pasted; implementer must
not reformat. (Risk: low; mitigated by verbatim copy.)
3. **`@supports` block placement order.** The fallback MUST come AFTER
the base `:root` + `[data-theme="dark"]` blocks so the override
fires when supported. Brief specifies the insertion location.
(Risk: low; mitigated by exact-insertion-point instruction.)
4. **`docs/DESIGN_TOKENS.md` path referenced from `AGENTS.md` before
the file exists.** Brief is atomic — all three files commit
together. (Risk: nil with atomic commit.)
5. **Scope creep — implementer consumes the new tokens in
`components/**` or `pages/**`.** This brief is foundation-only;
consumption begins in sub-convoy #2. Brief explicitly forbids `.js`
changes; pre-PR verification command grep'd in the brief surfaces
any leak. (Risk: low; gated by explicit anti-scope + verification.)
6. **No-go zone violation.** `styles/globals.css` is not a no-go zone;
`AGENTS.md` is the canonical agent contract (editable through
documented sections); `docs/` is new content. ✅ All in scope.
7. **Token-name collision.** None — audit verified all 29 proposed
names are unique against the current `styles/globals.css` namespace.
(Risk: nil.)
8. **Browser-fallback misfire on Safari 18+.** Safari 18+ supports
`backdrop-filter` unprefixed; `@supports not (...)` will NOT fire,
so the fallback alphas remain unused — the canonical 0.55 / 0.68 /
0.82 surfaces ship as intended. Cross-checked against caniuse 2026-
06-03. (Risk: nil.)
### Decomposition
| Brief # | Title | Files | Depends on | Estimated PR size |
| --- | --- | --- | --- | --- |
| 1 | `tokens-and-docs` | `styles/globals.css`, `docs/DESIGN_TOKENS.md`, `AGENTS.md` | — | ~110 LOC inserted into `styles/globals.css`; ~260 LOC `docs/DESIGN_TOKENS.md`; ~25 LOC `AGENTS.md`. Total ≈ 395 LOC, all additive. **No deletions.** |
Single brief; no multitask possible (single implementer, single PR).
Comfortably under the 400-LOC architect anti-pattern threshold.
### Slice dependencies (multitask-ready)
```yaml
slice_dependencies:
- brief: 1
depends_on: []
files:
- styles/globals.css
- docs/DESIGN_TOKENS.md
- AGENTS.md
```
**Multitask verdict:** no `/multitask` dispatch — single-brief convoy.
Conductor's serial-implementer path is the right shape here.
### Boot-the-brief check (architect verification, completed 2026-06-03)
1. **Dep set check.** Zero new packages added. Zero peer-dep concerns.
✅ Pass.
2. **Verbatim code shape check.**
- `@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)))` — CSS Conditional Rules Level 3 valid syntax (negation of a compound condition requires the outer parens around the OR group). Cross-checked against MDN's `@supports` reference. ✅ Verified.
- The 16 `rgba(...)` triples in the brief — all integer RGB values 0255, all alpha values 0.01.0. ✅ Syntactically valid.
- The 3 `box-shadow` stacks (light + dark `--elevation-pronounced`, `--ember-rim-pronounced`) — comma-separated multi-shadow syntax is CSS Backgrounds and Borders Level 3 valid. ✅ Verified.
- `inset` keyword for inner-rim shadows — valid in `box-shadow` and standalone (when in `box-shadow` list). ✅ Verified.
- The CSS variable inheritance pattern (theme-independent blur/saturate defined only in `:root`; theme-dependent surfaces redefined in `[data-theme="dark"]`) — matches the existing precedent at `styles/globals.css` lines 100154. ✅ Verified.
3. **Cross-brief commitments check.** Single brief; no commitments to
downstream briefs in this convoy. (Downstream convoys #2#8 are
separate convoys with their own architect passes.) ✅ N/A.
All boot-the-brief checks pass. No brief revision needed.
### Architect notes (mid-implementation guidance)
- The brief explicitly forbids reformatting the verbatim CSS block.
Implementer must copy-paste, not retype. This avoids whitespace
drift on the multi-line `box-shadow` stacks.
- If the implementer hits any unexpected obstacle (e.g. `Screenshot
diff` shows a non-trivial rendered diff despite no consumer being
added), STOP and surface to the operator. Do not "fix it" by editing
components.
- The 26-line AGENTS.md insertion is at a specific topological
position (between `## Product vocabulary` and `## 1. Project
overview`). Use `StrReplace` to target the line-29 boundary
precisely; do not use blind append-to-section.