1/*
  2 * Design tokens.
  3 *
  4 * Derived from Basecoat UI's defaults — see ATTRIBUTIONS.md. Scoped to
  5 * `.plain-admin` so admin styles don't leak onto the user's own pages.
  6 *
  7 * The base palette is shadcn-style Basecoat values with Plain's olive
  8 * primary on top. Plain semantic slots (`--success`, `--warning`,
  9 * `--danger`, `--info`, `--link`) are real values, not aliases, with
 10 * earthy hues tuned to harmonize with the olive. `--danger` replaces
 11 * Basecoat's `--destructive` — same role, single name.
 12 *
 13 * Customizing: re-declare any token in your own stylesheet loaded
 14 * after this one to retheme the admin without forking templates.
 15 */
 16
 17@custom-variant dark (&:is(.dark *));
 18
 19
 20/* -------------------------------------------------------------------------
 21 * Light mode tokens (Basecoat defaults)
 22 * ------------------------------------------------------------------------- */
 23.plain-admin {
 24  /* Master radius. Bumping this shifts every per-component radius
 25     below proportionally — the shadcn-style single-knob retune. For
 26     surgical changes, override the individual `--radius-*` token for
 27     just that component. */
 28  --radius: 0.5rem;
 29
 30  /* Per-component radii. Each component reads its own variable, so
 31     `--radius-card: 4px` retunes only cards (no ripple to dialogs,
 32     alerts, etc.). Defaults derive from `--radius` so the master
 33     knob still works. */
 34  --radius-card: var(--radius);
 35  --radius-button: var(--radius);
 36  --radius-input: var(--radius);
 37  --radius-select: var(--radius);
 38  --radius-textarea: var(--radius);
 39  --radius-dialog: calc(var(--radius) + 2px);
 40  --radius-alert: var(--radius);
 41  --radius-popover: calc(var(--radius) - 2px);
 42  --radius-tooltip: calc(var(--radius) - 2px);
 43  --radius-segmented: var(--radius);
 44  --radius-segmented-item: calc(var(--radius) - 2px);
 45  --radius-dropdown-item: calc(var(--radius) - 4px);
 46  --radius-kbd: calc(var(--radius) - 4px);
 47  --radius-checkbox: calc(var(--radius) - 4px);
 48  --radius-tabs-focus: calc(var(--radius) - 4px);
 49
 50  /* Fonts. Tailwind v4 wires the `font-sans` / `font-mono` utilities to
 51     these names automatically — overriding either retheme the admin's
 52     typography without touching templates. Inter and JetBrains Mono are
 53     vendored under `assets/admin/fonts/` and declared in
 54     `templates/admin/base.html`; the system stacks remain as fallbacks. */
 55  --font-sans:
 56    "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
 57    "Helvetica Neue", Arial, sans-serif;
 58  --font-mono:
 59    "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
 60    "Liberation Mono", monospace;
 61
 62  --background: oklch(1 0 0);
 63  --foreground: oklch(0.145 0 0);
 64  --card: oklch(1 0 0);
 65  --card-foreground: oklch(0.145 0 0);
 66  --popover: oklch(1 0 0);
 67  --popover-foreground: oklch(0.145 0 0);
 68  --primary: #4d5246;
 69  --primary-foreground: oklch(0.985 0 0);
 70  --secondary: oklch(0.97 0.004 80);
 71  --secondary-foreground: oklch(0.205 0 0);
 72  --muted: oklch(0.97 0.004 80);
 73  --muted-foreground: oklch(0.556 0 0);
 74  --accent: oklch(0.97 0.004 80);
 75  --accent-foreground: oklch(0.205 0 0);
 76  --danger: oklch(0.55 0.14 35);
 77  --danger-foreground: oklch(0.985 0 0);
 78  --border: oklch(0.922 0 0);
 79  --input: oklch(0.922 0 0);
 80  --ring: oklch(0.708 0 0);
 81
 82  /* Chart palette: earthy, brand-coordinated hues (Sage / Steel /
 83     Terracotta / Teal / Plum). Chroma kept modest (0.08–0.13) so the
 84     series feel like cousins of the olive primary, not a vibrant
 85     rainbow. Lightnesses cluster around 0.5–0.6 so no series visually
 86     outweighs another in stacked / grouped charts. */
 87  --chart-1: oklch(0.55 0.09 140);     /* Sage */
 88  --chart-2: oklch(0.55 0.08 225);     /* Steel */
 89  --chart-3: oklch(0.58 0.13 35);      /* Terracotta */
 90  --chart-4: oklch(0.6 0.11 195);      /* Teal */
 91  --chart-5: oklch(0.5 0.11 340);      /* Plum */
 92
 93  --scrollbar-track: transparent;
 94  --scrollbar-thumb: rgba(0, 0, 0, 0.3);
 95  --scrollbar-width: 6px;
 96  --scrollbar-radius: 6px;
 97  /* Chevron used as background-image on <select>, so the stroke color
 98     is the actual rendered color and must be redeclared in dark mode.
 99     Check is used as a CSS mask on checkboxes — mask uses the alpha
100     channel only, so a single declaration suffices for both modes. */
101  --chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>');
102  --chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>');
103  --check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>');
104
105  /* Plain-only semantic slots. Earthy, brand-coordinated hues — sage
106     green / warm amber / steel blue — desaturated enough to read
107     alongside the olive primary without fighting it. Used directly
108     (`text-success`) and at 10% opacity for badge/alert backgrounds.
109     `--danger` is declared above with the rest of the Basecoat-style
110     tokens since it underpins both status (badge) and emphasis
111     (button, alert) variants. */
112  --success: oklch(0.55 0.13 145);
113  --success-foreground: oklch(0.985 0 0);
114  --warning: oklch(0.72 0.14 80);
115  --warning-foreground: oklch(0.205 0 0);
116  --info: oklch(0.55 0.13 230);
117  --info-foreground: oklch(0.985 0 0);
118  --link: oklch(0.5 0.13 230);
119  --link-hover: oklch(0.4 0.13 230);
120
121  /* Prose tokens — surfaces that prose content uses, broken out from
122     the chrome tokens so they can be themed independently. (E.g.
123     making code blocks distinctive without affecting `--muted`-driven
124     hover surfaces.) */
125  --code-bg: var(--muted);
126
127  /* Header surface. Plain ships a warm off-white chrome bar in light
128     mode; override this token to match your own brand. (For full
129     contrast inversion — light page, dark header — add `class="dark"`
130     to the `<header>` element instead, which flips every token via
131     the dark variant.) */
132  --header-bg: #f6f3f2;
133}
134
135
136/* -------------------------------------------------------------------------
137 * Dark mode tokens (Basecoat defaults)
138 *
139 * Two selectors so this works whether `.dark` and `.plain-admin` are on
140 * the same element (current default — both on <html>) or `.plain-admin`
141 * is on a descendant of `.dark`.
142 * ------------------------------------------------------------------------- */
143.dark.plain-admin,
144.dark .plain-admin {
145  --background: oklch(0.145 0 0);
146  --foreground: oklch(0.985 0 0);
147  --card: oklch(0.205 0 0);
148  --card-foreground: oklch(0.985 0 0);
149  --popover: oklch(0.269 0 0);
150  --popover-foreground: oklch(0.985 0 0);
151  --primary: oklch(0.92 0.02 130);
152  --primary-foreground: oklch(0.205 0 0);
153  --secondary: oklch(0.269 0 0);
154  --secondary-foreground: oklch(0.985 0 0);
155  --muted: oklch(0.269 0 0);
156  --muted-foreground: oklch(0.708 0 0);
157  --accent: oklch(0.371 0 0);
158  --accent-foreground: oklch(0.985 0 0);
159  --danger: oklch(0.74 0.14 35);
160  --danger-foreground: oklch(0.205 0 0);
161  --border: oklch(1 0 0 / 10%);
162  --input: oklch(1 0 0 / 15%);
163  --ring: oklch(0.556 0 0);
164
165  /* Chart palette: same hues as light mode, lightness raised so the
166     series read on the dark page bg. Chroma unchanged. */
167  --chart-1: oklch(0.7 0.09 140);      /* Sage */
168  --chart-2: oklch(0.7 0.08 225);      /* Steel */
169  --chart-3: oklch(0.72 0.13 35);      /* Terracotta */
170  --chart-4: oklch(0.72 0.11 195);     /* Teal */
171  --chart-5: oklch(0.65 0.11 340);     /* Plum */
172
173  --header-bg: oklch(0.18 0.004 50);
174
175  /* Plain-only semantic slots — same hues as light mode, lighter
176     lightness for contrast on dark surfaces. The status backgrounds
177     are now light enough that `*-foreground` flips to a dark text
178     color (the inverse of light mode). */
179  --success: oklch(0.78 0.13 145);
180  --success-foreground: oklch(0.205 0 0);
181  --warning: oklch(0.8 0.14 80);
182  --warning-foreground: oklch(0.205 0 0);
183  --info: oklch(0.78 0.13 230);
184  --info-foreground: oklch(0.205 0 0);
185  --link: oklch(0.78 0.13 230);
186  --link-hover: oklch(0.85 0.13 230);
187
188  --scrollbar-thumb: rgba(255, 255, 255, 0.3);
189  --chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>');
190  --chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>');
191  --check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>');
192  color-scheme: dark;
193}
194
195
196/* -------------------------------------------------------------------------
197 * Tailwind theme bindings
198 *
199 * Expose every `--token` above as a Tailwind v4 utility class. The names
200 * here are what `bg-X`, `text-X`, `border-X` etc. resolve to.
201 * ------------------------------------------------------------------------- */
202@theme {
203  /* Generic rounded-admin-{sm,md,lg} utilities for ad-hoc template use
204     (menu items, preflight rows, chart legend swatches, etc.).
205     Component CSS no longer references these — each component owns its
206     own `--radius-*` token in `.plain-admin` above. */
207  --radius-admin-sm: calc(var(--radius) - 4px);
208  --radius-admin-md: calc(var(--radius) - 2px);
209  --radius-admin-lg: var(--radius);
210
211  --color-admin-background: var(--background);
212  --color-admin-foreground: var(--foreground);
213  --color-admin-card: var(--card);
214  --color-admin-card-foreground: var(--card-foreground);
215  --color-admin-popover: var(--popover);
216  --color-admin-popover-foreground: var(--popover-foreground);
217  --color-admin-primary: var(--primary);
218  --color-admin-primary-foreground: var(--primary-foreground);
219  --color-admin-secondary: var(--secondary);
220  --color-admin-secondary-foreground: var(--secondary-foreground);
221  --color-admin-muted: var(--muted);
222  --color-admin-muted-foreground: var(--muted-foreground);
223  --color-admin-accent: var(--accent);
224  --color-admin-accent-foreground: var(--accent-foreground);
225  --color-admin-border: var(--border);
226  --color-admin-input: var(--input);
227  --color-admin-ring: var(--ring);
228  --color-admin-chart-1: var(--chart-1);
229  --color-admin-chart-2: var(--chart-2);
230  --color-admin-chart-3: var(--chart-3);
231  --color-admin-chart-4: var(--chart-4);
232  --color-admin-chart-5: var(--chart-5);
233
234  /* Plain semantic Tailwind utilities so `text-success` / `bg-warning/10`
235     / `border-danger` / `text-link` resolve to the corresponding tokens
236     above. `--danger` doubles as the Basecoat-style emphasis color used
237     by `btn-danger` / `alert-danger`. The `*-foreground` pairings carry
238     the legible text color for solid status backgrounds (buttons). */
239  --color-admin-success: var(--success);
240  --color-admin-success-foreground: var(--success-foreground);
241  --color-admin-warning: var(--warning);
242  --color-admin-warning-foreground: var(--warning-foreground);
243  --color-admin-danger: var(--danger);
244  --color-admin-danger-foreground: var(--danger-foreground);
245  --color-admin-info: var(--info);
246  --color-admin-info-foreground: var(--info-foreground);
247  --color-admin-link: var(--link);
248  --color-admin-link-hover: var(--link-hover);
249  --color-admin-code-bg: var(--code-bg);
250  --color-admin-header-bg: var(--header-bg);
251}