@import "tailwindcss"; /* * Dark mode is class-based, not media-based, because the user picks it * explicitly and the choice persists. See components/theme/theme-script.tsx. */ @custom-variant dark (&:where(.dark, .dark *)); /* * Semantic tokens. Named for the job they do, never for the colour they are: * `--surface` survives a palette change, `--grey-100` does not. * * Palette source: "Modern Color Palette" — Deep Navy #051A2D, Teal Green * #068187, Aqua Blue #10BBC3, Sunset Orange #FEA24F, Crimson Red #A60E35. * * Every text token below clears 4.5:1 against the surface it is used on, in * both themes. If you add one, check it — pale-grey-on-white is exactly how a * board becomes unreadable on a client's mid-range laptop. */ :root { /* Structure: a tinted page with a raised panel floating on it. */ --page: #eef2f5; --panel: #ffffff; --surface: #f0f4f6; --surface-raised: #e2e8ec; --border-subtle: #d6dde2; --border-strong: #b8c2cb; --text-primary: #051a2d; --text-secondary: #2c3e50; --text-muted: #5e7080; /* Cool brand teal: primary actions, links, selection. */ --brand: #068187; --brand-hover: #045a60; --brand-fg: #ffffff; --brand-soft: #d6eded; /* * The warm accent. Reserved for exactly three things: an overdue date, the * Inbox count badge, and critical priority. It reads as urgent only while * it stays rare. */ --accent: #a60e35; --accent-fg: #ffffff; --accent-soft: #f9e2e7; --priority-critical: #a60e35; --priority-high: #d97a26; --priority-medium: #068187; --priority-low: #5e7080; --shadow-panel: 0 1px 2px rgba(5, 26, 45, 0.04), 0 16px 40px -12px rgba(5, 26, 45, 0.16); --shadow-tile: 0 1px 2px rgba(5, 26, 45, 0.06); --shadow-drag: 0 12px 28px -6px rgba(5, 26, 45, 0.28); } .dark { --page: #051a2d; --panel: #0a2338; --surface: #122d44; --surface-raised: #1b3854; --border-subtle: #1f3d5c; --border-strong: #2d4e6f; --text-primary: #f0f4f8; --text-secondary: #b8c8d8; --text-muted: #8499ad; --brand: #10bbc3; --brand-hover: #2dd0d8; --brand-fg: #051a2d; --brand-soft: #0c3a45; --accent: #ff5782; --accent-fg: #ffffff; --accent-soft: #4d1629; --priority-critical: #ff5782; --priority-high: #ffb775; --priority-medium: #10bbc3; --priority-low: #8499ad; --shadow-panel: 0 1px 2px rgba(0, 0, 0, 0.4), 0 16px 40px -12px rgba(0, 0, 0, 0.6); --shadow-tile: 0 1px 2px rgba(0, 0, 0, 0.3); --shadow-drag: 0 12px 28px -6px rgba(0, 0, 0, 0.7); } /* * Expose the tokens to Tailwind. `@theme inline` keeps the var() reference * live, so `bg-surface` resolves at runtime and follows the theme class. * Without `inline`, Tailwind would bake in the :root value and dark mode * would silently do nothing. */ @theme inline { --color-page: var(--page); --color-panel: var(--panel); --color-surface: var(--surface); --color-surface-raised: var(--surface-raised); --color-border-subtle: var(--border-subtle); --color-border-strong: var(--border-strong); --color-text-primary: var(--text-primary); --color-text-secondary: var(--text-secondary); --color-text-muted: var(--text-muted); --color-brand: var(--brand); --color-brand-hover: var(--brand-hover); --color-brand-fg: var(--brand-fg); --color-brand-soft: var(--brand-soft); --color-accent: var(--accent); --color-accent-fg: var(--accent-fg); --color-accent-soft: var(--accent-soft); --color-priority-critical: var(--priority-critical); --color-priority-high: var(--priority-high); --color-priority-medium: var(--priority-medium); --color-priority-low: var(--priority-low); --font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif; --radius-tile: 10px; --radius-panel: 18px; } * { border-color: var(--border-subtle); } body { background: var(--page); color: var(--text-primary); font-family: var(--font-sans); -webkit-font-smoothing: antialiased; } /* * Apply the user's stored theme preference on first paint using only CSS. * `:root.dark` is set by ThemeSync after mount; the explicit `:root.dark` * selector below the `@media` block wins over the media query when the * user has made a stored choice (applied before paint by the ThemeSync * effect running synchronously after hydration). * * This file deliberately does not contain any