diff --git a/docs/superpowers/plans/2026-08-10-ui-polish-bright.md b/docs/superpowers/plans/2026-08-10-ui-polish-bright.md
new file mode 100644
index 0000000..488350d
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-10-ui-polish-bright.md
@@ -0,0 +1,627 @@
+# UI Polish — Bright/Premium Theme Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Restyle the WebUI to a bright, premium (Stripe-like) theme: zinc-neutral surfaces, vivid cyan accents, Inter font, layered shadows — across the main UI, all dialogs, and the login page, in both light and dark mode.
+
+**Architecture:** Token-first, three independently shippable layers: (1) rewrite CSS design tokens in `globals.css` + wire Inter via `next/font/local`; (2) restyle the `src/components/ui/` primitives so every consumer inherits; (3) targeted screen-level class edits (chat area, thread list, login). Purely visual — no logic changes.
+
+**Tech Stack:** Tailwind CSS 3 (config `tailwind.config.mjs`, `darkMode: "class"`), shadcn-style Radix primitives, `next/font/local`, Vitest.
+
+**Spec:** `docs/superpowers/specs/2026-08-10-ui-polish-bright-design.md`
+
+## Global Constraints
+
+- This is a purely visual change: `npx tsc --noEmit`, `npx eslint .`, and `npm test` must stay green after every task. Any test breakage means logic was touched by accident — revert it.
+- Commit style: `feat(webui): …` / `style(webui): …`.
+- The working tree contains **unrelated uncommitted user changes**, including in `src/app/components/ChatInterface.tsx` and `src/app/components/ChatMessage.tsx`. Tasks that edit those files must NOT `git add` them wholesale — the controller stages only the task's hunks. Never use `git add -A` / `git add .` anywhere.
+- `src/app/components/ContextUsageIndicator.tsx` is untracked user work-in-progress — do not touch it. (Spec's `tnum` application is descoped to shipping the `.tnum` utility only; see Self-Review Notes.)
+- Dark mode is driven by the `.dark` class on ``; both `:root` and `.dark` token sets are updated in the same task.
+- WCAG AA for text: cyan-600 `#0891b2` on white; cyan-700 `#0e7490` on cyan-50 `#ecfeff`; dark-mode equivalents.
+
+---
+
+### Task 1: Design tokens + Inter font
+
+**Files:**
+- Create: `src/app/fonts/InterVariable.woff2` (downloaded font file)
+- Modify: `src/app/globals.css` (token blocks, body font, heading tracking, `.tnum` utility)
+- Modify: `src/app/layout.tsx` (font wiring, viewport themeColor)
+
+**Interfaces:**
+- Produces (consumed by Tasks 2–4):
+ - Color tokens: `--brand` `#0891b2` (light) / `#22d3ee` (dark); `--brand-solid`; `--color-active-bg` `#ecfeff` / `#083344`; `--color-active-text` `#0e7490` / `#67e8f9`; remapped shadcn HSL tokens (`--background`, `--muted`, `--accent`, `--ring`, …)
+ - Shadow tokens: `--shadow-sm` / `--shadow-lg` (popover) / `--shadow-xl` (dialog), overridden in `.dark`
+ - `--radius: 0.625rem` (so Tailwind `rounded-md` = 8px, `rounded-lg` = 10px)
+ - Font: `--font-inter` CSS variable on `
`; `.tnum` utility class
+
+- [ ] **Step 1: Download the Inter variable font**
+
+Run:
+
+```bash
+mkdir -p src/app/fonts
+curl -fL -o src/app/fonts/InterVariable.woff2 "https://rsms.me/inter/font-files/InterVariable.woff2?v=4.1"
+```
+
+Verify: `ls -la src/app/fonts/InterVariable.woff2` shows a file of roughly 800 KB – 1.5 MB, and `file src/app/fonts/InterVariable.woff2` reports `Web/OpenType font` (or `data` — woff2 has no magic string in some `file` builds; size is the real check).
+
+Fallback if rsms.me is unreachable:
+
+```bash
+curl -fL -o /tmp/inter.zip https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip
+unzip -l /tmp/inter.zip | grep -i "InterVariable.woff2" # locate the exact path inside
+unzip -j -o /tmp/inter.zip "" -d src/app/fonts/
+```
+
+If both fail (offline environment), STOP and report BLOCKED — do not substitute a different font or a CDN link.
+
+- [ ] **Step 2: Wire the font in `src/app/layout.tsx`**
+
+Add the import after the existing imports (line 8 area):
+
+```ts
+import localFont from "next/font/local";
+```
+
+Add the font definition after the `viewport` export (after line 22):
+
+```ts
+// Self-hosted Inter variable font; exposes --font-inter, consumed by globals.css.
+// No CJK glyphs — Chinese text falls through to PingFang/YaHei in the stack.
+const inter = localFont({
+ src: "./fonts/InterVariable.woff2",
+ variable: "--font-inter",
+ display: "swap",
+});
+```
+
+Change the `` tag (line 41) from:
+
+```tsx
+
+```
+
+to:
+
+```tsx
+
+```
+
+Update the stale viewport theme colors (lines 17-20) from:
+
+```ts
+ { media: "(prefers-color-scheme: light)", color: "#f9f9f9" },
+ { media: "(prefers-color-scheme: dark)", color: "#212121" },
+```
+
+to:
+
+```ts
+ { media: "(prefers-color-scheme: light)", color: "#fafafa" },
+ { media: "(prefers-color-scheme: dark)", color: "#09090b" },
+```
+
+- [ ] **Step 3: Replace the light `:root` token block in `src/app/globals.css`**
+
+In `src/app/globals.css`, inside `@layer base { :root { … } }`, replace everything from the comment `/* App-specific color variables */` (line 13) through `--chart-5: 330 26% 48%;` (line 144) with the block below. Keep: the alias block (`--text-primary: var(--color-text-primary);` … `--scrollbar-thumb-hover`) and the spacing/font-size/line-height/weight/radius-sm..full/layout variable sections exactly as they are — the replacement block below re-includes only what it replaces; anything not shown here stays untouched. (The old `--radius-sm/md/lg/full` and `--sidebar-width` etc. sections sit between the color section and the Tailwind variables section — leave them in place.)
+
+```css
+ /* App-specific colors — bright neutral palette. Light mode: zinc-50 app
+ background, white surfaces, near-black zinc text, vivid cyan accent. */
+ --color-primary: #18181b;
+ --color-user-message: #0e7490;
+ --color-user-message-bg: #ecfeff;
+ --color-avatar-bg: #cffafe;
+ --color-secondary: #52525b;
+ --color-success: #16a34a;
+ --color-warning: #d97706;
+ --color-error: #dc2626;
+ --color-background: #fafafa;
+ --color-subagent-hover: #f4f4f5;
+ --color-surface: #ffffff;
+ --color-border: #e4e4e7;
+ --color-border-light: #f4f4f5;
+ --color-text-primary: #18181b;
+ --color-text-secondary: #52525b;
+ --color-text-tertiary: #a1a1aa;
+ --color-file-button: #ffffff;
+ --color-file-button-hover: #f4f4f5;
+
+ /* Active/selected surface (sidebar items, tabs, selections). */
+ --color-active-bg: #ecfeff;
+ --color-active-text: #0e7490;
+
+ /* Brand accent — EvoScientist cyan. cyan-600 passes WCAG AA on white. */
+ --brand: #0891b2;
+ --brand-hover: #0e7490;
+ /* Flat fill for badges/marks; the primary Button adds its own gradient. */
+ --brand-solid: #0891b2;
+ --brand-foreground: #ffffff;
+```
+
+Then, still inside `:root`, replace the shadow variables block (lines 102-106, `/* Shadow variables */` through the `--shadow-xl` line) with:
+
+```css
+ /* Shadow variables — lighter, layered. lg = popover/dropdown, xl = dialog. */
+ --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
+ --shadow-lg: 0 4px 12px -2px rgb(0 0 0 / 0.08),
+ 0 2px 4px -2px rgb(0 0 0 / 0.04);
+ --shadow-xl: 0 20px 40px -12px rgb(0 0 0 / 0.18),
+ 0 4px 12px -4px rgb(0 0 0 / 0.06);
+```
+
+And replace the Tailwind/Radix HSL variables (lines 118-144, from `--radius: 0.5rem;` through `--chart-5`) with:
+
+```css
+ /* Tailwind/Radix UI component variables */
+ --radius: 0.625rem;
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+ --primary: 189 90% 36%;
+ --primary-foreground: 0 0% 100%;
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 10% 3.9%;
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 5% 34%;
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: 240 10% 3.9%;
+ --destructive: 0 72% 51%;
+ --destructive-foreground: 0 0% 100%;
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+ --ring: 189 90% 36%;
+ --sidebar: 0 0% 98%;
+ --chart-1: 189 90% 36%;
+ --chart-2: 173 80% 40%;
+ --chart-3: 38 92% 50%;
+ --chart-4: 199 89% 48%;
+ --chart-5: 350 89% 60%;
+```
+
+- [ ] **Step 4: Replace the `.dark` token block**
+
+Inside the same file's `.dark { … }` (lines 151-206), replace everything from `/* App-specific colors — warm charcoal dark mode …` through `--chart-5: 332 39% 64%;` with:
+
+```css
+ /* App-specific colors — cool neutral dark (zinc), cyan accents. */
+ --color-primary: #fafafa;
+ --color-user-message: #67e8f9;
+ --color-user-message-bg: #083344;
+ --color-avatar-bg: #083344;
+ --color-secondary: #a1a1aa;
+ --color-success: #4ade80;
+ --color-warning: #fbbf24;
+ --color-error: #f87171;
+ --color-background: #09090b;
+ --color-subagent-hover: #27272a;
+ --color-surface: #18181b;
+ --color-border: #27272a;
+ --color-border-light: #1f1f22;
+ --color-text-primary: #fafafa;
+ --color-text-secondary: #a1a1aa;
+ --color-text-tertiary: #71717a;
+ --color-file-button: #18181b;
+ --color-file-button-hover: #27272a;
+ --color-active-bg: #083344;
+ --color-active-text: #67e8f9;
+ --brand: #22d3ee;
+ --brand-hover: #67e8f9;
+ /* Deep, saturated cyan fill: colorful but low-lightness so buttons don't
+ glow on the dark surface; white text passes AA. */
+ --brand-solid: #0e7490;
+ --brand-foreground: #ffffff;
+
+ /* Shadows need more contrast on dark surfaces. */
+ --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
+ --shadow-lg: 0 4px 12px -2px rgb(0 0 0 / 0.5),
+ 0 2px 4px -2px rgb(0 0 0 / 0.3);
+ --shadow-xl: 0 20px 40px -12px rgb(0 0 0 / 0.6),
+ 0 4px 12px -4px rgb(0 0 0 / 0.4);
+
+ /* Tailwind/Radix UI component variables for dark mode */
+ --radius: 0.625rem;
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 192 86% 29%;
+ --primary-foreground: 0 0% 100%;
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 72% 51%;
+ --destructive-foreground: 0 0% 100%;
+ --border: 240 3.7% 15.9%;
+ --input: 240 3.7% 15.9%;
+ --ring: 187 85% 53%;
+ --sidebar: 240 6% 8%;
+ --chart-1: 187 85% 53%;
+ --chart-2: 172 66% 50%;
+ --chart-3: 43 96% 56%;
+ --chart-4: 198 93% 60%;
+ --chart-5: 351 95% 71%;
+```
+
+- [ ] **Step 5: Body font stack, heading tracking, `.tnum` utility**
+
+In the same file, change the `body` rule's `font-family` (lines 234-235) from:
+
+```css
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ "Helvetica Neue", Arial, sans-serif;
+```
+
+to:
+
+```css
+ font-family: var(--font-inter), -apple-system, BlinkMacSystemFont,
+ "Segoe UI", "PingFang SC", "Microsoft YaHei", Roboto, "Helvetica Neue",
+ Arial, sans-serif;
+```
+
+Change the `h1, h2, h3, h4, h5, h6` rule (lines 244-252) from:
+
+```css
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: 600;
+ line-height: 1.25;
+ color: var(--color-text-primary);
+}
+```
+
+to:
+
+```css
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: 600;
+ line-height: 1.25;
+ letter-spacing: -0.02em;
+ color: var(--color-text-primary);
+}
+```
+
+Append at the end of the file:
+
+```css
+/* Tabular numerals for token/usage readouts. */
+.tnum {
+ font-feature-settings: "tnum";
+}
+```
+
+- [ ] **Step 6: Verify**
+
+Run: `npx tsc --noEmit && npx eslint src/app/layout.tsx && npm test`
+Expected: no type errors, no lint errors, all tests pass (310 passed / 1 skipped at plan time).
+
+- [ ] **Step 7: Commit**
+
+These files are NOT in the user's uncommitted set — stage them directly:
+
+```bash
+git add src/app/globals.css src/app/layout.tsx src/app/fonts/InterVariable.woff2
+git commit -m "style(webui): bright zinc+cyan design tokens and Inter font"
+```
+
+- [ ] **Step 8 (controller, not implementer):** Hand to the user for the Layer-1 visual gate (light + dark overall palette/feel) per the spec.
+
+---
+
+### Task 2: `ui/` primitives restyle
+
+**Files:**
+- Modify: `src/components/ui/button.tsx`
+- Modify: `src/components/ui/input.tsx`
+- Modify: `src/components/ui/textarea.tsx`
+- Modify: `src/components/ui/select.tsx`
+- Modify: `src/components/ui/dialog.tsx`
+- Modify: `src/components/ui/dropdown-menu.tsx`
+
+**Interfaces:**
+- Consumes (from Task 1): `--brand`, `--brand-solid`, `--brand-hover`, `--brand-foreground`, `--shadow-lg`, `--shadow-xl`.
+- Produces: every existing consumer of Button/Input/Select/Dialog/DropdownMenu inherits the new look without edits (Task 3–4 rely on this).
+
+All edits are exact string replacements inside `cn(...)`/cva class strings. No logic changes.
+
+- [ ] **Step 1: Button — gradient primary variant**
+
+In `src/components/ui/button.tsx`, replace the `default` variant string (line 13):
+
+```ts
+ default:
+ "bg-[var(--brand-solid)] text-[var(--brand-foreground)] shadow-xs hover:opacity-90",
+```
+
+with:
+
+```ts
+ default:
+ "bg-gradient-to-b from-[#06b6d4] to-[var(--brand-solid)] text-[var(--brand-foreground)] shadow-sm hover:from-[#0891b2] hover:to-[var(--brand-hover)] dark:bg-none dark:bg-[var(--brand-solid)] dark:hover:bg-[var(--brand-hover)]",
+```
+
+(Light: subtle vertical cyan-500→cyan-600 gradient deepening on hover. Dark: flat `--brand-solid` — a gradient would glow.)
+
+- [ ] **Step 2: Input — cyan focus treatment**
+
+In `src/components/ui/input.tsx`, replace (line 12):
+
+```ts
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
+```
+
+with:
+
+```ts
+ "focus-visible:border-[var(--brand)] focus-visible:ring-[3px] focus-visible:ring-[var(--brand)]/20",
+```
+
+- [ ] **Step 3: Textarea — same focus treatment**
+
+In `src/components/ui/textarea.tsx`, replace (line 12):
+
+```ts
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+```
+
+with:
+
+```ts
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:border-[var(--brand)] focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--brand)]/20 disabled:cursor-not-allowed disabled:opacity-50",
+```
+
+- [ ] **Step 4: Select — trigger focus + content/item polish**
+
+In `src/components/ui/select.tsx`:
+
+a) Trigger (line 22) — replace:
+
+```ts
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1",
+```
+
+with:
+
+```ts
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm focus:border-[var(--brand)] focus:outline-none focus:ring-[3px] focus:ring-[var(--brand)]/20 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1",
+```
+
+b) Content (line 78) — replace `rounded-md border bg-popover text-popover-foreground shadow-md` with `rounded-xl border bg-popover text-popover-foreground shadow-[var(--shadow-lg)]` (keep every other class in that string untouched).
+
+c) Item (line 121) — replace `rounded-sm` with `rounded-md` in the SelectItem class string.
+
+- [ ] **Step 5: Dialog — blur overlay, 16px radius, layered shadow**
+
+In `src/components/ui/dialog.tsx`:
+
+a) Overlay (line 61) — replace:
+
+```ts
+ "fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
+```
+
+with:
+
+```ts
+ "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
+```
+
+b) Content (line 83) — replace `rounded-lg border bg-background p-6 shadow-lg` with `rounded-2xl border bg-background p-6 shadow-[var(--shadow-xl)]` (keep every other class untouched, including the max-height/translate/animation classes).
+
+- [ ] **Step 6: Dropdown — rounded content + items, layered shadow**
+
+In `src/components/ui/dropdown-menu.tsx`:
+
+a) Content (line 63) — replace `rounded-md border bg-popover p-1 text-popover-foreground shadow-md` with `rounded-xl border bg-popover p-1 text-popover-foreground shadow-[var(--shadow-lg)]`.
+
+b) Item (line 96) — replace `rounded-sm` with `rounded-md` in the DropdownMenuItem class string.
+
+- [ ] **Step 7: Verify**
+
+Run: `npx tsc --noEmit && npx eslint src/components/ui/ && npm test`
+Expected: clean, all tests pass.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add src/components/ui/button.tsx src/components/ui/input.tsx src/components/ui/textarea.tsx src/components/ui/select.tsx src/components/ui/dialog.tsx src/components/ui/dropdown-menu.tsx
+git commit -m "style(webui): restyle ui primitives (gradient primary, cyan focus, layered shadows)"
+```
+
+- [ ] **Step 9 (controller, not implementer):** Hand to the user for the Layer-3 visual gate (dialogs, buttons, inputs, dropdowns — hover/focus/disabled, both modes).
+
+---
+
+### Task 3: Chat area polish
+
+**Files:**
+- Modify: `src/app/components/ChatInterface.tsx:2048` (composer container)
+- Modify: `src/app/components/ChatMessage.tsx:274` (user bubble)
+- Modify: `src/app/components/ToolCallBox.tsx:305` (container radius)
+
+**Interfaces:**
+- Consumes (from Task 1): `--brand`, `--shadow-sm`, `--color-user-message-bg` (now `#ecfeff` light / `#083344` dark — applied via the existing inline style, no code change needed).
+- Consumes (from Task 2): the composer Send/Stop `Button` already renders the gradient primary variant — no edit.
+
+**Commit discipline (unusual — read first):** `ChatInterface.tsx` and `ChatMessage.tsx` contain unrelated uncommitted user changes. The implementer makes the edits and runs verification but does NOT commit; the controller stages only the task's hunks (`git apply --cached` with a filtered diff) and commits. `ToolCallBox.tsx` is clean and committed normally by the controller too.
+
+- [ ] **Step 1: Composer container — 16px radius, subtle shadow, cyan focus-within ring**
+
+In `src/app/components/ChatInterface.tsx`, find (line 2048, first element of a `cn(` call):
+
+```ts
+ "relative z-10 mx-auto mb-2 flex w-[calc(100%-16px)] max-w-[960px] flex-shrink-0 flex-col overflow-hidden rounded-xl border border-border bg-background transition-colors duration-200 ease-in-out sm:mb-4 sm:w-[calc(100%-24px)]",
+```
+
+Replace with:
+
+```ts
+ "relative z-10 mx-auto mb-2 flex w-[calc(100%-16px)] max-w-[960px] flex-shrink-0 flex-col overflow-hidden rounded-2xl border border-border bg-background shadow-[var(--shadow-sm)] transition-colors duration-200 ease-in-out focus-within:border-[var(--brand)] focus-within:ring-2 focus-within:ring-[var(--brand)]/20 sm:mb-4 sm:w-[calc(100%-24px)]",
+```
+
+- [ ] **Step 2: User message bubble — 16px radius, borderless**
+
+In `src/app/components/ChatMessage.tsx`, find (line 274, inside the `isUser` ternary):
+
+```ts
+ ? "rounded-xl rounded-br-none border border-border px-3 py-2 text-foreground"
+```
+
+Replace with:
+
+```ts
+ ? "rounded-2xl rounded-br-md px-3.5 py-2 text-foreground"
+```
+
+(The bubble's background keeps coming from the inline `backgroundColor: "var(--color-user-message-bg)"` a few lines below — Task 1 changed that token to cyan-50/dark-cyan, so the bubble restyles with no further edit.)
+
+- [ ] **Step 3: ToolCallBox container radius**
+
+In `src/app/components/ToolCallBox.tsx`, find (line 305):
+
+```ts
+ className="rounded-sm border border-border"
+```
+
+Replace with:
+
+```ts
+ className="rounded-lg border border-border"
+```
+
+- [ ] **Step 4: Verify (implementer)**
+
+Run: `npx tsc --noEmit && npx eslint src/app/components/ChatInterface.tsx src/app/components/ChatMessage.tsx src/app/components/ToolCallBox.tsx && npm test`
+Expected: clean, all tests pass.
+
+- [ ] **Step 5: Selective commit (controller, not implementer)**
+
+The controller extracts only the hunks containing the three replacement strings above from the working-tree diff, stages them with `git apply --cached`, and commits:
+
+```bash
+git commit -m "style(webui): polish composer, user bubble, and tool-call box"
+```
+
+- [ ] **Step 6 (controller):** Hand to the user for the chat-area visual gate (both modes).
+
+---
+
+### Task 4: Thread list + login page polish
+
+**Files:**
+- Modify: `src/app/components/ThreadList.tsx` (active states ×4)
+- Modify: `src/app/login/page.tsx` (login card)
+
+**Interfaces:**
+- Consumes (from Task 1): `--color-active-bg`, `--color-active-text`, `--shadow-xl`.
+
+Both files are clean of user changes — normal staging.
+
+- [ ] **Step 1: Thread list — active conversation uses the cyan active surface**
+
+In `src/app/components/ThreadList.tsx`, find (line 462, the active branch of a ternary):
+
+```ts
+ ? "border border-primary bg-accent hover:bg-accent"
+```
+
+Replace with:
+
+```ts
+ ? "border border-transparent bg-[var(--color-active-bg)] text-[var(--color-active-text)] hover:bg-[var(--color-active-bg)]"
+```
+
+- [ ] **Step 2: Sidebar view tabs — active state uses the cyan active surface**
+
+In the same file, replace each of these three strings:
+
+```ts
+ view === "skills" && "bg-accent"
+```
+
+```ts
+ view === "memory" && "bg-accent"
+```
+
+```ts
+ view === "schedule" && "bg-accent"
+```
+
+with, respectively:
+
+```ts
+ view === "skills" &&
+ "bg-[var(--color-active-bg)] text-[var(--color-active-text)]"
+```
+
+```ts
+ view === "memory" &&
+ "bg-[var(--color-active-bg)] text-[var(--color-active-text)]"
+```
+
+```ts
+ view === "schedule" &&
+ "bg-[var(--color-active-bg)] text-[var(--color-active-text)]"
+```
+
+- [ ] **Step 3: Login card — 16px radius, dialog shadow, more padding**
+
+In `src/app/login/page.tsx`, find (line 79):
+
+```ts
+ className="w-full max-w-sm rounded-lg border border-border bg-[var(--color-surface)] p-6 shadow-sm"
+```
+
+Replace with:
+
+```ts
+ className="w-full max-w-sm rounded-2xl border border-border bg-[var(--color-surface)] p-8 shadow-[var(--shadow-xl)]"
+```
+
+(The sign-in button already uses the shared `Button` default variant → gradient from Task 2; inputs inherit Task 2's focus treatment.)
+
+- [ ] **Step 4: Verify**
+
+Run: `npx tsc --noEmit && npx eslint src/app/components/ThreadList.tsx src/app/login/page.tsx && npm test`
+Expected: clean, all tests pass.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/app/components/ThreadList.tsx src/app/login/page.tsx
+git commit -m "style(webui): cyan active states in sidebar, premium login card"
+```
+
+- [ ] **Step 6 (controller):** Hand to the user for the final visual gate (thread list active states, login page, both modes).
+
+---
+
+## Self-Review Notes (already applied)
+
+- **Spec coverage:** Layer 1 → Task 1 (tokens, font, typography, shadows, radii, both modes). Layer 2 → Task 1 Step 2/5 (font wiring, tracking) + Task 1 Step 5 (`.tnum` utility). Layer 3 → Task 2 (all six primitives). Layer 4 → Tasks 3–4 (chat, thread list, login). Workspace panel needs **no** edits: its checkboxes use `accent-[var(--brand)]` (= cyan-600 after Task 1) and its category labels already match the 11px uppercase micro style — token remap carries it, verified by reading `WorkspacePanel.tsx`.
+- **Descoped from spec, deliberately:** (a) blanket "more breathing room between messages" — no concrete receiver could be pinned without redesigning message layout; the lighter tokens already open the UI up, and the user's per-layer visual gates are the right place to request specific spacing follow-ups. (b) Applying `.tnum` to specific numeric readouts — the natural target (`ContextUsageIndicator.tsx`) is untracked user WIP and must not be swept into a commit; the utility ships ready to use.
+- **Placeholder scan:** every edit step contains exact old/new code strings with line anchors.
+- **Type/consistency:** token names referenced in Tasks 2–4 (`--brand`, `--brand-solid`, `--brand-hover`, `--color-active-bg`, `--color-active-text`, `--shadow-sm/lg/xl`) are all produced by Task 1.
+- **Risk:** Task 1 Step 3 instructs replacing a large token block while *keeping* the alias/spacing/layout sections that interleave it — the implementer must read the current file and preserve everything not explicitly replaced. The brief tells them exactly which line ranges are replaced.