Adds light / dark / system theming. The layout previously force-locked light; now there's a real theme system with a Settings toggle.
What's added
- Theme bootstrap runs before paint (no flash) — resolves the stored preference (
light|dark|system, default system) againstprefers-color-scheme. A Settings → Appearance segmented control persists the choice and applies it live, and keepssystemin sync if the OS theme flips while the page is open. - Dark theme via a scoped
.darkCSS remap. The views use a small, consistent utility palette (bg-white cards, zinc borders, neutral text), so one place remaps them — surfaces, borders, text, hover states, and inverted primary buttons — instead of addingdark:variants to ~380 call sites. Unlayered<head>styles beat crosswind's layered utilities. - Full coverage: the shell (sidebar/workspace, targeted by data-attribute), the analytics chart's hardcoded SVG palette (so it's not a light island), the timeline feed and toast stylesheets (their own classes, not utilities), and the login page (separate layout — self-contained bootstrap + overrides).
Verification (browser)
Screenshotted in dark across composer, queue (list and calendar), analytics (chart blends in), settings (toggle), accounts, timeline (feed), and login — all readable and consistent. Light mode unchanged (the .dark-scoped rules only activate with the class — confirmed no regression). tsc exit 0; 36 tests; pickier clean.
Off the optional MVP list.