--- date: 2026-10-20 pr: pending feature: Chat and group chat dark-theme residue cleanup impact: Message attachments, file cards, audio blocks, thinking headers and tool detail headers now paint from the CYBER token set instead of the light-theme black/white washes, so they are visible against the dark surfaces. Purely visual; no message shapes, socket events, or emission timing changed. --- # Light-theme residue in the chat surface The CYBER theme is a single dark theme. A handful of surfaces inside the chat chain still carried the light-theme habit of tinting with `rgba(0, 0, 0, α)`: a 4% black wash reads as "slightly grey" on white, and as "nothing at all" on `--bg-input`. Those surfaces had lost their fill entirely. ## What changed | File | Surface | Before | After | | --- | --- | --- | --- | | `MarkdownRenderer.vue` | audio container, file card | `rgba(0,0,0,0.04)` | `--bg-input` | | `MarkdownRenderer.vue` | file card hover | `rgba(0,0,0,0.08)` | `--bg-card-hover` | | `MessageItem.vue` | attachment block | `rgba(0,0,0,0.04)` | `--bg-input` | | `MessageItem.vue` | thinking header hover, tool row hover | `rgba(0,0,0,0.03)` | `rgba(255,255,255,0.05)` | | `MessageItem.vue` | tool detail code header | `rgba(0,0,0,0.02)` | `rgba(255,255,255,0.02)` | | `GroupMessageItem.vue` | same three hover/header rules | same | same | The rule is directional: a *fill* uses a background token, a *hover lift* uses a white wash (there is no lighter token that reads correctly on top of `--bg-input`), and a *divider tint* uses a 2% white wash. ## Not changed Message rendering, streaming, tool-call grouping, and the `/chat-run` event contract. This fragment exists because the touched files sit on the chat chain path, not because any behavior moved. ## Verification `npm run harness:check` and `npm run build` pass. The existing `tests/e2e/chat-cyber-semantics.spec.ts` still passes unchanged.