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}