← Back to task

Commit c8451f89

commit c8451f89d9982e4b00ccf8f94bb6aad7a104dc8e
Author: Coder Agent <coder@agents.omni>
Date:   Wed Feb 18 14:24:54 2026

    Add light theme support via prefers-color-scheme media query
    
    Define CSS custom properties on :root with dark theme defaults and
    override them in a @media (prefers-color-scheme: light) block with a
    warm-cream light palette. Append var() override rules after all
    Clay-generated dark styles so they win the cascade for both the shared
    design system (Omni/Web/Style.hs) and task-specific selectors
    (Omni/Task/Web/Style.hs).
    
    Light palette (inverse of ef-dream dark theme):
    - Background: #faf8f6 (warm cream), raised: #ffffff
    - Foreground: #232025, muted: #6b6568, faint: #9a9497
    - Borders: #d4d0cc / #e8e5e2
    - Accent colors darkened for contrast on light backgrounds
    
    Also update theme-color meta tags in both sharedShell and Task web
    Components to include media attributes for dark/light scheme switching.
    
    Task-Id: t-637

diff --git a/Omni/Task/Web/Components.hs b/Omni/Task/Web/Components.hs
index eaaca370..e93e0bc5 100644
--- a/Omni/Task/Web/Components.hs
+++ b/Omni/Task/Web/Components.hs
@@ -201,7 +201,8 @@ pageHead title =
       ]
     Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-capable", Lucid.content_ "yes"]
     Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-status-bar-style", Lucid.content_ "black-translucent"]
-    Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025"]
+    Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025", Lucid.makeAttribute "media" "(prefers-color-scheme: dark)"]
+    Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#faf8f6", Lucid.makeAttribute "media" "(prefers-color-scheme: light)"]
     Lucid.link_ [Lucid.rel_ "stylesheet", Lucid.href_ "/style.css"]
     Lucid.script_
       [ Lucid.src_ "https://unpkg.com/htmx.org@2.0.4",
diff --git a/Omni/Task/Web/Style.hs b/Omni/Task/Web/Style.hs
index 1f98bc23..13972ecf 100644
--- a/Omni/Task/Web/Style.hs
+++ b/Omni/Task/Web/Style.hs
@@ -18,7 +18,7 @@ import qualified Data.Text.Lazy as LazyText
 import qualified Omni.Web.Style as WebStyle
 
 css :: LazyText.Text
-css = WebStyle.css <> render stylesheet
+css = WebStyle.css <> render stylesheet <> WebStyle.lightModeCss <> taskLightModeCss
 
 stylesheet :: Css
 stylesheet = do
@@ -1937,6 +1937,258 @@ responsiveStyles = do
 darkModeStyles :: Css
 darkModeStyles = pure ()
 
+-- | Light mode var() overrides for task-specific selectors.
+-- Appended after all Clay-generated dark styles AND after
+-- 'WebStyle.lightModeCss' (which defines the CSS custom properties
+-- and overrides shared design-system selectors).
+taskLightModeCss :: LazyText.Text
+taskLightModeCss =
+  LazyText.unlines
+    [ "/* ── Light theme: task-specific var() overrides ────────── */",
+      "",
+      "/* Task navigation */",
+      ".navbar { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+      ".navbar-brand { color: var(--c-accent); }",
+      ".hamburger-line { background-color: var(--c-fg-muted); }",
+      ".navbar-link { color: var(--c-fg-muted); }",
+      ".navbar-link:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+      ".navbar-dropdown-btn { color: var(--c-fg-muted); }",
+      ".navbar-dropdown-btn:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+      ".navbar-dropdown-content { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".navbar-dropdown-item { color: var(--c-fg-muted); }",
+      ".navbar-dropdown-item:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+      "header { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+      ".nav-brand { color: var(--c-accent); }",
+      "",
+      "/* Breadcrumbs */",
+      ".breadcrumb-sep { color: var(--c-fg-faint); }",
+      ".breadcrumb-current { color: var(--c-fg-muted); }",
+      ".breadcrumb-list a { color: var(--c-accent); }",
+      "",
+      "/* Task cards & panels */",
+      ".task-card, .stat-card, .task-detail, .task-summary,",
+      ".filter-form, .status-form, .diff-section, .review-actions",
+      "{ background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".task-card:hover { border-color: var(--c-accent-dim); }",
+      ".task-card-link, .task-card-link:visited { color: var(--c-fg); }",
+      ".task-id { color: var(--c-accent); }",
+      ".priority { color: var(--c-fg-muted); }",
+      ".blocking-impact { color: var(--c-fg-muted); background-color: var(--c-border); }",
+      ".task-title { color: var(--c-fg); }",
+      ".empty-msg, .info-msg { color: var(--c-fg-muted); }",
+      ".kb-preview { color: var(--c-fg-muted); }",
+      ".ready-link { color: var(--c-accent); }",
+      ".count-badge { background-color: var(--c-accent-dim); }",
+      ".description { background-color: var(--c-bg-raised); color: var(--c-fg); }",
+      "",
+      "/* Stat cards */",
+      ".stat-label { color: var(--c-fg-muted); }",
+      ".stat-card.badge-open { border-left-color: var(--c-yellow); }",
+      ".stat-card.badge-open > .stat-count { color: var(--c-yellow); }",
+      ".stat-card.badge-inprogress { border-left-color: var(--c-blue); }",
+      ".stat-card.badge-inprogress > .stat-count { color: var(--c-blue); }",
+      ".stat-card.badge-review { border-left-color: var(--c-purple); }",
+      ".stat-card.badge-review > .stat-count { color: var(--c-purple); }",
+      ".stat-card.badge-approved { border-left-color: var(--c-accent); }",
+      ".stat-card.badge-approved > .stat-count { color: var(--c-accent); }",
+      ".stat-card.badge-done { border-left-color: var(--c-green); }",
+      ".stat-card.badge-done > .stat-count { color: var(--c-green); }",
+      ".stat-card.badge-neutral { border-left-color: var(--c-fg-muted); }",
+      ".stat-card.badge-neutral > .stat-count { color: var(--c-fg-muted); }",
+      "",
+      "/* List groups */",
+      ".list-group { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".list-group-item { border-bottom-color: var(--c-border-subtle); color: var(--c-fg); }",
+      ".list-group-item:visited { color: var(--c-fg); }",
+      ".list-group-item:hover { background-color: var(--c-bg-hover); }",
+      ".list-group-item-id { color: var(--c-accent); }",
+      ".list-group-item-title { color: var(--c-fg); }",
+      "",
+      "/* Status/priority/complexity dropdowns */",
+      ".status-dropdown-menu, .priority-dropdown-menu, .complexity-dropdown-menu",
+      "{ background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      "",
+      "/* Complexity badges */",
+      ".badge-complexity { color: var(--c-accent-dim); }",
+      ".badge-complexity-1 { color: var(--c-green); }",
+      ".badge-complexity-2 { color: var(--c-blue); }",
+      ".badge-complexity-3 { color: var(--c-yellow); }",
+      ".badge-complexity-4 { color: var(--c-orange); }",
+      ".badge-complexity-5 { color: var(--c-red); }",
+      ".badge-complexity-none { background-color: var(--c-bg-hover); color: var(--c-fg-muted); }",
+      "",
+      "/* Progress bars */",
+      ".progress-bar, .multi-progress-bar { background-color: var(--c-border); }",
+      ".progress-fill { background-color: var(--c-accent-dim); }",
+      ".progress-done, .legend-done { background-color: var(--c-green); }",
+      ".progress-inprogress, .legend-inprogress { background-color: var(--c-yellow); }",
+      ".progress-open, .legend-open { background-color: var(--c-blue); }",
+      ".progress-legend { color: var(--c-fg-muted); }",
+      "",
+      "/* Stats & summary sections */",
+      ".stats-section, .summary-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".stats-label { color: var(--c-fg); }",
+      ".stats-count { color: var(--c-fg); }",
+      "",
+      "/* Task buttons */",
+      ".action-btn { background-color: var(--c-bg-raised); border-color: var(--c-border); color: var(--c-fg); }",
+      ".action-btn:hover { background-color: var(--c-bg-hover); border-color: var(--c-fg-faint); }",
+      ".action-btn-primary, .filter-btn, .submit-btn { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+      ".accept-btn { color: var(--c-green); }",
+      ".reject-btn { color: var(--c-red); }",
+      ".review-link-btn { color: var(--c-purple); }",
+      ".clear-btn { background-color: var(--c-bg-hover); color: var(--c-fg); border-color: var(--c-border); }",
+      ".clear-btn:hover { background-color: var(--c-bg-active); }",
+      ".btn-secondary, .load-more-btn { background-color: var(--c-fg-muted); }",
+      "",
+      "/* Task forms */",
+      ".filter-select, .filter-input, .status-select { border-color: var(--c-border); background-color: var(--c-bg); color: var(--c-fg); }",
+      ".reject-notes { border-color: var(--c-border); }",
+      ".description-textarea { border-color: var(--c-border); }",
+      ".form-group > label { color: var(--c-fg); }",
+      ".form-input, .form-textarea { border-color: var(--c-border); }",
+      ".form-input:focus, .form-textarea:focus { border-color: var(--c-accent-dim); }",
+      ".fact-create-form { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".edit-link, .cancel-link { color: var(--c-accent); }",
+      "button.cancel-link { color: var(--c-red); }",
+      ".edit-description > summary { color: var(--c-accent); }",
+      ".task-link { color: var(--c-accent); }",
+      ".error-msg { color: var(--c-red); border-color: var(--c-red); }",
+      ".danger-zone { border-color: var(--c-red); }",
+      ".danger-zone > h2 { color: var(--c-red); }",
+      ".back-link { border-top-color: var(--c-border-subtle); }",
+      ".back-link > a { color: var(--c-fg-muted); }",
+      ".back-link > a:hover { color: var(--c-fg); }",
+      "",
+      "/* Execution details */",
+      ".execution-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".metric-label { color: var(--c-fg-muted); }",
+      ".metric-card { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+      ".metric-card > .metric-value { color: var(--c-fg); }",
+      ".metric-card > .metric-label { color: var(--c-fg-muted); }",
+      ".amp-link { color: var(--c-accent); }",
+      ".amp-thread-btn { background-color: var(--c-purple); }",
+      ".retry-count { color: var(--c-orange); }",
+      ".attempts-divider { border-top-color: var(--c-border-subtle); }",
+      ".attempt-header { color: var(--c-fg); border-bottom-color: var(--c-border-subtle); }",
+      "",
+      "/* Activity timeline */",
+      ".activity-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".activity-timeline::before { background-color: var(--c-border); }",
+      ".activity-icon { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".activity-time { color: var(--c-fg-muted); }",
+      ".activity-message { color: var(--c-fg); }",
+      ".metadata-json { background-color: var(--c-bg-hover); }",
+      ".stage-claiming > .activity-icon { border-color: var(--c-blue); color: var(--c-blue); }",
+      ".stage-running > .activity-icon { border-color: var(--c-yellow); color: var(--c-yellow); }",
+      ".stage-reviewing > .activity-icon { border-color: var(--c-purple); color: var(--c-purple); }",
+      ".stage-retrying > .activity-icon { border-color: var(--c-orange); color: var(--c-orange); }",
+      ".stage-completed > .activity-icon { border-color: var(--c-green); color: var(--c-green); }",
+      ".stage-failed > .activity-icon { border-color: var(--c-red); color: var(--c-red); }",
+      "",
+      "/* Commits */",
+      ".commit-item { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+      ".commit-hash { color: var(--c-accent); background-color: var(--c-border); }",
+      ".commit-summary { color: var(--c-fg); }",
+      ".commit-meta { color: var(--c-fg-muted); }",
+      ".commit-files { color: var(--c-fg-faint); }",
+      "",
+      "/* Markdown */",
+      ".markdown-content { color: var(--c-fg); }",
+      ".md-h1 { border-bottom-color: var(--c-border-subtle); }",
+      ".md-code { background-color: var(--c-bg-raised); color: var(--c-fg); border-color: var(--c-border); }",
+      ".md-inline-code { background-color: var(--c-bg-hover); }",
+      "",
+      "/* Comments */",
+      ".comment-card { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+      ".comment-text { color: var(--c-fg); }",
+      ".author-human { color: var(--c-blue); }",
+      ".author-junior { color: var(--c-green); }",
+      ".comment-time { color: var(--c-fg-faint); }",
+      ".comment-textarea { border-color: var(--c-border); }",
+      ".comment-textarea:focus { border-color: var(--c-accent-dim); }",
+      "",
+      "/* Retry banners */",
+      ".retry-attempt { color: var(--c-fg); }",
+      ".retry-value { color: var(--c-fg-muted); }",
+      ".retry-hint { color: var(--c-fg-muted); }",
+      ".retry-warning-message { color: var(--c-red); }",
+      ".retry-commit { background-color: var(--c-bg-hover); }",
+      "",
+      "/* Time filters */",
+      ".time-filter-btn { border-color: var(--c-border); background-color: var(--c-bg-raised); color: var(--c-fg); }",
+      ".time-filter-btn:hover { border-color: var(--c-fg-faint); background-color: var(--c-bg-hover); }",
+      ".time-filter-btn.active { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+      ".time-filter-btn.active:hover { background-color: var(--c-accent); border-color: var(--c-accent); }",
+      "",
+      "/* Sort dropdown */",
+      ".sort-label { color: var(--c-fg-muted); }",
+      ".sort-dropdown-btn { border-color: var(--c-border); background-color: var(--c-bg-raised); color: var(--c-fg); }",
+      ".sort-dropdown-btn:hover { border-color: var(--c-fg-faint); background-color: var(--c-bg-hover); }",
+      "",
+      "/* Task meta */",
+      ".task-meta-id { background-color: var(--c-bg-hover); }",
+      ".task-meta-label { color: var(--c-fg-muted); }",
+      ".meta-sep { color: var(--c-fg); }",
+      "",
+      "/* Detail rows */",
+      ".detail-label { color: var(--c-fg-muted); }",
+      ".detail-section { border-top-color: var(--c-border-subtle); }",
+      ".dep-type, .child-status { color: var(--c-fg-muted); }",
+      ".child-title { color: var(--c-fg); }",
+      ".priority-desc { color: var(--c-fg-muted); }",
+      "",
+      "/* Timeline events */",
+      ".event-bubble { background-color: var(--c-bg-hover); }",
+      ".event-truncated { color: var(--c-fg-muted); }",
+      ".event-tool-call { border-left-color: var(--c-blue); }",
+      ".tool-name { color: var(--c-blue); }",
+      ".tool-summary { color: var(--c-fg-muted); }",
+      ".tool-output-pre, .tool-output { background-color: var(--c-bg); color: var(--c-fg); }",
+      ".event-tool-result { border-left-color: var(--c-green); }",
+      ".event-cost, .timeline-cost { color: var(--c-fg-muted); }",
+      ".line-count { color: var(--c-fg-muted); background-color: var(--c-bg-hover); }",
+      ".result-collapsible > summary, .output-collapsible > summary { color: var(--c-accent); }",
+      ".event-error { border-left-color: var(--c-red); }",
+      ".event-error > .event-label { color: var(--c-red); }",
+      ".error-message { color: var(--c-red); }",
+      ".event-complete { color: var(--c-green); }",
+      "",
+      "/* Unified timeline */",
+      ".unified-timeline-section { border-top-color: var(--c-border-subtle); }",
+      ".timeline-live-toggle { color: var(--c-green); border-color: var(--c-green); }",
+      ".timeline-live-toggle.timeline-live-paused { color: var(--c-fg-muted); background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+      ".timeline-autoscroll-toggle { color: var(--c-blue); border-color: var(--c-blue); }",
+      ".timeline-autoscroll-toggle.timeline-autoscroll-disabled { color: var(--c-fg-muted); background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+      ".timeline-live { color: var(--c-green); }",
+      ".actor-human { color: var(--c-purple); }",
+      ".actor-junior { color: var(--c-blue); }",
+      ".actor-system { color: var(--c-fg-muted); background-color: var(--c-bg-hover); }",
+      ".timeline-comment > .comment-bubble { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+      ".status-change-text { color: var(--c-green); }",
+      ".timeline-activity { color: var(--c-fg-muted); }",
+      ".activity-detail { color: var(--c-fg-faint); }",
+      ".timeline-error > .error-message { color: var(--c-red); }",
+      ".timeline-thought > .thought-bubble { color: var(--c-yellow); }",
+      ".timeline-tool-call { border-left-color: var(--c-blue); }",
+      ".timeline-tool-result { border-left-color: var(--c-green); }",
+      ".timeline-checkpoint { border-left-color: var(--c-purple); }",
+      ".timeline-guardrail { border-left-color: var(--c-yellow); }",
+      ".timeline-guardrail > .guardrail-content { color: var(--c-yellow); }",
+      ".timeline-generic { color: var(--c-fg-muted); }",
+      ".formatted-json { background-color: var(--c-bg-raised); }",
+      "",
+      "/* Tool compact styles */",
+      ".tool-check { color: var(--c-green); }",
+      ".tool-label { color: var(--c-fg-muted); }",
+      ".tool-path { color: var(--c-blue); }",
+      ".tool-pattern { color: var(--c-purple); }",
+      ".tool-path-suffix { color: var(--c-fg-muted); }",
+      ".tool-bash-prompt { color: var(--c-yellow); }",
+      ".tool-bash-cmd { color: var(--c-fg); background-color: var(--c-bg-hover); }",
+      ".tool-args-pre { background-color: var(--c-bg-raised); }"
+    ]
+
 statusBadgeClass :: Text -> Text
 statusBadgeClass status = case status of
   "Open" -> "badge badge-open"
diff --git a/Omni/Web/Style.hs b/Omni/Web/Style.hs
index 41c8d5fe..be4266a7 100644
--- a/Omni/Web/Style.hs
+++ b/Omni/Web/Style.hs
@@ -15,6 +15,9 @@ module Omni.Web.Style
   ( -- * Full rendered CSS
     css,
 
+    -- * Light mode CSS (append after all dark styles)
+    lightModeCss,
+
     -- * Design tokens (re-export for other Clay modules)
     cBg,
     cBgRaised,
@@ -53,6 +56,7 @@ import qualified Clay.Media as Media
 import qualified Clay.Stylesheet as Stylesheet
 import qualified Data.Text.Lazy as LazyText
 import qualified Lucid
+import qualified Lucid.Base as Lucid
 
 -- ============================================================================
 -- Design Tokens
@@ -553,6 +557,122 @@ selectionStyles :: Css
 selectionStyles =
   Stylesheet.key "::selection" ("{ background: rgba(111,179,192,0.35); }" :: Text)
 
+-- ============================================================================
+-- Light Mode Support
+-- ============================================================================
+
+-- | CSS custom properties with dark defaults, light-mode @media overrides,
+-- and var() rules for all shared design-system selectors.
+--
+-- Append this AFTER all Clay-generated dark styles so the var() declarations
+-- win the cascade.  In dark mode the vars resolve to the same values Clay
+-- hard-coded; in light mode they resolve to the light palette.
+lightModeCss :: LazyText.Text
+lightModeCss =
+  LazyText.unlines
+    [ "/* ── Light theme: custom properties ─────────────────────── */",
+      ":root {",
+      "  --c-bg: #232025;",
+      "  --c-bg-raised: #2c292e;",
+      "  --c-bg-hover: #322f34;",
+      "  --c-bg-active: #3b393e;",
+      "  --c-border: #3b393e;",
+      "  --c-border-subtle: #322f34;",
+      "  --c-fg: #efd5c5;",
+      "  --c-fg-muted: #8f8886;",
+      "  --c-fg-faint: #6b6568;",
+      "  --c-accent: #6fb3c0;",
+      "  --c-accent-dim: #5a9aa6;",
+      "  --c-link: #57b0ff;",
+      "  --c-green: #51b04f;",
+      "  --c-yellow: #c0b24f;",
+      "  --c-orange: #f3c09a;",
+      "  --c-red: #ff6f6f;",
+      "  --c-purple: #d0b0ff;",
+      "  --c-blue: #57b0ff;",
+      "}",
+      "@media (prefers-color-scheme: light) {",
+      "  :root {",
+      "    --c-bg: #faf8f6;",
+      "    --c-bg-raised: #ffffff;",
+      "    --c-bg-hover: #f0eeec;",
+      "    --c-bg-active: #e6e3e0;",
+      "    --c-border: #d4d0cc;",
+      "    --c-border-subtle: #e8e5e2;",
+      "    --c-fg: #232025;",
+      "    --c-fg-muted: #6b6568;",
+      "    --c-fg-faint: #9a9497;",
+      "    --c-accent: #4a8f9a;",
+      "    --c-accent-dim: #3d7a84;",
+      "    --c-link: #2563eb;",
+      "    --c-green: #16a34a;",
+      "    --c-yellow: #a38a00;",
+      "    --c-orange: #c2601a;",
+      "    --c-red: #dc2626;",
+      "    --c-purple: #7c3aed;",
+      "    --c-blue: #2563eb;",
+      "  }",
+      "  ::selection { background: rgba(37,99,235,0.2); }",
+      "  ::-webkit-scrollbar-thumb { background: #d4d0cc; }",
+      "}",
+      "",
+      "/* ── Light theme: var() overrides for shared design system ─ */",
+      "body { background-color: var(--c-bg); color: var(--c-fg); }",
+      "a, a:visited { color: var(--c-link); }",
+      "h2 { color: var(--c-fg); }",
+      "code { background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+      "pre { background-color: var(--c-bg); border-color: var(--c-border); }",
+      ".reading > blockquote { border-left-color: var(--c-accent-dim); color: var(--c-fg-muted); }",
+      "",
+      "/* Navigation */",
+      ".on-nav { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+      ".on-nav-brand { color: var(--c-accent); }",
+      ".on-nav-link { color: var(--c-fg-muted); }",
+      ".on-nav-link:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+      ".on-active { color: var(--c-accent); background-color: var(--c-bg-hover); }",
+      "",
+      "/* Cards */",
+      ".card { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+      ".card-link:hover > .card { border-color: var(--c-accent-dim); }",
+      "",
+      "/* Badges */",
+      ".badge-open { color: var(--c-yellow); }",
+      ".badge-inprogress { color: var(--c-blue); }",
+      ".badge-review { color: var(--c-purple); }",
+      ".badge-approved { color: var(--c-accent); }",
+      ".badge-done { color: var(--c-green); }",
+      ".badge-needshelp { color: var(--c-orange); }",
+      ".badge-p0 { color: var(--c-red); }",
+      ".badge-p1 { color: var(--c-orange); }",
+      ".badge-p2 { color: var(--c-blue); }",
+      ".badge-p3 { background-color: var(--c-bg-hover); color: var(--c-fg-muted); }",
+      ".badge-p4 { background-color: var(--c-bg-hover); color: var(--c-fg-faint); }",
+      "",
+      "/* Buttons */",
+      ".btn { background-color: var(--c-bg-raised); border-color: var(--c-border); color: var(--c-fg); }",
+      ".btn:hover { background-color: var(--c-bg-hover); border-color: var(--c-fg-faint); }",
+      ".btn:active { background-color: var(--c-bg-active); }",
+      ".btn-primary { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+      ".btn-primary:hover { background-color: var(--c-accent); border-color: var(--c-accent); }",
+      ".btn-ghost:hover { background-color: var(--c-bg-hover); }",
+      "",
+      "/* Forms */",
+      "input, select, textarea { color: var(--c-fg); background-color: var(--c-bg); border-color: var(--c-border); }",
+      "input:focus, select:focus, textarea:focus { border-color: var(--c-accent-dim); }",
+      "",
+      "/* Tables */",
+      "th, td { border-bottom-color: var(--c-border); }",
+      "th { color: var(--c-fg-muted); }",
+      "tr:hover { background-color: var(--c-bg-hover); }",
+      "",
+      "/* Utilities */",
+      ".muted { color: var(--c-fg-muted); }",
+      ".faint { color: var(--c-fg-faint); }",
+      ".accent { color: var(--c-accent); }",
+      ".empty-state { color: var(--c-fg-faint); }",
+      ".divider { border-top-color: var(--c-border); }"
+    ]
+
 -- ============================================================================
 -- Shared HTML Components
 -- ============================================================================
@@ -583,9 +703,10 @@ sharedShell title' activePage' content' =
       Lucid.meta_ [Lucid.name_ "viewport", Lucid.content_ "width=device-width, initial-scale=1, viewport-fit=cover"]
       Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-capable", Lucid.content_ "yes"]
       Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-status-bar-style", Lucid.content_ "black-translucent"]
-      Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025"]
+      Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025", Lucid.makeAttribute "media" "(prefers-color-scheme: dark)"]
+      Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#faf8f6", Lucid.makeAttribute "media" "(prefers-color-scheme: light)"]
       Lucid.title_ (Lucid.toHtml title')
-      Lucid.style_ (LazyText.toStrict css)
+      Lucid.style_ (LazyText.toStrict (css <> lightModeCss))
     Lucid.body_ <| do
       sharedNav activePage'
       content'