# Story: Dark Tech Theme **Phase:** `08_story_dark_tech_theme.md` · **E2E:** `tests/e2e/test_dark_tech_theme.py` ## Narrative As **a user**, I want Brain of Reese to look like a serious piece of my infrastructure — dark, technical, free of emojis, with a subtly animated background — so the app feels at home next to my dashboards and is comfortable to use at night, instead of a generic light-mode chat widget. - **Given** the v1 UI (light palette, emoji avatars/brand/favicon, static background) - **When** the theme phase is complete - **Then** every page is dark with a tech aesthetic, no emoji appear anywhere in the UI chrome, a subtle pure-CSS animated background (drifting grid + breathing glows) is visible but never distracting, all layout metrics and behavior are unchanged, and WCAG 2.1 AA still holds on the new palette. ## Acceptance criteria 1. Dark palette (tokens in the phase file) applied to **both** pages (`/`, `/sources.html`); every text/background pair ≥4.5:1 — computed, not eyeballed. 2. **Zero emoji in UI chrome** — HTML templates, JS-rendered chrome, favicon, CSS — replaced by inline SVG glyphs. An integration test enforces this as a permanent regression guard. 3. Subtly animated background: a fine drifting grid + two slow breathing radial glows, **pure CSS, zero JS**, `pointer-events: none`, never blocking or dimming content. 4. `prefers-reduced-motion: reduce` → background animations disabled (static grid + glows remain). 5. No-CDN invariant holds on both pages (all script/link refs local or `data:`). 6. Layout metrics unchanged (PLAN §7.1): 72rem frame, 46rem centered chat column, full-width Sources table, mobile rules; behavior unchanged (chat state machine, chips, banners). 7. WCAG 2.1 AA: landmarks, labels, skip link, `:focus-visible`, live regions intact on the new palette. 8. LLM answer content is out of scope (the model may output anything; the rule binds UI chrome only). 9. Whole pytest suite green, `app/` coverage >90%, story E2E green in isolation. ## UI Visualization & Structure - **Palette (all pairs computed, ≥4.5:1):** page `#0a0e17`, surface `#121a2e`, ink `#e8ebf4` (14.5:1 on surface), ink-soft `#9aa4bd` (6.9:1), line `#26304a`, brand `#6d78f2` with **dark** text `#0a0e17` (5.2:1 — white on brand is 3.7:1, forbidden for text), brand-soft `#232b52` + brand-ink `#a5b4fc` (6.9:1), deflection `#2b2110`/`#fbbf24` (9.5:1) + border `#f59e0b`, error `#2d1318`/`#fca5a5` (9.1:1), ok `#10241b`/`#6ee7a8` (10.6:1). - **Busy button** keeps its `#a5b4fc` background (the existing unit test asserts the token) with a **dark** arc (`#0a0e17`, 9.7:1). - **Avatars:** brain → inline SVG circuit-node glyph; user → minimal silhouette SVG; `aria-hidden` retained. - **Favicon / brand mark:** tech SVG mark (hex + node), still an inline `data:` URI (no CDN). - **Tech details:** mono wordmark + stat values, 1px `--line` borders, radii 10px/6px, a 2px brand→cyan gradient hairline under the sticky header. - **Background:** `body::before` grid (44px cells, ~35% alpha 1px lines, radial mask fade, 60s seamless diagonal drift); `body::after` two radial glows (indigo ~14% top-left, cyan ~10% bottom-right, 14s breathing). Both `position: fixed; inset: 0; pointer-events: none`. ## Playwright Mapping Rule **Test Scenario → `tests/e2e/test_dark_tech_theme.py`** (mock LLM, seeded KB): 1. `test_dark_palette_and_contrast` — body background is `rgb(10, 14, 23)`; computed ink-on-surface and brand-button text/background pairs ≥4.5:1 (same contrast helper as Phase 07). 2. `test_no_emoji_in_chrome` — neither page's `document.body.innerText` nor raw `outerHTML` contains any emoji code point. 3. `test_animated_background` — computed style of `body::before` has a non-none `background-image` and `animation-name`; `body::after` animates too. 4. `test_reduced_motion_honored` — a browser context created with `reduced_motion="reduce"` → `animation-name: none` on both layers. 5. `test_behavior_unchanged_smoke` — on-topic question streams an answer + a source chip + the send button recovers (state machine intact under the new skin). 6. `test_all_assets_local` — every `script[src]` / `link[href]` on both pages is relative or `data:` (no CDN).