The seven existing themes are warm beige, cool grey, sage grey, slate, warm dark, graphite and black — every one neutral, six of the seven accented orange. Picking a theme changed the shade of grey and little else. These colour the SURFACES, not just the accent, and each takes a different primary so the picker tells them apart at a glance: violet on deep indigo, cyan on deep teal, magenta on aubergine, indigo on a crisp cool white. The semantic colours stay recognisable as themselves. A logger is read for hours and "red means a problem" cannot become a decorative choice, so the hue budget went on the surfaces and the primary. Where a theme's primary would have collided with a meaning, the MEANING kept its identity and the ornament moved: Ocean's info is blue rather than cyan, Plum's danger is red rather than rose, and the matrix entity ramp shifts to cyan under Indigo and teal under Nordic so a "confirmed" cell never reads as a button. Everything else follows for free: the grids resolve var(--…) at runtime, so they re-skin with no re-render. The three shared registrations that do NOT follow automatically are done — the chart palette (light vs dark ramp) and the date-picker icon inversion, which is keyed on an explicit list of dark themes.
974 lines
31 KiB
CSS
974 lines
31 KiB
CSS
/* External @imports must come first per CSS spec. */
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
|
||
@import "tailwindcss";
|
||
@import "tw-animate-css";
|
||
|
||
/* ============================================================================
|
||
THEME SYSTEM
|
||
----------------------------------------------------------------------------
|
||
Every color is a semantic CSS variable declared per theme below. The
|
||
`@theme inline` block points Tailwind's color utilities (bg-*, text-*,
|
||
border-*, ring-*, …) directly at those variables, so switching the
|
||
`data-theme` attribute on <html> reskins the whole UI at runtime with no
|
||
rebuild. Four themes ship: two light-neutral surfaces and status families
|
||
for OK / warning / caution / danger / info that adapt to each surface.
|
||
|
||
Semantic status vocabulary (use these, NOT raw palette colors):
|
||
success — worked / confirmed / data / OK (was emerald)
|
||
warning — new-band / attention (was amber)
|
||
caution — new-mode / new-slot / lighter alert (was yellow)
|
||
danger — new / split / negative (was rose)
|
||
info — informational (was sky/blue)
|
||
destructive — hard errors / live REC / delete (was red)
|
||
Each family has: base (dots/bars/solid), -foreground (text on base),
|
||
-muted (soft pill fill), -muted-foreground (text on pill), -border.
|
||
========================================================================= */
|
||
|
||
/* ---- Theme 1: Warm light (default) — warm taupe & burnt orange ---------- */
|
||
:root,
|
||
[data-theme="light-warm"] {
|
||
--background: #e8dfc9; /* warm taupe — deeper than cream */
|
||
--foreground: #1c1917; /* stone-900 */
|
||
--card: #faf6ea; /* lifted off-white cream */
|
||
--card-foreground: #1c1917;
|
||
--popover: #faf6ea;
|
||
--popover-foreground: #1c1917;
|
||
--primary: #b8410c; /* burnt orange, slightly deeper */
|
||
--primary-foreground: #fff7ed;
|
||
--secondary: #ddd2b8;
|
||
--secondary-foreground: #1c1917;
|
||
--muted: #ddd2b8; /* toolbars / table headers */
|
||
--muted-foreground: #44403c; /* stone-700 — readable on muted */
|
||
--accent: #f8d6a9; /* warm amber-cream */
|
||
--accent-foreground: #7c2d12; /* orange-900 */
|
||
--destructive: #a51c1c;
|
||
--destructive-foreground: #ffffff;
|
||
--destructive-muted: #fef2f2;
|
||
--destructive-muted-foreground: #b91c1c;
|
||
--border: #c8b994; /* deeper warm beige border */
|
||
--input: #c8b994;
|
||
--ring: #d97706; /* amber-600 focus ring */
|
||
|
||
--success: #16a34a;
|
||
--success-foreground: #ffffff;
|
||
--success-muted: #dcfce7;
|
||
--success-muted-foreground: #166534;
|
||
--success-border: #86efac;
|
||
|
||
--warning: #d97706;
|
||
--warning-foreground: #ffffff;
|
||
--warning-muted: #fef3c7;
|
||
--warning-muted-foreground: #92400e;
|
||
--warning-border: #fcd34d;
|
||
|
||
--caution: #ca8a04;
|
||
--caution-foreground: #1c1917;
|
||
--caution-muted: #fef9c3;
|
||
--caution-muted-foreground: #854d0e;
|
||
--caution-border: #fde047;
|
||
|
||
--danger: #e11d48;
|
||
--danger-foreground: #ffffff;
|
||
--danger-muted: #ffe4e6;
|
||
--danger-muted-foreground: #9f1239;
|
||
--danger-border: #fda4af;
|
||
|
||
--info: #0284c7;
|
||
--info-foreground: #ffffff;
|
||
--info-muted: #e0f2fe;
|
||
--info-muted-foreground: #075985;
|
||
--info-border: #7dd3fc;
|
||
|
||
/* Band/mode matrix (BandSlotGrid) — original emerald/indigo/stone ramp. */
|
||
--mx-call-conf: #15803d; /* call confirmed */
|
||
--mx-call-work: #6ee7b7; /* call worked */
|
||
--mx-dx-conf: #3730a3; /* entity confirmed*/
|
||
--mx-dx-work: #a5b4fc; /* entity worked */
|
||
--mx-none: #e7e5e4; /* never worked */
|
||
|
||
--scrollbar-thumb: #b8a880;
|
||
--scrollbar-thumb-hover: #968455;
|
||
--card-shadow: 0 1px 2px rgba(28, 25, 23, 0.05), 0 0 0 1px rgba(28, 25, 23, 0.02);
|
||
color-scheme: light;
|
||
}
|
||
|
||
/* ---- Theme 1b: Cool light (slate/blue) — crisp daylight, neutral cool --- */
|
||
[data-theme="light-cool"] {
|
||
--background: #f4f6f8; /* cool blue-white */
|
||
--foreground: #0f172a; /* slate-900 */
|
||
--card: #ffffff;
|
||
--card-foreground: #0f172a;
|
||
--popover: #ffffff;
|
||
--popover-foreground: #0f172a;
|
||
--primary: #2563eb; /* blue-600 */
|
||
--primary-foreground: #ffffff;
|
||
--secondary: #e2e8f0; /* slate-200 */
|
||
--secondary-foreground: #0f172a;
|
||
--muted: #e6ebf1; /* toolbars / table headers */
|
||
--muted-foreground: #475569; /* slate-600 — readable on muted */
|
||
--accent: #dbeafe; /* blue-100 */
|
||
--accent-foreground: #1e3a8a; /* blue-900 */
|
||
--destructive: #dc2626;
|
||
--destructive-foreground: #ffffff;
|
||
--destructive-muted: #fef2f2;
|
||
--destructive-muted-foreground: #b91c1c;
|
||
--border: #d3dae2; /* cool slate border */
|
||
--input: #d3dae2;
|
||
--ring: #3b82f6; /* blue-500 focus ring */
|
||
|
||
--success: #16a34a;
|
||
--success-foreground: #ffffff;
|
||
--success-muted: #dcfce7;
|
||
--success-muted-foreground: #166534;
|
||
--success-border: #86efac;
|
||
|
||
--warning: #d97706;
|
||
--warning-foreground: #ffffff;
|
||
--warning-muted: #fef3c7;
|
||
--warning-muted-foreground: #92400e;
|
||
--warning-border: #fcd34d;
|
||
|
||
--caution: #ca8a04;
|
||
--caution-foreground: #1c1917;
|
||
--caution-muted: #fef9c3;
|
||
--caution-muted-foreground: #854d0e;
|
||
--caution-border: #fde047;
|
||
|
||
--danger: #e11d48;
|
||
--danger-foreground: #ffffff;
|
||
--danger-muted: #ffe4e6;
|
||
--danger-muted-foreground: #9f1239;
|
||
--danger-border: #fda4af;
|
||
|
||
--info: #0284c7;
|
||
--info-foreground: #ffffff;
|
||
--info-muted: #e0f2fe;
|
||
--info-muted-foreground: #075985;
|
||
--info-border: #7dd3fc;
|
||
|
||
/* Band/mode matrix (BandSlotGrid) — emerald/indigo ramp, cool neutral base. */
|
||
--mx-call-conf: #15803d;
|
||
--mx-call-work: #6ee7b7;
|
||
--mx-dx-conf: #3730a3;
|
||
--mx-dx-work: #a5b4fc;
|
||
--mx-none: #e2e8f0;
|
||
|
||
--scrollbar-thumb: #b6c2d1;
|
||
--scrollbar-thumb-hover: #8fa1b5;
|
||
--card-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 0 0 1px rgba(15, 23, 42, 0.02);
|
||
color-scheme: light;
|
||
}
|
||
|
||
/* ---- Theme 1c: Sage light (Nordic) — soft green-grey, low-fatigue ------- */
|
||
[data-theme="light-sage"] {
|
||
--background: #eef1ec; /* pale sage */
|
||
--foreground: #1a2420; /* deep pine-ink */
|
||
--card: #f8faf6; /* lifted off-white with a green cast */
|
||
--card-foreground: #1a2420;
|
||
--popover: #f8faf6;
|
||
--popover-foreground: #1a2420;
|
||
--primary: #2f855a; /* deep sage green */
|
||
--primary-foreground: #f0fdf4;
|
||
--secondary: #dde5db;
|
||
--secondary-foreground: #1a2420;
|
||
--muted: #dde5db; /* toolbars / table headers */
|
||
--muted-foreground: #445048; /* muted pine — readable on muted */
|
||
--accent: #d7e8d5; /* soft green */
|
||
--accent-foreground: #1e4634; /* forest-900 */
|
||
--destructive: #b91c1c;
|
||
--destructive-foreground: #ffffff;
|
||
--destructive-muted: #fef2f2;
|
||
--destructive-muted-foreground: #b91c1c;
|
||
--border: #cdd8ce; /* soft sage border */
|
||
--input: #cdd8ce;
|
||
--ring: #38a169; /* green focus ring */
|
||
|
||
--success: #16a34a;
|
||
--success-foreground: #ffffff;
|
||
--success-muted: #dcfce7;
|
||
--success-muted-foreground: #166534;
|
||
--success-border: #86efac;
|
||
|
||
--warning: #d97706;
|
||
--warning-foreground: #ffffff;
|
||
--warning-muted: #fef3c7;
|
||
--warning-muted-foreground: #92400e;
|
||
--warning-border: #fcd34d;
|
||
|
||
--caution: #ca8a04;
|
||
--caution-foreground: #1c1917;
|
||
--caution-muted: #fef9c3;
|
||
--caution-muted-foreground: #854d0e;
|
||
--caution-border: #fde047;
|
||
|
||
--danger: #e11d48;
|
||
--danger-foreground: #ffffff;
|
||
--danger-muted: #ffe4e6;
|
||
--danger-muted-foreground: #9f1239;
|
||
--danger-border: #fda4af;
|
||
|
||
--info: #0284c7;
|
||
--info-foreground: #ffffff;
|
||
--info-muted: #e0f2fe;
|
||
--info-muted-foreground: #075985;
|
||
--info-border: #7dd3fc;
|
||
|
||
/* Band/mode matrix (BandSlotGrid) — emerald/indigo ramp, sage neutral base. */
|
||
--mx-call-conf: #15803d;
|
||
--mx-call-work: #6ee7b7;
|
||
--mx-dx-conf: #3730a3;
|
||
--mx-dx-work: #a5b4fc;
|
||
--mx-none: #dde5db;
|
||
|
||
--scrollbar-thumb: #b3c2b3;
|
||
--scrollbar-thumb-hover: #91a591;
|
||
--card-shadow: 0 1px 2px rgba(26, 36, 32, 0.06), 0 0 0 1px rgba(26, 36, 32, 0.02);
|
||
color-scheme: light;
|
||
}
|
||
|
||
/* ---- Theme 1d: Dim slate — mid-tone twilight, between light and dark ---- */
|
||
[data-theme="dim-slate"] {
|
||
--background: #343b47; /* medium slate — light text, not deep dark */
|
||
--foreground: #eceef2;
|
||
--card: #3d4552; /* lifted panel */
|
||
--card-foreground: #eceef2;
|
||
--popover: #3d4552;
|
||
--popover-foreground: #eceef2;
|
||
--primary: #fb923c; /* orange-400 — brand warmth, pops on slate */
|
||
--primary-foreground: #241206;
|
||
--secondary: #454e5c;
|
||
--secondary-foreground: #eceef2;
|
||
--muted: #414a58; /* toolbars / table headers */
|
||
--muted-foreground: #b4bcc9; /* readable on muted */
|
||
--accent: #495468; /* hover / selection tint */
|
||
--accent-foreground: #dce3ef;
|
||
--destructive: #ef4444;
|
||
--destructive-foreground: #1a0808;
|
||
--destructive-muted: #3f1f1f;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #4e5867;
|
||
--input: #4e5867;
|
||
--ring: #fdba74; /* orange-300 focus ring */
|
||
|
||
--success: #34d399;
|
||
--success-foreground: #06231a;
|
||
--success-muted: #1c3d34;
|
||
--success-muted-foreground: #6ee7b7;
|
||
--success-border: #2a5f4c;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #241a06;
|
||
--warning-muted: #41371a;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #5e4d22;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #242006;
|
||
--caution-muted: #403b1a;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #5b5222;
|
||
|
||
--danger: #fb7185;
|
||
--danger-foreground: #260a11;
|
||
--danger-muted: #421f28;
|
||
--danger-muted-foreground: #fda4af;
|
||
--danger-border: #63303c;
|
||
|
||
--info: #38bdf8;
|
||
--info-foreground: #061f2b;
|
||
--info-muted: #16384a;
|
||
--info-muted-foreground: #7dd3fc;
|
||
--info-border: #1d5670;
|
||
|
||
/* Matrix: bright confirmed, medium worked, slate not-worked (sits on dim). */
|
||
--mx-call-conf: #22c55e;
|
||
--mx-call-work: #2f7d55;
|
||
--mx-dx-conf: #6366f1;
|
||
--mx-dx-work: #3f3f8a;
|
||
--mx-none: #4a5262;
|
||
|
||
--scrollbar-thumb: #565f70;
|
||
--scrollbar-thumb-hover: #6b7588;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(236, 238, 242, 0.05);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 2: Warm dark (espresso) — same soul, night mode ------------- */
|
||
[data-theme="dark-warm"] {
|
||
--background: #221d18;
|
||
--foreground: #ede6d8;
|
||
--card: #2e2820;
|
||
--card-foreground: #ede6d8;
|
||
--popover: #2e2820;
|
||
--popover-foreground: #ede6d8;
|
||
--primary: #e07a2e; /* brighter burnt orange for dark */
|
||
--primary-foreground: #1a1512;
|
||
--secondary: #3a3229;
|
||
--secondary-foreground: #ede6d8;
|
||
--muted: #352e26;
|
||
--muted-foreground: #bcae9a;
|
||
--accent: #4a3a28;
|
||
--accent-foreground: #f8d6a9;
|
||
--destructive: #e5484d;
|
||
--destructive-foreground: #1a1512;
|
||
--destructive-muted: #3a1414;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #473e33;
|
||
--input: #473e33;
|
||
--ring: #e8853a;
|
||
|
||
--success: #34d399;
|
||
--success-foreground: #0e2a20;
|
||
--success-muted: #123024;
|
||
--success-muted-foreground: #6ee7b7;
|
||
--success-border: #1e5540;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #2a1f08;
|
||
--warning-muted: #3a2e10;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #5c4a18;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #2a2607;
|
||
--caution-muted: #38330f;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #574f18;
|
||
|
||
--danger: #fb7185;
|
||
--danger-foreground: #2a0d13;
|
||
--danger-muted: #3a1720;
|
||
--danger-muted-foreground: #fda4af;
|
||
--danger-border: #5c2530;
|
||
|
||
--info: #38bdf8;
|
||
--info-foreground: #08222e;
|
||
--info-muted: #0c2a3a;
|
||
--info-muted-foreground: #7dd3fc;
|
||
--info-border: #164a5c;
|
||
|
||
/* Matrix: bright confirmed, medium-solid worked (clearly visible on dark),
|
||
warm-gray not-worked lifted off the card. */
|
||
--mx-call-conf: #22c55e;
|
||
--mx-call-work: #2f7d55;
|
||
--mx-dx-conf: #6366f1;
|
||
--mx-dx-work: #3f3f8a;
|
||
--mx-none: #453d33;
|
||
|
||
--scrollbar-thumb: #5c5044;
|
||
--scrollbar-thumb-hover: #7a6a58;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 240, 220, 0.04);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 3: Graphite dark — cool slate, technical look --------------- */
|
||
[data-theme="dark-graphite"] {
|
||
--background: #16181d;
|
||
--foreground: #e6e8ec;
|
||
--card: #1f232b;
|
||
--card-foreground: #e6e8ec;
|
||
--popover: #1f232b;
|
||
--popover-foreground: #e6e8ec;
|
||
--primary: #f97316; /* orange-500 pops on slate */
|
||
--primary-foreground: #0a0c10;
|
||
--secondary: #272c35;
|
||
--secondary-foreground: #e6e8ec;
|
||
--muted: #232830;
|
||
--muted-foreground: #9aa2b1;
|
||
--accent: #2a3240;
|
||
--accent-foreground: #cbd5e5;
|
||
--destructive: #ef4444;
|
||
--destructive-foreground: #0a0c10;
|
||
--destructive-muted: #331414;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #2e343f;
|
||
--input: #2e343f;
|
||
--ring: #fb8c3c;
|
||
|
||
--success: #34d399;
|
||
--success-foreground: #06231a;
|
||
--success-muted: #0f2e26;
|
||
--success-muted-foreground: #6ee7b7;
|
||
--success-border: #17513f;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #241a06;
|
||
--warning-muted: #33290f;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #4f3f16;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #242006;
|
||
--caution-muted: #322e0e;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #4c4416;
|
||
|
||
--danger: #fb7185;
|
||
--danger-foreground: #260a11;
|
||
--danger-muted: #34141d;
|
||
--danger-muted-foreground: #fda4af;
|
||
--danger-border: #532430;
|
||
|
||
--info: #38bdf8;
|
||
--info-foreground: #061f2b;
|
||
--info-muted: #0b2938;
|
||
--info-muted-foreground: #7dd3fc;
|
||
--info-border: #124659;
|
||
|
||
/* Matrix: bright confirmed, medium-solid worked, cool-gray not-worked. */
|
||
--mx-call-conf: #22c55e;
|
||
--mx-call-work: #2f7d55;
|
||
--mx-dx-conf: #6366f1;
|
||
--mx-dx-work: #3f3f8a;
|
||
--mx-none: #333a45;
|
||
|
||
--scrollbar-thumb: #3a4150;
|
||
--scrollbar-thumb-hover: #4e5768;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(230, 232, 236, 0.04);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 4: High contrast — pure black / white, max legibility ------- */
|
||
[data-theme="high-contrast"] {
|
||
--background: #000000;
|
||
--foreground: #ffffff;
|
||
--card: #0d0d0d;
|
||
--card-foreground: #ffffff;
|
||
--popover: #0d0d0d;
|
||
--popover-foreground: #ffffff;
|
||
--primary: #ff7a1a;
|
||
--primary-foreground: #000000;
|
||
--secondary: #1a1a1a;
|
||
--secondary-foreground: #ffffff;
|
||
--muted: #141414;
|
||
--muted-foreground: #d4d4d4;
|
||
--accent: #262626;
|
||
--accent-foreground: #ffffff;
|
||
--destructive: #ff4d4d;
|
||
--destructive-foreground: #000000;
|
||
--destructive-muted: #2a0808;
|
||
--destructive-muted-foreground: #ff9999;
|
||
--border: #3f3f3f;
|
||
--input: #3f3f3f;
|
||
--ring: #ffab5e;
|
||
|
||
--success: #22e37a;
|
||
--success-foreground: #002313;
|
||
--success-muted: #06231a;
|
||
--success-muted-foreground: #6affb0;
|
||
--success-border: #0f6b4a;
|
||
|
||
--warning: #ffcc33;
|
||
--warning-foreground: #201900;
|
||
--warning-muted: #2a2205;
|
||
--warning-muted-foreground: #ffe08a;
|
||
--warning-border: #6b5510;
|
||
|
||
--caution: #ffe14d;
|
||
--caution-foreground: #201d00;
|
||
--caution-muted: #2a2705;
|
||
--caution-muted-foreground: #fff08a;
|
||
--caution-border: #6b6210;
|
||
|
||
--danger: #ff6b7d;
|
||
--danger-foreground: #23000a;
|
||
--danger-muted: #2a0a12;
|
||
--danger-muted-foreground: #ffb3bd;
|
||
--danger-border: #6b1f2c;
|
||
|
||
--info: #4dc4ff;
|
||
--info-foreground: #001a26;
|
||
--info-muted: #052330;
|
||
--info-muted-foreground: #a3e0ff;
|
||
--info-border: #0f5a6b;
|
||
|
||
/* Matrix: max-contrast ramp on pure black. */
|
||
--mx-call-conf: #2ee87f;
|
||
--mx-call-work: #17a85a;
|
||
--mx-dx-conf: #7d97ff;
|
||
--mx-dx-work: #3646b0;
|
||
--mx-none: #3a3a3a;
|
||
|
||
--scrollbar-thumb: #4a4a4a;
|
||
--scrollbar-thumb-hover: #666666;
|
||
--card-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 5: Indigo — deep blue-violet, electric accent ----------------
|
||
The first seven themes are all neutral (beige, slate, graphite, black) with an
|
||
orange accent, so "another dark theme" only ever meant another grey. These
|
||
four carry a real hue in the SURFACES, not just in the accent, and each takes
|
||
a different primary — violet, cyan, pink, indigo — so they are told apart at a
|
||
glance in the picker.
|
||
|
||
Every one keeps the same contract: the semantic colours (success / warning /
|
||
caution / danger / info) must stay recognisable as themselves. A logger is
|
||
read for hours and "red = a problem" cannot become a decorative choice, so the
|
||
hue budget is spent on the surfaces and the primary, never on the meanings. */
|
||
[data-theme="dark-indigo"] {
|
||
--background: #0e0f1f;
|
||
--foreground: #e4e5f2;
|
||
--card: #181a30;
|
||
--card-foreground: #e4e5f2;
|
||
--popover: #181a30;
|
||
--popover-foreground: #e4e5f2;
|
||
--primary: #7c6cff; /* electric violet */
|
||
--primary-foreground: #0b0a1a;
|
||
--secondary: #222542;
|
||
--secondary-foreground: #e4e5f2;
|
||
--muted: #1e2038;
|
||
--muted-foreground: #9d9fbe;
|
||
--accent: #2a2d51;
|
||
--accent-foreground: #c7c9ee;
|
||
--destructive: #f2555a;
|
||
--destructive-foreground: #12030a;
|
||
--destructive-muted: #34131c;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #2a2d4c;
|
||
--input: #2a2d4c;
|
||
--ring: #9b8dff;
|
||
|
||
--success: #34d399;
|
||
--success-foreground: #052018;
|
||
--success-muted: #0e2f27;
|
||
--success-muted-foreground: #6ee7b7;
|
||
--success-border: #165241;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #221803;
|
||
--warning-muted: #33280f;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #4f3e15;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #221e04;
|
||
--caution-muted: #322d0e;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #4b4315;
|
||
|
||
--danger: #fb7185;
|
||
--danger-foreground: #250912;
|
||
--danger-muted: #37151f;
|
||
--danger-muted-foreground: #fda4af;
|
||
--danger-border: #562533;
|
||
|
||
--info: #38bdf8;
|
||
--info-foreground: #051d29;
|
||
--info-muted: #0b2a3a;
|
||
--info-muted-foreground: #7dd3fc;
|
||
--info-border: #12475c;
|
||
|
||
/* The entity ramp moves to CYAN here: violet is the primary, and a violet
|
||
"entity confirmed" cell would read as a button. */
|
||
--mx-call-conf: #22c55e;
|
||
--mx-call-work: #2c7a52;
|
||
--mx-dx-conf: #22d3ee;
|
||
--mx-dx-work: #1b6b7c;
|
||
--mx-none: #2c2f4d;
|
||
|
||
--scrollbar-thumb: #383c63;
|
||
--scrollbar-thumb-hover: #4c5182;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(228, 229, 242, 0.05);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 6: Ocean — deep teal, cyan accent --------------------------- */
|
||
[data-theme="dark-teal"] {
|
||
--background: #061c21;
|
||
--foreground: #dcecee;
|
||
--card: #0d2c33;
|
||
--card-foreground: #dcecee;
|
||
--popover: #0d2c33;
|
||
--popover-foreground: #dcecee;
|
||
--primary: #22d3ee; /* cyan */
|
||
--primary-foreground: #041d24;
|
||
--secondary: #143b44;
|
||
--secondary-foreground: #dcecee;
|
||
--muted: #10333b;
|
||
--muted-foreground: #92b4ba;
|
||
--accent: #15424c;
|
||
--accent-foreground: #a5d8e0;
|
||
--destructive: #f2555a;
|
||
--destructive-foreground: #12030a;
|
||
--destructive-muted: #35161a;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #1a4652;
|
||
--input: #1a4652;
|
||
--ring: #5fe3f5;
|
||
|
||
--success: #4ade80;
|
||
--success-foreground: #04220f;
|
||
--success-muted: #0e3020;
|
||
--success-muted-foreground: #86efac;
|
||
--success-border: #17593a;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #221803;
|
||
--warning-muted: #33280f;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #4f3e15;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #221e04;
|
||
--caution-muted: #312d0e;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #4a4315;
|
||
|
||
--danger: #fb7185;
|
||
--danger-foreground: #250912;
|
||
--danger-muted: #38161f;
|
||
--danger-muted-foreground: #fda4af;
|
||
--danger-border: #572634;
|
||
|
||
/* Info moves to a BLUE: a cyan "info" would be the primary colour again. */
|
||
--info: #7aa2f7;
|
||
--info-foreground: #04122e;
|
||
--info-muted: #16233f;
|
||
--info-muted-foreground: #a9c2fb;
|
||
--info-border: #2b3d66;
|
||
|
||
--mx-call-conf: #4ade80;
|
||
--mx-call-work: #2b7a4d;
|
||
--mx-dx-conf: #a78bfa;
|
||
--mx-dx-work: #5b4a9c;
|
||
--mx-none: #1c4550;
|
||
|
||
--scrollbar-thumb: #235a68;
|
||
--scrollbar-thumb-hover: #31788a;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(220, 236, 238, 0.05);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 7: Plum — aubergine, magenta accent ------------------------- */
|
||
[data-theme="dark-plum"] {
|
||
--background: #180f1e;
|
||
--foreground: #eee0f0;
|
||
--card: #251830;
|
||
--card-foreground: #eee0f0;
|
||
--popover: #251830;
|
||
--popover-foreground: #eee0f0;
|
||
--primary: #f472b6; /* magenta-pink */
|
||
--primary-foreground: #250518;
|
||
--secondary: #33203f;
|
||
--secondary-foreground: #eee0f0;
|
||
--muted: #2b1b36;
|
||
--muted-foreground: #b39dbd;
|
||
--accent: #3b2648;
|
||
--accent-foreground: #dcc2e6;
|
||
--destructive: #f2555a;
|
||
--destructive-foreground: #12030a;
|
||
--destructive-muted: #3a1620;
|
||
--destructive-muted-foreground: #fca5a5;
|
||
--border: #3a2547;
|
||
--input: #3a2547;
|
||
--ring: #f9a8d4;
|
||
|
||
--success: #34d399;
|
||
--success-foreground: #052018;
|
||
--success-muted: #10302a;
|
||
--success-muted-foreground: #6ee7b7;
|
||
--success-border: #1a5745;
|
||
|
||
--warning: #fbbf24;
|
||
--warning-foreground: #221803;
|
||
--warning-muted: #352a12;
|
||
--warning-muted-foreground: #fcd34d;
|
||
--warning-border: #524019;
|
||
|
||
--caution: #facc15;
|
||
--caution-foreground: #221e04;
|
||
--caution-muted: #342e12;
|
||
--caution-muted-foreground: #fde047;
|
||
--caution-border: #4e4519;
|
||
|
||
/* Danger goes RED here, not rose: a rose "danger" beside a pink primary is
|
||
indistinguishable, and this is the one colour that must never be missed. */
|
||
--danger: #f43f3f;
|
||
--danger-foreground: #2a0606;
|
||
--danger-muted: #3d1418;
|
||
--danger-muted-foreground: #fca5a5;
|
||
--danger-border: #5c2128;
|
||
|
||
--info: #38bdf8;
|
||
--info-foreground: #051d29;
|
||
--info-muted: #0f2b3a;
|
||
--info-muted-foreground: #7dd3fc;
|
||
--info-border: #16485d;
|
||
|
||
--mx-call-conf: #22c55e;
|
||
--mx-call-work: #2f7d55;
|
||
--mx-dx-conf: #60a5fa;
|
||
--mx-dx-work: #35538c;
|
||
--mx-none: #3a2647;
|
||
|
||
--scrollbar-thumb: #4a3159;
|
||
--scrollbar-thumb-hover: #644176;
|
||
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(238, 224, 240, 0.05);
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* ---- Theme 8: Nordic light — crisp cool white, indigo accent ----------- */
|
||
[data-theme="light-nordic"] {
|
||
--background: #eef1f7;
|
||
--foreground: #1b2130;
|
||
--card: #ffffff;
|
||
--card-foreground: #1b2130;
|
||
--popover: #ffffff;
|
||
--popover-foreground: #1b2130;
|
||
--primary: #4f46e5; /* indigo */
|
||
--primary-foreground: #ffffff;
|
||
--secondary: #dfe4ee;
|
||
--secondary-foreground: #1b2130;
|
||
--muted: #e2e7f0;
|
||
--muted-foreground: #4a5468;
|
||
--accent: #dcdcfb;
|
||
--accent-foreground: #312a91;
|
||
--destructive: #b91c1c;
|
||
--destructive-foreground: #ffffff;
|
||
--destructive-muted: #fef2f2;
|
||
--destructive-muted-foreground: #b91c1c;
|
||
--border: #c9d1e0;
|
||
--input: #c9d1e0;
|
||
--ring: #6366f1;
|
||
|
||
--success: #15803d;
|
||
--success-foreground: #ffffff;
|
||
--success-muted: #dcfce7;
|
||
--success-muted-foreground: #14532d;
|
||
--success-border: #86efac;
|
||
|
||
--warning: #b45309;
|
||
--warning-foreground: #ffffff;
|
||
--warning-muted: #fef3c7;
|
||
--warning-muted-foreground: #92400e;
|
||
--warning-border: #fcd34d;
|
||
|
||
--caution: #a16207;
|
||
--caution-foreground: #ffffff;
|
||
--caution-muted: #fef9c3;
|
||
--caution-muted-foreground: #854d0e;
|
||
--caution-border: #fde047;
|
||
|
||
--danger: #dc2626;
|
||
--danger-foreground: #ffffff;
|
||
--danger-muted: #fee2e2;
|
||
--danger-muted-foreground: #991b1b;
|
||
--danger-border: #fca5a5;
|
||
|
||
--info: #0369a1;
|
||
--info-foreground: #ffffff;
|
||
--info-muted: #e0f2fe;
|
||
--info-muted-foreground: #075985;
|
||
--info-border: #7dd3fc;
|
||
|
||
/* Entity ramp shifts to TEAL: indigo is the primary here. */
|
||
--mx-call-conf: #15803d;
|
||
--mx-call-work: #86efac;
|
||
--mx-dx-conf: #0f766e;
|
||
--mx-dx-work: #99f6e4;
|
||
--mx-none: #dde3ec;
|
||
|
||
--scrollbar-thumb: #b6c0d2;
|
||
--scrollbar-thumb-hover: #93a0b8;
|
||
--card-shadow: 0 1px 2px rgba(27, 33, 48, 0.06), 0 0 0 1px rgba(27, 33, 48, 0.03);
|
||
color-scheme: light;
|
||
}
|
||
|
||
/* ── Data-viz palette (Statistics dashboard) ────────────────────────────────
|
||
A VALIDATED categorical palette, not hand-picked: the slot ORDER is what makes
|
||
it colour-blind-safe (worst adjacent ΔE 24.2 light / 10.3 dark), so never
|
||
reorder, recycle, or generate a 9th hue — fold the tail into "Other" instead.
|
||
The dark column is the same eight hues re-stepped for a dark surface, NOT an
|
||
automatic inversion.
|
||
|
||
--chart-1 is the DEFAULT single-series hue: a chart with one series uses it for
|
||
every bar. Colour is for identity, never to re-encode a length the bar already
|
||
shows.
|
||
|
||
--chart-seq-* is a ONE-HUE ramp for magnitude (the hour×band heatmap). On light
|
||
themes it runs light→dark; on dark themes dark→light, so "near zero" always
|
||
recedes toward the surface instead of glowing. */
|
||
:root,
|
||
[data-theme="light-warm"],
|
||
[data-theme="light-cool"],
|
||
[data-theme="light-sage"],
|
||
[data-theme="light-nordic"] {
|
||
--chart-1: #2a78d6; /* blue — default single-series hue */
|
||
--chart-2: #1baf7a; /* aqua */
|
||
--chart-3: #eda100; /* yellow */
|
||
--chart-4: #008300; /* green */
|
||
--chart-5: #4a3aa7; /* violet */
|
||
--chart-6: #e34948; /* red */
|
||
--chart-7: #e87ba4; /* magenta */
|
||
--chart-8: #eb6834; /* orange */
|
||
|
||
--chart-seq-1: #cde2fb;
|
||
--chart-seq-2: #9ec5f4;
|
||
--chart-seq-3: #6da7ec;
|
||
--chart-seq-4: #3987e5;
|
||
--chart-seq-5: #256abf;
|
||
--chart-seq-6: #104281;
|
||
}
|
||
|
||
[data-theme="dim-slate"],
|
||
[data-theme="dark-warm"],
|
||
[data-theme="dark-graphite"],
|
||
[data-theme="high-contrast"],
|
||
[data-theme="dark-indigo"],
|
||
[data-theme="dark-teal"],
|
||
[data-theme="dark-plum"] {
|
||
--chart-1: #3987e5;
|
||
--chart-2: #199e70;
|
||
--chart-3: #c98500;
|
||
--chart-4: #008300;
|
||
--chart-5: #9085e9;
|
||
--chart-6: #e66767;
|
||
--chart-7: #d55181;
|
||
--chart-8: #d95926;
|
||
|
||
/* Reversed: the lowest step must sink toward the dark surface. */
|
||
--chart-seq-1: #0d366b;
|
||
--chart-seq-2: #184f95;
|
||
--chart-seq-3: #256abf;
|
||
--chart-seq-4: #3987e5;
|
||
--chart-seq-5: #6da7ec;
|
||
--chart-seq-6: #9ec5f4;
|
||
}
|
||
|
||
/* Map Tailwind's color utilities onto the semantic vars above. `inline` makes
|
||
the generated utilities reference var(--…) directly, so overriding a var in
|
||
a [data-theme] block re-skins every utility at runtime. */
|
||
@theme inline {
|
||
--color-background: var(--background);
|
||
--color-foreground: var(--foreground);
|
||
--color-card: var(--card);
|
||
--color-card-foreground: var(--card-foreground);
|
||
--color-popover: var(--popover);
|
||
--color-popover-foreground: var(--popover-foreground);
|
||
--color-primary: var(--primary);
|
||
--color-primary-foreground: var(--primary-foreground);
|
||
--color-secondary: var(--secondary);
|
||
--color-secondary-foreground: var(--secondary-foreground);
|
||
--color-muted: var(--muted);
|
||
--color-muted-foreground: var(--muted-foreground);
|
||
--color-accent: var(--accent);
|
||
--color-accent-foreground: var(--accent-foreground);
|
||
--color-destructive: var(--destructive);
|
||
--color-destructive-foreground: var(--destructive-foreground);
|
||
--color-destructive-muted: var(--destructive-muted);
|
||
--color-destructive-muted-foreground: var(--destructive-muted-foreground);
|
||
--color-border: var(--border);
|
||
--color-input: var(--input);
|
||
--color-ring: var(--ring);
|
||
|
||
--color-success: var(--success);
|
||
--color-success-foreground: var(--success-foreground);
|
||
--color-success-muted: var(--success-muted);
|
||
--color-success-muted-foreground: var(--success-muted-foreground);
|
||
--color-success-border: var(--success-border);
|
||
|
||
--color-warning: var(--warning);
|
||
--color-warning-foreground: var(--warning-foreground);
|
||
--color-warning-muted: var(--warning-muted);
|
||
--color-warning-muted-foreground: var(--warning-muted-foreground);
|
||
--color-warning-border: var(--warning-border);
|
||
|
||
--color-caution: var(--caution);
|
||
--color-caution-foreground: var(--caution-foreground);
|
||
--color-caution-muted: var(--caution-muted);
|
||
--color-caution-muted-foreground: var(--caution-muted-foreground);
|
||
--color-caution-border: var(--caution-border);
|
||
|
||
--color-danger: var(--danger);
|
||
--color-danger-foreground: var(--danger-foreground);
|
||
--color-danger-muted: var(--danger-muted);
|
||
--color-danger-muted-foreground: var(--danger-muted-foreground);
|
||
--color-danger-border: var(--danger-border);
|
||
|
||
--color-info: var(--info);
|
||
--color-info-foreground: var(--info-foreground);
|
||
--color-info-muted: var(--info-muted);
|
||
--color-info-muted-foreground: var(--info-muted-foreground);
|
||
--color-info-border: var(--info-border);
|
||
|
||
--color-mx-call-conf: var(--mx-call-conf);
|
||
--color-mx-call-work: var(--mx-call-work);
|
||
--color-mx-dx-conf: var(--mx-dx-conf);
|
||
--color-mx-dx-work: var(--mx-dx-work);
|
||
--color-mx-none: var(--mx-none);
|
||
|
||
--radius: 0.5rem;
|
||
|
||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
--font-mono: 'JetBrains Mono', 'Cascadia Mono', Consolas, 'Courier New', monospace;
|
||
}
|
||
|
||
@layer base {
|
||
* { @apply border-border; }
|
||
html, body, #root { height: 100%; }
|
||
/* Root rem base. Tailwind sizing (text/heights/padding/gaps) is in rem, so
|
||
this is the single global density knob — shrinks the whole UI uniformly
|
||
while Leaflet maps (px-based) stay correct. Default browser base is 16px. */
|
||
html { font-size: 13px; }
|
||
body {
|
||
@apply bg-background text-foreground;
|
||
font-family: var(--font-sans);
|
||
font-size: 0.95rem;
|
||
line-height: 1.4;
|
||
-webkit-font-smoothing: antialiased;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
}
|
||
|
||
/* Subtle elevation on every Card-styled surface so cards visibly sit on
|
||
top of the background — paper-on-paper feel, tuned per theme. */
|
||
.bg-card {
|
||
box-shadow: var(--card-shadow);
|
||
}
|
||
|
||
/* Scrollbar tuned to each theme's surface */
|
||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb {
|
||
background: var(--scrollbar-thumb);
|
||
border-radius: 5px;
|
||
border: 2px solid var(--background);
|
||
}
|
||
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
|
||
|
||
/* ── Frameless window: drag region ──────────────────────────────────────────
|
||
The OS title bar is gone (main.go, Frameless), so the app header IS the title
|
||
bar and must be draggable. Everything interactive inside it opts OUT: a drag
|
||
region swallows clicks, and without this the menus and toolbar buttons would
|
||
move the window instead of doing their job. Opting out by ROLE rather than
|
||
listing each child keeps a future button working without anyone remembering
|
||
this rule. */
|
||
.app-dragregion {
|
||
--wails-draggable: drag;
|
||
}
|
||
.app-dragregion button,
|
||
.app-dragregion a,
|
||
.app-dragregion input,
|
||
.app-dragregion select,
|
||
.app-dragregion textarea,
|
||
.app-dragregion nav,
|
||
.app-dragregion [role='button'],
|
||
.app-dragregion [role='menu'],
|
||
.app-dragregion [data-no-drag] {
|
||
--wails-draggable: no-drag;
|
||
}
|
||
|
||
/* Native date inputs: the WebView draws its calendar glyph in near-black, which
|
||
disappears on the dark themes. Invert it there — the control itself is worth
|
||
keeping (OS calendar, locale date order, keyboard entry), only its icon needs
|
||
help. */
|
||
Every temporal input carries the same glyph, so the rule names them all:
|
||
datetime-local was left out at first and its icon stayed invisible. */
|
||
:is([data-theme='dim-slate'], [data-theme='dark-warm'], [data-theme='dark-graphite'], [data-theme='high-contrast'],
|
||
[data-theme='dark-indigo'], [data-theme='dark-teal'], [data-theme='dark-plum'])
|
||
:is(input[type='date'], input[type='datetime-local'], input[type='time'], input[type='month'], input[type='week'])::-webkit-calendar-picker-indicator {
|
||
filter: invert(1) brightness(1.8);
|
||
opacity: 0.9;
|
||
}
|