diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index 3cb896f..a3c6ae2 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -1,232 +1,573 @@
@import "tailwindcss";
/* ============================================================
- Theme tokens
-
- Two palettes, one set of names. Nothing below this block should name a
- colour directly: a literal in a rule is a colour that cannot follow the
- theme, and it will be the one that glows white in a dark room.
-
- `[data-theme]` is written onto by an inline script before first paint
- (see `ThemeScript`), always resolved to `light` or `dark` — never `system`.
- Resolving in the script rather than in CSS is what lets the dark palette be
- written once here instead of duplicated into a `prefers-color-scheme` block,
- and the app is React-only, so there is no meaningful no-JS case to keep the
- duplicate for.
-
- Tints and borders are mixed from their base colour and the current surface,
- so they follow both the accent and the theme without a second table to keep
- in sync. Anything that has to stay dark in both themes -- the titlebar, the
- terminal -- is named for the surface it sits on rather than the theme.
+ Redrob design tokens
+
+ Three bands, and a colour is decided in exactly one of them.
+
+ 1. PRIMITIVES `--rr-*`. The brand's confirmed HEX values, transcribed verbatim from
+ Redrob Console's `apps/web/src/app/globals.css`, which is the source of truth for
+ every Redrob product. They carry no meaning and no theme: Blue 6 is Blue 6 in
+ light and in dark, so none of them is repeated in the dark block. No rule below
+ the token layer may name this band.
+
+ 2. SEMANTIC ROLES bare role names, re-pointed per theme. Console's names at
+ Console's values, so `--card` is the same colour in Eval as it is in Console and a
+ role learned in one product is the same role in the other. A handful of roles
+ Console does not name are declared here too, each commented where it is declared,
+ and each built from primitives only. Every value in this band is a `var(--rr-*)`
+ or a mix of two; a literal here would be a second place a colour is decided.
+
+ 3. LOCAL NAMES this app's existing vocabulary, as aliases onto band 2. It has no
+ values of its own. It exists because 4,300 lines of rules below already name
+ `--panel`, `--line` and `--brand`, and rebinding those names costs one line each
+ where rewriting the call sites costs three hundred. New rules should name band 2.
+
+ `[data-theme]` is written onto by an inline script before first paint (see
+ `ThemeScript`), always resolved to `light` or `dark`, never `system`. Resolving in the
+ script rather than in CSS is what lets the dark palette be written once here instead of
+ duplicated into a `prefers-color-scheme` block.
+
+ ── What this port deliberately does not take ─────────────────────────────────────────
+
+ The type size, leading and tracking scale. Its steps differ from the ones in use at
+ every level, so adopting it re-flows every screen at once, which wants reviewing
+ against the live layouts rather than arriving with a colour change. This is the same
+ decision Console and the Office port made, and it is load bearing: the sizes in the
+ rules below are unchanged on purpose, and a later change that takes the scale has to
+ take it everywhere at once. Radius and spacing are likewise unchanged; both are already
+ on a 4px grid.
+
+ ── Where a name could not be shared ─────────────────────────────────────────────────
+
+ Console's `--muted` is a surface (Gray 2) and this app's `--muted` was its secondary
+ text colour, so the two could not both hold the name. The text role is renamed to
+ Console's `--muted-foreground`, which is what it always was, and `--muted` now means
+ what it means in Console.
============================================================ */
+/* ── 1. PRIMITIVES ───────────────────────────────────────────
+ Theme-independent. Declared once, on :root, and not repeated in `[data-theme='dark']`:
+ only the semantic aliases below are re-pointed there. */
+:root {
+ /* Primary */
+ --rr-blue: #2b52ff; /* Redrob Blue, the same value as Blue 6 */
+ --rr-black: #0a0b0c; /* Redrob Black */
+ --rr-white: #ffffff; /* Redrob White */
+
+ /* Blue tint, 1 lightest to 10 darkest */
+ --rr-blue-1: #eff4ff;
+ --rr-blue-2: #d9e6ff;
+ --rr-blue-3: #bad2ff;
+ --rr-blue-4: #8aafff;
+ --rr-blue-5: #507fff;
+ --rr-blue-6: #2b52ff;
+ --rr-blue-7: #1733d5;
+ --rr-blue-8: #09209c;
+ --rr-blue-9: #061460;
+ --rr-blue-10: #030c34;
+
+ /* Grayscale, 1 lightest to 9 darkest */
+ --rr-gray-1: #f8f9fb;
+ --rr-gray-2: #eff1f4;
+ --rr-gray-3: #dfe2e8;
+ --rr-gray-4: #cbcfd7;
+ --rr-gray-5: #aab0bb;
+ --rr-gray-6: #7c8390;
+ --rr-gray-7: #576071;
+ --rr-gray-8: #292e37;
+ --rr-gray-9: #141719;
+
+ /* Primary gradient */
+ --rr-gradient-primary: linear-gradient(135deg, #f8f9fb 0%, #507fff 100%);
+
+ /* Accent spectrum, 9 hues x 5 levels. The brand document scopes it to contents,
+ marketing and graphics: category distinction and point colour, not product chrome.
+ 1 lightest to 5 darkest. */
+ --rr-teal-1: #dcfffe;
+ --rr-teal-2: #b9fffd;
+ --rr-teal-3: #6ff4f0;
+ --rr-teal-4: #00b5c2;
+ --rr-teal-5: #006a7a;
+ --rr-sky-1: #e4f0ff;
+ --rr-sky-2: #bad9ff;
+ --rr-sky-3: #2f8dff;
+ --rr-sky-4: #0e51b6;
+ --rr-sky-5: #002a68;
+ --rr-violet-1: #f2e9ff;
+ --rr-violet-2: #d4b3ff;
+ --rr-violet-3: #8944ff;
+ --rr-violet-4: #4500ac;
+ --rr-violet-5: #140042;
+ --rr-pink-1: #ffe3fc;
+ --rr-pink-2: #ffb2f6;
+ --rr-pink-3: #ff39ba;
+ --rr-pink-4: #8a0061;
+ --rr-pink-5: #380037;
+ --rr-red-1: #ffe8e1;
+ --rr-red-2: #ffc2ba;
+ --rr-red-3: #ff5452;
+ --rr-red-4: #a31310;
+ --rr-red-5: #560100;
+ --rr-orange-1: #ffedda;
+ --rr-orange-2: #ffd5ab;
+ --rr-orange-3: #ff9c1b;
+ --rr-orange-4: #ae5100;
+ --rr-orange-5: #5c2d00;
+ --rr-yellow-1: #fff7cc;
+ --rr-yellow-2: #ffed94;
+ --rr-yellow-3: #ffda1e;
+ --rr-yellow-4: #d2a100;
+ --rr-yellow-5: #734f00;
+ --rr-lime-1: #f2ffc3;
+ --rr-lime-2: #e5ff81;
+ --rr-lime-3: #cffd21;
+ --rr-lime-4: #89ad00;
+ --rr-lime-5: #2f5f00;
+ --rr-green-1: #d6ffe1;
+ --rr-green-2: #a6ffbf;
+ --rr-green-3: #29e474;
+ --rr-green-4: #00864a;
+ --rr-green-5: #004829;
+}
+
+/* ── 2. SEMANTIC ROLES, light ────────────────────────────────
+ Light is the default palette; dark is applied by `[data-theme='dark']` on .
+ Console's reasoning for each value, including its four departures from the brand
+ proposal, is written out in Console's own header and is not restated here so there is
+ one place it is written. */
:root {
color-scheme: light;
+ /* Page and panels. Gray 1 as the page and White as the surface is fixed by the brand
+ document, and it inverts what this app used to do (grey page, white panels was close,
+ but the page was Tailwind grey and the panel had no step of its own): a card now sits
+ above the page rather than beside it. */
+ --background: var(--rr-gray-1);
+ --background-secondary: var(--rr-gray-2);
+ --card: var(--rr-white);
+ --card-foreground: var(--rr-gray-9);
+ --popover: var(--rr-white);
+ --popover-foreground: var(--rr-gray-9);
+
+ /* Text. Four steps, and only the first two are for body copy: Gray 6 on Gray 1 is
+ 3.62:1, which clears 3:1 for large text, icons and non-text UI and does not clear
+ 4.5:1 for a sentence. */
+ --foreground: var(--rr-gray-9);
+ --muted-foreground: var(--rr-gray-7);
+ --subtle-foreground: var(--rr-gray-6);
+ /* Disabled text is exempt from the contrast minimum, which is the only reason Gray 5
+ is here. */
+ --disabled-foreground: var(--rr-gray-5);
+
+ /* Interactive surfaces. `accent` is the role a hover or an open menu item paints on the
+ page; `accent-active` is the pressed step, and it is also what a hover on a card takes
+ here, because on dark `accent` and `card` are the same mix. The brand accent colour is
+ `primary`. */
+ --accent: var(--rr-gray-2);
+ --accent-foreground: var(--rr-gray-9);
+ --accent-active: var(--rr-gray-3);
+ --secondary: var(--rr-gray-2);
+ --secondary-foreground: var(--rr-gray-9);
+ --muted: var(--rr-gray-2);
+
+ /* Brand. Blue 6 with White reads 5.6:1, so a primary button carries body-size text. */
+ --primary: var(--rr-blue-6);
+ --primary-foreground: var(--rr-white);
+ --primary-hover: var(--rr-blue-7);
+ --primary-muted: var(--rr-blue-3);
+ --primary-soft: var(--rr-blue-1);
+ /* Brand-coloured text, a link or an eyebrow label, rather than a fill. Light needs no
+ step away from the accent; dark does, which is why the role exists. */
+ --primary-ink: var(--rr-blue-6);
+
+ --border-subtle: var(--rr-gray-2);
+ --border: var(--rr-gray-3);
+ --border-strong: var(--rr-gray-4);
+ /* The boundary of a control. WCAG 1.4.11 asks 3:1 of it, and every step up to Gray 5 is
+ under 3:1 on a White surface, so control boundaries take Gray 6 (3.8:1 on the surface,
+ 3.6:1 on the page) and `--border-strong` keeps Gray 4 for decorative emphasis. */
+ --input: var(--rr-gray-6);
+ /* A control boundary under the cursor. Console has no hover step for `--input`; a ghost
+ button here does, and it has to read as stronger than the resting edge, so it takes
+ one step further from the surface in each theme: Gray 7 on light, Gray 5 on dark. */
+ --input-hover: var(--rr-gray-7);
+
+ /* The focus ring is the brand blue rather than a grey: on-brand, and the stronger
+ indicator at 5.6:1 against the page. Drawn at full opacity. */
+ --ring: var(--rr-blue-6);
+
+ /* Status. The brand document defines no product status colours, so these come from the
+ accent spectrum: level 4 on light, level 3 on dark. Four roles each, because a status
+ appears as all four and one value cannot be all four: the solid fill, the ink on that
+ fill, the tinted strip behind a message, and the readable text on page or strip. */
+ --success: var(--rr-green-4);
+ --success-foreground: var(--rr-white);
+ --success-soft: var(--rr-green-1);
+ --success-ink: var(--rr-green-5);
+ --warning: var(--rr-orange-4);
+ --warning-foreground: var(--rr-white);
+ --warning-soft: var(--rr-orange-1);
+ --warning-ink: var(--rr-orange-4);
+ --destructive: var(--rr-red-4);
+ --destructive-foreground: var(--rr-white);
+ --destructive-soft: var(--rr-red-1);
+ --destructive-ink: var(--rr-red-4);
+ /* The edge of a status strip. Console draws its strips without one, so it names no step
+ for it; a banner and a chip here are outlined. Level 2 on light and level 4 on dark,
+ one step in from the `-soft` fill, so the edge is a step of the same hue rather than
+ an invented alpha of the fill. */
+ --success-line: var(--rr-green-2);
+ --warning-line: var(--rr-orange-2);
+ --destructive-line: var(--rr-red-2);
+
+ --overlay: color-mix(in srgb, var(--rr-black) 45%, transparent);
+
+ /* A tooltip stays dark in both palettes, so it reads as an overlay rather than as
+ another panel. */
+ --tooltip: var(--rr-gray-9);
+ --tooltip-foreground: var(--rr-gray-1);
+
+ /* The sidebar is a surface, like a card, separated from the page by Gray 1 behind it. */
+ --sidebar: var(--rr-white);
+ --sidebar-foreground: var(--rr-gray-9);
+ --sidebar-primary: var(--rr-blue-6);
+ --sidebar-primary-foreground: var(--rr-white);
+ --sidebar-accent: var(--rr-gray-2);
+ --sidebar-accent-foreground: var(--rr-gray-9);
+ --sidebar-border: var(--rr-gray-3);
+ --sidebar-ring: var(--rr-blue-6);
+
+ /* Graphics. The spectrum's own use: category distinction and point colour in charts and
+ illustration, never product chrome. Held to 3:1 against the surface they are drawn on,
+ because a chart series is needed to understand the chart. Level 4 on light, except
+ teal, yellow and lime, whose mid steps cannot read on white; level 3 on dark. */
+ --spectrum-teal: var(--rr-teal-5);
+ --spectrum-sky: var(--rr-sky-4);
+ --spectrum-violet: var(--rr-violet-4);
+ --spectrum-pink: var(--rr-pink-4);
+ --spectrum-red: var(--rr-red-4);
+ --spectrum-orange: var(--rr-orange-4);
+ --spectrum-yellow: var(--rr-yellow-5);
+ --spectrum-lime: var(--rr-lime-5);
+ --spectrum-green: var(--rr-green-4);
+
+ /* Chart series, drawn from the spectrum and ordered so neighbouring hues are not
+ neighbouring indices. */
+ --chart-1: var(--spectrum-sky);
+ --chart-2: var(--spectrum-pink);
+ --chart-3: var(--spectrum-lime);
+ --chart-4: var(--spectrum-orange);
+ --chart-5: var(--spectrum-violet);
+ --chart-6: var(--spectrum-teal);
+ --chart-7: var(--spectrum-yellow);
+ --chart-8: var(--spectrum-green);
+ /* Tied to the ink so it darkens the panel on light and lightens it on dark, rather than
+ being one fixed near-white that only worked on one. Console paints the band under a
+ chart cursor with it; this app draws its gridlines with it, which is the same job. */
+ --chart-cursor: color-mix(in srgb, var(--rr-gray-8) 8%, transparent);
+
+ /* Syntax colours, picked against the code surface rather than the page: a code block
+ sits on `--muted`, Gray 2 on light and Gray 8 on dark, which is the tighter of the
+ two constraints. Every entry clears 4.5:1 there. */
+ --code-comment: var(--rr-gray-7);
+ --code-string: var(--rr-green-5);
+ --code-number: var(--rr-orange-4);
+ --code-keyword: var(--rr-violet-4);
+ --code-literal: var(--rr-pink-4);
+ --code-property: var(--rr-sky-4);
+ --code-variable: var(--rr-teal-5);
+ --code-operator: var(--rr-gray-7);
+ --code-punctuation: var(--rr-gray-7);
+
+ /* Elevation. Three steps, tinted with Gray 8 rather than pure black on light. */
+ --shadow-soft: 0 1px 2px color-mix(in srgb, var(--rr-gray-8) 5%, transparent);
+ --shadow-card:
+ 0 1px 2px color-mix(in srgb, var(--rr-gray-8) 5%, transparent),
+ 0 4px 16px color-mix(in srgb, var(--rr-gray-8) 7%, transparent);
+ --shadow-elevated:
+ 0 2px 4px color-mix(in srgb, var(--rr-gray-8) 6%, transparent),
+ 0 12px 32px color-mix(in srgb, var(--rr-gray-8) 12%, transparent);
+
+ /* ── Roles that do not change with the theme ──────────────
+ Two surfaces here are dark in both palettes, so their roles are named for the surface
+ they sit on rather than for the theme, and they are not re-pointed below. */
+
+ /* The titlebar. It was navy in light and a lighter navy in dark, neither of which is a
+ brand value; it is now Gray 8 in both. One value rather than two because the dark
+ theme's requirement fixes it: the bar has to separate from the page it sits on, and a
+ bar darker than a Gray 9 page measures 1.19:1, which is to say it is not visible. Gray
+ 8 is 1.55:1 against the dark page and 12.9:1 against the light one, so the same value
+ works in both and the dark twin the old palette needed is gone. */
+ --titlebar: var(--rr-gray-8);
+ --titlebar-ink: var(--rr-gray-3);
+ --titlebar-ink-strong: var(--rr-gray-1);
+ --titlebar-ink-max: var(--rr-white);
+ --titlebar-muted: var(--rr-gray-5);
+ --titlebar-muted-strong: var(--rr-gray-4);
+ --titlebar-hover-ink: var(--rr-gray-2);
+ --titlebar-accent: var(--rr-blue-4);
+ --titlebar-accent-hover: var(--rr-blue-3);
+ /* The generator's live state, at 0.7rem. Good takes level 3 at 8.1:1 on the bar; bad
+ steps to level 2, because Red 3 there is 4.3:1 and this is small text. The asymmetry
+ is a property of the two hues at the same level, not a choice between them. */
+ --titlebar-ok: var(--rr-green-3);
+ --titlebar-bad: var(--rr-red-2);
+ /* Washes of White and of the brand over the bar. Translucent rather than flattened,
+ because a hairline and a hover both have to work over whatever the bar is carrying. */
+ --titlebar-hairline: color-mix(in srgb, var(--rr-white) 10%, transparent);
+ --titlebar-hover: color-mix(in srgb, var(--rr-white) 8%, transparent);
+ --titlebar-divider: color-mix(in srgb, var(--rr-white) 14%, transparent);
+ --titlebar-active: color-mix(in srgb, var(--rr-blue-6) 30%, transparent);
+ --titlebar-btn-line: color-mix(in srgb, var(--rr-white) 22%, transparent);
+ --titlebar-btn-hover: color-mix(in srgb, var(--rr-white) 10%, transparent);
+ --titlebar-btn-hover-line: color-mix(in srgb, var(--rr-white) 35%, transparent);
+
+ /* The terminal. A terminal is dark in both themes, which is what a terminal looks like,
+ so Redrob Black is the surface and there is nothing to re-resolve when the theme
+ changes. `DeployTerminal` reads these two through `getComputedStyle`, because xterm
+ paints to a canvas and a canvas does not resolve `var()`. */
+ --terminal-bg: var(--rr-black);
+ --terminal-cursor: var(--rr-blue-4);
+ --terminal-scroll: var(--rr-gray-7);
+
+ /* Progress, teal to orange as a run advances. Drawn on the titlebar as often as on a
+ panel, and the titlebar is dark in both themes, so this takes the bright spectrum
+ levels in both rather than the light-theme steps: Teal 5 on the bar would be invisible.
+ Nothing sits on top of it, so it has no ink token. */
+ --progress-start: var(--rr-teal-4);
+ --progress-end: var(--rr-orange-3);
+}
+
+/* ── 2b. SEMANTIC ROLES, dark ────────────────────────────────
+ Only aliases are re-pointed; no new primitive is invented here.
+
+ Gray 9 is the dark background per the brand document, which leaves Redrob Black as the
+ deeper recessed layer beneath it. The scale has nothing between Gray 9 and Gray 8, so a
+ raised surface would jump twelve steps of lightness at once; the answer, Console's, is
+ to mix them for the panel surface. Two extra dark steps in the brand scale would remove
+ the need for it, and would also give the three surfaces below three distinct values
+ instead of two: on dark, `--secondary`, `--muted` and `--accent-active` are all Gray 8,
+ because that is every step the scale has left. */
+[data-theme='dark'] {
+ color-scheme: dark;
+
+ --background: var(--rr-gray-9);
+ --background-secondary: var(--rr-black);
+ --card: color-mix(in srgb, var(--rr-gray-8) 45%, var(--rr-gray-9));
+ --card-foreground: var(--rr-gray-1);
+ --popover: color-mix(in srgb, var(--rr-gray-8) 45%, var(--rr-gray-9));
+ --popover-foreground: var(--rr-gray-1);
+
+ --foreground: var(--rr-gray-1);
+ --muted-foreground: var(--rr-gray-5);
+ /* Gray 6 clears 4.5:1 on Gray 9, unlike on light, but the token keeps one meaning on
+ both sides so its use stays the same: large text, icons and non-text UI. */
+ --subtle-foreground: var(--rr-gray-6);
+ --disabled-foreground: var(--rr-gray-7);
+
+ --accent: color-mix(in srgb, var(--rr-gray-8) 45%, var(--rr-gray-9));
+ --accent-foreground: var(--rr-gray-1);
+ --accent-active: var(--rr-gray-8);
+ --secondary: var(--rr-gray-8);
+ --secondary-foreground: var(--rr-gray-1);
+ --muted: var(--rr-gray-8);
+
+ /* Blue 6 does not carry enough contrast against Gray 9, so dark steps one tint lighter
+ and pairs it with Redrob Black ink: Blue 5 is 5.5:1 with Black and only 3.6:1 with
+ White. Which is also why hover lightens here rather than deepening: the old palette
+ could not lighten a fill without dropping its white label below 4.5:1, and with black
+ ink Blue 4 reads 9.1:1. */
+ --primary: var(--rr-blue-5);
+ --primary-foreground: var(--rr-black);
+ --primary-hover: var(--rr-blue-4);
+ --primary-muted: var(--rr-blue-9);
+ --primary-soft: var(--rr-blue-10);
+ /* Blue 5 is 5.1:1 on the page but 4.4:1 on a raised panel, and a link inside a card is
+ small text, so brand-coloured text steps once more to Blue 4 while `--primary` stays
+ Blue 5 for fills, borders and the focus ring, where the bar is 3:1. */
+ --primary-ink: var(--rr-blue-4);
+
+ --border-subtle: color-mix(in srgb, var(--rr-gray-8) 55%, var(--rr-gray-9));
+ --border: var(--rr-gray-8);
+ --border-strong: var(--rr-gray-7);
+ /* Gray 7 is 2.5:1 against a raised panel, so a control boundary takes Gray 6 here for
+ the same reason it does on light. It is the one step that is the same value in both
+ themes. */
+ --input: var(--rr-gray-6);
+ --input-hover: var(--rr-gray-5);
+ --ring: var(--rr-blue-5);
+
+ /* Level 3 on dark, and Redrob Black as the ink on a fill: Red 3 with White is 3.2:1. */
+ --success: var(--rr-green-3);
+ --success-foreground: var(--rr-black);
+ --success-soft: var(--rr-green-5);
+ --success-ink: var(--rr-green-3);
+ --warning: var(--rr-orange-3);
+ --warning-foreground: var(--rr-black);
+ --warning-soft: var(--rr-orange-5);
+ --warning-ink: var(--rr-orange-3);
+ --destructive: var(--rr-red-3);
+ --destructive-foreground: var(--rr-black);
+ --destructive-soft: var(--rr-red-5);
+ --destructive-ink: var(--rr-red-3);
+ --success-line: var(--rr-green-4);
+ --warning-line: var(--rr-orange-4);
+ --destructive-line: var(--rr-red-4);
+
+ --overlay: color-mix(in srgb, var(--rr-black) 65%, transparent);
+
+ --tooltip: var(--rr-black);
+ --tooltip-foreground: var(--rr-gray-1);
+
+ --sidebar: var(--rr-black);
+ --sidebar-foreground: var(--rr-gray-1);
+ --sidebar-primary: var(--rr-blue-5);
+ --sidebar-primary-foreground: var(--rr-black);
+ --sidebar-accent: var(--rr-gray-8);
+ --sidebar-accent-foreground: var(--rr-gray-1);
+ --sidebar-border: var(--rr-gray-8);
+ --sidebar-ring: var(--rr-blue-5);
+
+ --spectrum-teal: var(--rr-teal-3);
+ --spectrum-sky: var(--rr-sky-3);
+ --spectrum-violet: var(--rr-violet-3);
+ --spectrum-pink: var(--rr-pink-3);
+ --spectrum-red: var(--rr-red-3);
+ --spectrum-orange: var(--rr-orange-3);
+ --spectrum-yellow: var(--rr-yellow-3);
+ --spectrum-lime: var(--rr-lime-3);
+ --spectrum-green: var(--rr-green-3);
+ --chart-cursor: color-mix(in srgb, var(--rr-gray-1) 8%, transparent);
+
+ /* Violet, pink and sky at level 3 are under 4.5:1 on the Gray 8 a code block sits on,
+ so those three step to level 2 here. The rest clear it at level 3. */
+ --code-comment: var(--rr-gray-5);
+ --code-string: var(--rr-green-3);
+ --code-number: var(--rr-orange-3);
+ --code-keyword: var(--rr-violet-2);
+ --code-literal: var(--rr-pink-2);
+ --code-property: var(--rr-sky-2);
+ --code-variable: var(--rr-teal-3);
+ --code-operator: var(--rr-gray-5);
+ --code-punctuation: var(--rr-gray-5);
+
+ /* Re-mixed, because on dark a shadow has to be darker than the surface to read at all. */
+ --shadow-soft: 0 1px 2px color-mix(in srgb, var(--rr-black) 30%, transparent);
+ --shadow-card:
+ 0 1px 2px color-mix(in srgb, var(--rr-black) 25%, transparent),
+ 0 4px 16px color-mix(in srgb, var(--rr-black) 35%, transparent);
+ --shadow-elevated:
+ 0 2px 4px color-mix(in srgb, var(--rr-black) 30%, transparent),
+ 0 12px 32px color-mix(in srgb, var(--rr-black) 50%, transparent);
+}
+
+/* ── 3. LOCAL NAMES ──────────────────────────────────────────
+ Aliases onto band 2, declared once and never re-pointed per theme: the role each one
+ names is already re-pointed above, so a dark twin here would be a second place the dark
+ palette is decided. Deleting those twins is most of what this change removes.
+
+ No colour is decided in this band. Every value is a `var()` of a role, or a mix of one
+ with `transparent` where the old value was translucent and has to stay that way: those
+ sit over table rows and cards as often as over a plain panel, and flattening them would
+ paint over what is behind. */
+:root {
/* Surfaces */
- --bg: #f3f4f6;
- --panel: #ffffff;
- --surface-sunken: #f8fafc;
- --surface-hover: #f1f5f9;
+ --bg: var(--background);
+ --panel: var(--card);
+ --surface-sunken: var(--muted);
+ /* `--accent-active` rather than `--accent`, because these hovers happen on a card and on
+ dark `--accent` and `--card` are the same mix, which would leave the hover invisible. */
+ --surface-hover: var(--accent-active);
/* Lines and text */
- --line: #e5e7eb;
- --line-strong: #d1d5db;
- --ink: #111827;
- --muted: #6b7280;
- --miss: #9ca3af;
- /* Text on a saturated fill. Not `--panel`: a white button label stays white
- in dark mode, where the panel does not. */
- --on-solid: #ffffff;
-
- /* Redrob brand — mark blue #2B52FF */
- --brand: #2b52ff;
- --brand-deep: #1e3acc;
- --brand-ink: #16299b;
- --brand-tint: #eef2ff;
- /* The fill under a white label, kept separate from `--brand` because the two want
- opposite things in dark mode: text must lighten to stay readable on a dark panel,
- while a fill must stay saturated or the white label on top of it disappears. */
- --brand-solid: #2b52ff;
- --brand-solid-hover: #1e3acc;
- --danger-solid: #b91c1c;
+ --line: var(--border);
+ --line-strong: var(--border-strong);
+ --ink: var(--foreground);
+ --miss: var(--disabled-foreground);
+ /* The ink on a saturated fill. White on light and Redrob Black on dark, which is what
+ `--destructive-foreground` carries too, so this one alias serves both fills. */
+ --on-solid: var(--primary-foreground);
+
+ /* Brand. `--brand` is the fill, the border and the ring; `--brand-deep` is the same
+ colour as text, which needs its own step on dark. `--brand-solid` and `--brand-ink`
+ are gone: under the brand tokens the first was `--brand` and the second was
+ `--brand-deep`, and two names for one value is the thing that drifts. */
+ --brand: var(--primary);
+ --brand-hover: var(--primary-hover);
+ --brand-deep: var(--primary-ink);
+ --brand-tint: var(--primary-soft);
+ --brand-tint-soft: color-mix(in srgb, var(--primary) 8%, transparent);
/* Status */
- --ok: #047857;
- --danger: #b91c1c;
- --warn: #a16207;
-
- /* Progress. Teal to orange as a run advances — the only place teal is used, now that
- the primary button is brand blue. Nothing sits on top of it, so it has no ink token. */
- --run: #14b8a6;
- --progress-end: #f97316;
-
- /* Data series */
- --feasible: #1d4ed8;
- --series-seed: #c2410c;
- --series-neutral: #94a3b8;
- --chart-axis: #94a3b8;
- --chart-tick: #64748b;
- --chart-label: #334155;
- --chart-faint: #cbd5e1;
- --chart-grid: rgba(15, 23, 42, 0.08);
- --accent-deep: #0f766e;
-
- /* Ghost controls on a light surface */
- --ghost-line: rgba(15, 23, 42, 0.18);
- --ghost-line-hover: rgba(15, 23, 42, 0.28);
+ --ok: var(--success-ink);
+ --ok-tint: var(--success-soft);
+ --ok-line: var(--success-line);
+ --ok-tint-soft: color-mix(in srgb, var(--success) 10%, transparent);
+ --warn: var(--warning-ink);
+ --warn-tint: var(--warning-soft);
+ --warn-line: var(--warning-line);
+ --warn-line-soft: color-mix(in srgb, var(--warning) 25%, transparent);
+ --danger: var(--destructive-ink);
+ --danger-solid: var(--destructive);
+ --danger-tint: var(--destructive-soft);
+ --danger-line: var(--destructive-line);
+ /* The row tint that marks a router row. A point colour rather than a status, so it comes
+ from the spectrum. */
+ --router-tint: color-mix(in srgb, var(--spectrum-orange) 6%, transparent);
+
+ /* Ghost control boundary */
+ --ghost-line: var(--input);
+ --ghost-line-hover: var(--input-hover);
+
+ /* Data series and chart chrome. The teal `--accent-*` family is gone with the teal
+ primary that named it: every caller was a selected state, and a selected state is
+ brand blue. */
+ --feasible: var(--spectrum-sky);
+ --series-seed: var(--spectrum-orange);
+ --series-neutral: var(--subtle-foreground);
+ --chart-tick: var(--muted-foreground);
+ --chart-label: var(--foreground);
+ --chart-faint: var(--border-strong);
+ --chart-grid: var(--chart-cursor);
/* Overlays */
- --scrim: rgba(14, 22, 51, 0.5);
- --shadow-modal: 0 20px 60px rgba(14, 22, 51, 0.25);
-
- --background: var(--bg);
- --foreground: var(--ink);
-}
-
-[data-theme='dark'] {
- color-scheme: dark;
-
- /* Navy rather than neutral grey, so the page reads as the same product as
- the titlebar instead of a greyscale inversion of it. */
- --bg: #0a0e1a;
- --panel: #141a2b;
- --surface-sunken: #0f1423;
- --surface-hover: #1e2540;
-
- --line: #262d45;
- --line-strong: #39425f;
- --ink: #e8eaf2;
- --muted: #99a2bd;
- --miss: #6b7490;
- --on-solid: #ffffff;
-
- /* Lifted: #2b52ff on a dark panel is close to unreadable for body text, and
- these carry link and label text as well as fills. */
- --brand: #7d97ff;
- --brand-deep: #9cb0ff;
- --brand-ink: #c3cfff;
- --brand-tint: #1b2547;
- /* Not lifted with `--brand`: these carry white text, so they stay saturated. Both
- clear 4.5:1 against white and 3:1 against the panel they sit on. */
- --brand-solid: #3a5eff;
- /* Deepens on hover rather than lightening, matching light mode. Lightening is the
- more usual instinct on a dark theme, but it drops the white label below 4.5:1. */
- --brand-solid-hover: #2b52ff;
- --danger-solid: #dc2626;
-
- --ok: #34d399;
- --danger: #f87171;
- --warn: #fbbf24;
-
- --run: #14b8a6;
- --progress-end: #fb923c;
-
- --feasible: #60a5fa;
- --series-seed: #fb923c;
- --series-neutral: #7c86a3;
- --chart-axis: #6b7490;
- --chart-tick: #8b95b3;
- --chart-label: #b6bdd4;
- --chart-faint: #606b90;
- --chart-grid: rgba(232, 234, 242, 0.08);
- --accent-deep: #2dd4bf;
-
- --ghost-line: rgba(232, 234, 242, 0.2);
- --ghost-line-hover: rgba(232, 234, 242, 0.34);
-
- --scrim: rgba(3, 6, 15, 0.66);
- --shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.6);
-}
-
-/* Status tints, spelled out per theme rather than mixed from the base colour.
- Mixing was tried and is tidier, but a mix of `--warn` into white lands on beige,
- not on the cream the light theme has always used: the elegant mechanism quietly
- restyles the existing product. Two explicit rows cost more lines and change nothing
- that was not asked for.
-
- The translucent ones stay translucent. They sit over table rows and cards as often
- as over a plain panel, and flattening them to opaque would lose the surface
- underneath. */
-:root {
- --ok-tint: #ecfdf5;
- --ok-line: #a7f3d0;
- --warn-tint: #fffbeb;
- --warn-line: #fde68a;
- --danger-tint: #fef2f2;
- --danger-line: #fecaca;
- --brand-tint-soft: rgba(37, 99, 235, 0.06);
- --accent-tint: rgba(15, 118, 110, 0.08);
- --accent-tint-strong: rgba(15, 118, 110, 0.1);
- --accent-line: rgba(15, 118, 110, 0.45);
- --router-tint: rgba(194, 65, 12, 0.04);
- /* Translucent rather than a tint of the panel: both sit on surfaces that are not the
- panel, so flattening them would paint over what is behind. */
- --warn-line-soft: rgba(161, 98, 7, 0.25);
- --ok-tint-soft: rgba(4, 120, 87, 0.1);
-}
-
-[data-theme='dark'] {
- /* Dark enough to read as a tint of the panel rather than a block of colour, with
- the hue still identifiable as green, amber or red at a glance. */
- --ok-tint: #0e2a22;
- --ok-line: #1f5545;
- --warn-tint: #2b2312;
- --warn-line: #5c4a1a;
- --danger-tint: #2e1618;
- --danger-line: #6b2b2e;
- --brand-tint-soft: rgba(125, 151, 255, 0.1);
- --accent-tint: rgba(45, 212, 191, 0.1);
- --accent-tint-strong: rgba(45, 212, 191, 0.16);
- --accent-line: rgba(45, 212, 191, 0.45);
- --router-tint: rgba(251, 146, 60, 0.07);
- --warn-line-soft: rgba(251, 191, 36, 0.3);
- --ok-tint-soft: rgba(52, 211, 153, 0.14);
-}
-
-/* The titlebar is dark in both themes, so its tokens are named for the surface
- they sit on rather than for the theme, and they do not change. */
-:root {
- --titlebar: #0e1633;
- --titlebar-ink: #e5e7eb;
- --titlebar-ink-strong: #f8fafc;
- --titlebar-ink-max: #ffffff;
- --titlebar-muted: #94a3b8;
- --titlebar-muted-strong: #cbd5e1;
- --titlebar-hover-ink: #e2e8f0;
- --titlebar-accent: #6f8bff;
- --titlebar-accent-hover: #9db1ff;
- --titlebar-ok: #a5b4fc;
- --titlebar-bad: #fca5a5;
- --titlebar-hairline: rgba(255, 255, 255, 0.06);
- --titlebar-hover: rgba(255, 255, 255, 0.06);
- --titlebar-divider: rgba(255, 255, 255, 0.12);
- --titlebar-active: rgba(43, 82, 255, 0.28);
- --titlebar-btn-line: rgba(255, 255, 255, 0.22);
- --titlebar-btn-hover: rgba(255, 255, 255, 0.1);
- --titlebar-btn-hover-line: rgba(255, 255, 255, 0.35);
-
- --terminal-bg: #0b0f14;
- --terminal-cursor: #6f8bff;
- --terminal-scroll: #3a4658;
-}
-
-[data-theme='dark'] {
- /* In light mode the titlebar is "the dark one" and needs no help separating itself.
- In dark mode nothing is the dark one, so it separates by hue and by being slightly
- lighter than the page instead: a darker bar measured 1.02:1 against the page
- behind it, which is to say it was not visible at all. The hairline is opened up
- for the same reason -- at 6% on a near-black page there was no edge to see. */
- --titlebar: #172a5c;
- --titlebar-hairline: rgba(255, 255, 255, 0.12);
- /* The bar it sits on is lighter here, so the logo lifts with it. */
- --titlebar-accent: #8aa2ff;
+ --scrim: var(--overlay);
+ --shadow-modal: var(--shadow-elevated);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
- --font-sans: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+
+ /* Type. Pretendard is the single Redrob product family and the only approved product
+ typeface: it carries Latin and Hangul in one face, so Korean is not a fallback here,
+ it is the same font. Geist, Inter and Fraunces are not part of product typography.
+
+ `Pretendard Variable` is the family the pinned variable dynamic subset registers; the
+ root layout loads it from jsDelivr, which is the one place the version is named. Bare
+ `Pretendard` covers an OS install or the static build, for engines that ignore
+ variable fonts, and the system stack is a last resort while the webfont is in flight
+ rather than an alternative: its metrics are close enough that the swap barely moves
+ the text.
+
+ Declared here rather than in band 2 because Tailwind emits `@theme` keys as `:root`
+ custom properties, so this is both the utility and the `var(--font-sans)` the rules
+ below name, and there is one declaration of the stack instead of the five literal
+ copies this file used to carry. */
+ --font-sans:
+ "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui,
+ "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ /* A deliberate alias, not a placeholder for a future serif. There is no second display
+ face; brand moments use the same family at a heavier weight and tighter tracking. */
+ --font-display: var(--font-sans);
+ /* Code only: token values, terminal output, JSON, model ids. Never UI labels or body
+ copy. The brand's monospace is JetBrains Mono, which this product does not load and
+ will not vendor, so the stack names it for machines that have it and falls through to
+ the system face, which is what actually renders here. Nothing is fetched for it. */
+ --font-mono:
+ "JetBrains Mono Variable", "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono",
+ Menlo, Consolas, "Liberation Mono", monospace;
}
* {
@@ -238,12 +579,12 @@ body {
margin: 0;
min-height: 100%;
color: var(--ink);
- font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ font-family: var(--font-sans);
background: var(--bg);
}
code {
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
font-size: 0.9em;
}
@@ -315,7 +656,7 @@ code {
align-items: center;
gap: 0.45rem;
flex: none;
- font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ font-family: var(--font-sans);
font-weight: 650;
font-size: 0.88rem;
letter-spacing: -0.02em;
@@ -378,7 +719,7 @@ code {
.app-progress-bar {
height: 100%;
- background: linear-gradient(90deg, var(--run), var(--progress-end));
+ background: linear-gradient(90deg, var(--progress-start), var(--progress-end));
transition: width 0.2s ease;
}
@@ -469,12 +810,12 @@ code {
}
.app-run-btn {
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
}
.app-run-btn:hover:not(:disabled) {
- background: var(--brand-solid-hover);
+ background: var(--brand-hover);
}
.app-run-btn:disabled {
@@ -589,7 +930,7 @@ body.is-pane-resizing * {
flex-direction: column;
gap: 0.3rem;
font-size: 0.8rem;
- color: var(--muted);
+ color: var(--muted-foreground);
min-width: 0;
max-width: 100%;
}
@@ -654,7 +995,7 @@ body.is-pane-resizing * {
.empty {
margin: 0;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.4;
}
@@ -676,7 +1017,7 @@ body.is-pane-resizing * {
.sample-presets button.on {
border-color: var(--brand);
- background: var(--accent-tint);
+ background: var(--brand-tint);
color: var(--brand-deep);
font-weight: 600;
}
@@ -764,7 +1105,7 @@ body.is-pane-resizing * {
font-size: 0.66rem;
text-transform: uppercase;
letter-spacing: 0.04em;
- color: var(--muted);
+ color: var(--muted-foreground);
background: var(--surface-sunken);
position: sticky;
top: 0;
@@ -783,12 +1124,12 @@ body.is-pane-resizing * {
.sub {
display: block;
font-size: 0.68rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-weight: 400;
}
.mono {
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
font-size: 0.7rem;
}
@@ -821,7 +1162,7 @@ body.is-pane-resizing * {
.or-models-title {
margin: 0;
- font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ font-family: var(--font-sans);
font-size: 0.95rem;
font-weight: 700;
}
@@ -829,7 +1170,7 @@ body.is-pane-resizing * {
.or-models-sub {
margin: 0.15rem 0 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.or-models-tools {
@@ -842,7 +1183,7 @@ body.is-pane-resizing * {
align-self: center;
font-size: 0.7rem;
font-weight: 600;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
@@ -872,7 +1213,7 @@ body.is-pane-resizing * {
.or-source-chip {
border: 1px solid var(--line);
background: var(--panel);
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.68rem;
font-weight: 600;
padding: 0.2rem 0.5rem;
@@ -905,7 +1246,7 @@ body.is-pane-resizing * {
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
- color: var(--muted);
+ color: var(--muted-foreground);
display: flex;
align-items: center;
gap: 0.35rem;
@@ -940,8 +1281,8 @@ body.is-pane-resizing * {
}
.or-model-card.on {
- border-color: var(--accent-line);
- background: var(--accent-tint);
+ border-color: var(--brand);
+ background: var(--brand-tint);
}
.or-model-card.browse-only {
@@ -988,22 +1329,22 @@ body.is-pane-resizing * {
.or-model-slug {
font-size: 0.66rem;
- color: var(--muted);
+ color: var(--muted-foreground);
word-break: break-all;
}
.or-select-mark {
font-size: 0.66rem;
font-weight: 600;
- color: var(--muted);
+ color: var(--muted-foreground);
border: 1px solid var(--line);
padding: 0.12rem 0.35rem;
}
.or-select-mark.checked {
color: var(--brand-deep);
- border-color: var(--accent-line);
- background: var(--accent-tint-strong);
+ border-color: var(--brand);
+ background: var(--brand-tint);
}
.or-browse-mark {
@@ -1019,7 +1360,7 @@ body.is-pane-resizing * {
font-size: 0.62rem;
font-weight: 600;
color: var(--ok);
- border: 1px solid color-mix(in srgb, var(--ok) 45%, transparent);
+ border: 1px solid var(--ok-line);
padding: 0.12rem 0.3rem;
}
@@ -1027,7 +1368,7 @@ body.is-pane-resizing * {
margin: 0.35rem 0 0;
font-size: 0.7rem;
line-height: 1.35;
- color: var(--muted);
+ color: var(--muted-foreground);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@@ -1040,7 +1381,7 @@ body.is-pane-resizing * {
flex-wrap: wrap;
gap: 0.3rem 0.55rem;
font-size: 0.66rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
@media (max-width: 1100px) {
@@ -1155,7 +1496,7 @@ body.is-pane-resizing * {
}
.run-progress-head span {
- color: var(--muted);
+ color: var(--muted-foreground);
white-space: nowrap;
}
@@ -1169,7 +1510,7 @@ body.is-pane-resizing * {
.run-progress-bar {
height: 100%;
- background: var(--run);
+ background: var(--progress-start);
transition: width 0.2s ease;
}
@@ -1185,7 +1526,7 @@ body.is-pane-resizing * {
.run-progress-detail {
margin: 0.3rem 0 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -1218,7 +1559,7 @@ body.is-pane-resizing * {
display: block;
margin-top: 0.1rem;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.getting-started-dismiss {
@@ -1228,7 +1569,7 @@ body.is-pane-resizing * {
padding: 0 0.45rem;
font-size: 0.7rem;
cursor: pointer;
- color: var(--muted);
+ color: var(--muted-foreground);
flex: none;
}
@@ -1268,12 +1609,12 @@ body.is-pane-resizing * {
justify-content: center;
font-size: 0.65rem;
font-weight: 700;
- color: var(--muted);
+ color: var(--muted-foreground);
background: var(--panel);
}
.getting-started-steps li[data-done='1'] .getting-started-n {
- border-color: var(--ok);
+ border-color: var(--ok-line);
background: var(--ok-tint-soft);
color: var(--ok);
}
@@ -1287,7 +1628,7 @@ body.is-pane-resizing * {
.getting-started-steps p {
margin: 0.1rem 0 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.35;
}
@@ -1308,13 +1649,13 @@ body.is-pane-resizing * {
}
.getting-started-primary {
- background: var(--brand-solid);
+ background: var(--brand);
border-color: var(--brand);
color: var(--on-solid);
}
.getting-started-primary:hover {
- background: var(--brand-solid-hover);
+ background: var(--brand-hover);
}
.getting-started-secondary {
@@ -1330,7 +1671,7 @@ body.is-pane-resizing * {
.getting-started-foot {
margin: 0;
font-size: 0.68rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.4;
}
@@ -1365,7 +1706,7 @@ body.is-pane-resizing * {
.evolve-glance-kicker {
margin: 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
@@ -1384,7 +1725,7 @@ body.is-pane-resizing * {
margin-left: 0.35rem;
font-size: 0.85rem;
font-weight: 600;
- color: var(--muted);
+ color: var(--muted-foreground);
letter-spacing: 0;
}
@@ -1395,7 +1736,7 @@ body.is-pane-resizing * {
padding: 0.25rem 0.5rem;
border: 1px solid var(--line);
background: var(--panel);
- color: var(--muted);
+ color: var(--muted-foreground);
}
.evolve-glance-status[data-tone='ok'] {
@@ -1426,7 +1767,7 @@ body.is-pane-resizing * {
.evolve-glance-stats span {
display: block;
font-size: 0.65rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.03em;
@@ -1454,7 +1795,7 @@ body.is-pane-resizing * {
.evolve-glance-caption {
margin: 0;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.evolve-runway {
@@ -1509,7 +1850,7 @@ body.is-pane-resizing * {
.evolve-runway-n {
font-size: 0.62rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.evolve-view-switch {
@@ -1530,7 +1871,7 @@ body.is-pane-resizing * {
.evolve-view-switch button.on {
border-color: var(--brand);
- background: var(--accent-tint);
+ background: var(--brand-tint);
color: var(--brand-deep);
font-weight: 600;
}
@@ -1558,7 +1899,7 @@ body.is-pane-resizing * {
.evolve-chart-read p {
margin: 0;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.4;
}
@@ -1584,7 +1925,7 @@ body.is-pane-resizing * {
flex-wrap: wrap;
gap: 0.45rem 0.85rem;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.evolve-chart-legend li {
@@ -1637,7 +1978,7 @@ body.is-pane-resizing * {
background: var(--panel);
padding: 0.45rem 0.55rem;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
max-width: 220px;
line-height: 1.35;
}
@@ -1648,7 +1989,7 @@ body.is-pane-resizing * {
}
.evolve-chart-tip-id {
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
font-size: 0.65rem;
margin: 0.1rem 0 0.25rem;
word-break: break-all;
@@ -1731,7 +2072,7 @@ body.is-pane-resizing * {
.deploy-notice {
margin: 0;
font-size: 12px;
- color: var(--brand-ink);
+ color: var(--brand-deep);
background: var(--brand-tint);
border-radius: 0;
padding: 6px 10px;
@@ -1742,7 +2083,7 @@ body.is-pane-resizing * {
margin: 0 0 6px;
}
.deploy-lede {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 13px;
margin: 0 0 14px;
}
@@ -1759,18 +2100,18 @@ body.is-pane-resizing * {
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-callout {
margin: 0;
padding: 8px 10px;
font-size: 12px;
line-height: 1.45;
- border: 1px solid color-mix(in srgb, var(--warn) 45%, var(--line));
+ border: 1px solid var(--warn-line);
background: color-mix(in srgb, var(--warn) 7%, transparent);
}
.deploy-callout.is-error {
- border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
+ border-color: var(--danger-line);
background: color-mix(in srgb, var(--danger) 7%, transparent);
}
.deploy-callout a {
@@ -1816,18 +2157,18 @@ body.is-pane-resizing * {
place-items: center;
font-size: 11px;
font-weight: 700;
- color: var(--muted);
+ color: var(--muted-foreground);
border: 1px solid var(--line);
}
.deploy-step.is-done .deploy-step-num {
color: var(--ok);
- border-color: color-mix(in srgb, var(--ok) 45%, var(--line));
- background: color-mix(in srgb, var(--ok) 10%, transparent);
+ border-color: var(--ok-line);
+ background: var(--ok-tint-soft);
}
.deploy-step.is-running .deploy-step-num {
- color: var(--brand);
- border-color: color-mix(in srgb, var(--brand) 55%, var(--line));
- background: color-mix(in srgb, var(--brand) 12%, transparent);
+ color: var(--brand-deep);
+ border-color: var(--brand);
+ background: var(--brand-tint-soft);
animation: deploy-step-pulse 1.4s ease-in-out infinite;
}
@keyframes deploy-step-pulse {
@@ -1855,7 +2196,7 @@ body.is-pane-resizing * {
margin: 0;
font-size: 12px;
line-height: 1.45;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-step-actions {
display: flex;
@@ -1863,16 +2204,16 @@ body.is-pane-resizing * {
gap: 6px;
}
.deploy-op-btn.is-primary {
- background: var(--brand-solid);
- border-color: var(--brand-solid);
+ background: var(--brand);
+ border-color: var(--brand);
color: var(--on-solid);
}
.deploy-op-btn.is-primary:hover:not(:disabled) {
- background: var(--brand-solid-hover);
+ background: var(--brand-hover);
}
.deploy-op-btn.is-danger {
color: var(--danger);
- border-color: color-mix(in srgb, var(--danger) 35%, var(--line));
+ border-color: var(--danger-line);
}
.deploy-section-head {
display: flex;
@@ -1902,7 +2243,7 @@ body.is-pane-resizing * {
}
.deploy-host-facts dt {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-host-facts dd {
margin: 0;
@@ -1928,13 +2269,13 @@ body.is-pane-resizing * {
}
.deploy-slot-status {
font-weight: 400;
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 12px;
}
.deploy-slot-meta {
margin: 0 0 10px;
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-slot-paths {
display: grid;
@@ -1943,7 +2284,7 @@ body.is-pane-resizing * {
}
.deploy-slot-paths dt {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-slot-paths dd {
margin: 0;
@@ -1970,7 +2311,7 @@ body.is-pane-resizing * {
gap: 8px;
}
.deploy-facts dt {
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-facts dd {
margin: 0;
@@ -1978,7 +2319,7 @@ body.is-pane-resizing * {
}
.deploy-hint {
margin: 0 0 12px;
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 12px;
line-height: 1.5;
}
@@ -1994,7 +2335,7 @@ body.is-pane-resizing * {
flex-direction: column;
gap: 4px;
font-size: 12px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-field select {
padding: 6px 8px;
@@ -2024,7 +2365,7 @@ body.is-pane-resizing * {
border: 1px solid var(--line);
border-radius: 0;
font-size: 13px;
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
background: var(--panel);
}
.deploy-hf-input:focus {
@@ -2060,7 +2401,7 @@ body.is-pane-resizing * {
cursor: not-allowed;
}
.deploy-empty {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 13px;
}
.deploy-error {
@@ -2091,7 +2432,7 @@ body.is-pane-resizing * {
.deploy-terminal-hint {
flex: 1;
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.deploy-terminal-surface {
height: 320px;
@@ -2155,7 +2496,7 @@ body.is-pane-resizing * {
border-radius: 999px;
border: 1px solid var(--line-strong);
background: var(--panel);
- color: var(--muted);
+ color: var(--muted-foreground);
cursor: pointer;
max-width: 100%;
overflow: hidden;
@@ -2171,7 +2512,7 @@ body.is-pane-resizing * {
.arena-chip.on {
background: var(--brand-tint);
border-color: var(--brand);
- color: var(--brand-ink);
+ color: var(--brand-deep);
font-weight: 600;
}
@@ -2221,7 +2562,7 @@ body.is-pane-resizing * {
.arena-rank {
font-size: 13px;
font-weight: 700;
- color: var(--muted);
+ color: var(--muted-foreground);
min-width: 1ch;
}
@@ -2238,7 +2579,7 @@ body.is-pane-resizing * {
.arena-name code {
font-size: 10px;
- color: var(--muted);
+ color: var(--muted-foreground);
overflow: hidden;
text-overflow: ellipsis;
}
@@ -2259,7 +2600,7 @@ body.is-pane-resizing * {
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.04em;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.arena-row-stats {
@@ -2267,7 +2608,7 @@ body.is-pane-resizing * {
flex-wrap: wrap;
gap: 4px 10px;
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -2279,7 +2620,7 @@ body.is-pane-resizing * {
.arena-dims span {
font-size: 10px;
- color: var(--muted);
+ color: var(--muted-foreground);
background: var(--surface-sunken);
border: 1px solid var(--line);
border-radius: 999px;
@@ -2299,7 +2640,7 @@ body.is-pane-resizing * {
.arena-sub {
font-size: 10px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.arena-error {
@@ -2310,7 +2651,7 @@ body.is-pane-resizing * {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.03em;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.arena-status.is-done {
@@ -2322,13 +2663,13 @@ body.is-pane-resizing * {
}
.arena-status.is-running {
- color: var(--brand);
+ color: var(--brand-deep);
}
.arena-prompt summary {
font-size: 12px;
cursor: pointer;
- color: var(--muted);
+ color: var(--muted-foreground);
padding: 3px 0;
}
@@ -2437,7 +2778,7 @@ body.is-pane-resizing * {
.settings-nav-sub {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.settings-nav-dot {
@@ -2464,7 +2805,7 @@ body.is-pane-resizing * {
}
.settings-pane-head p {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 12px;
line-height: 1.6;
margin: 0;
@@ -2486,27 +2827,27 @@ body.is-pane-resizing * {
}
.settings-h1 {
- font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ font-family: var(--font-sans);
font-size: 20px;
font-weight: 800;
letter-spacing: -0.03em;
margin: 0 0 6px;
}
.settings-lede {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 12px;
line-height: 1.6;
margin: 0;
}
.settings-note {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 12px;
margin: 8px 0 0;
}
.settings-note code,
.settings-lede code {
background: var(--brand-tint);
- color: var(--brand-ink);
+ color: var(--brand-deep);
padding: 1px 5px;
border-radius: 4px;
font-size: 0.92em;
@@ -2546,12 +2887,12 @@ body.is-pane-resizing * {
padding: 10px;
margin: -10px;
border-radius: 8px;
- box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand, var(--brand)) 35%, transparent);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring) 35%, transparent);
}
.settings-hint-link {
margin-top: 4px;
font-size: 12px;
- color: var(--brand, var(--brand));
+ color: var(--brand-deep);
}
@media (max-width: 720px) {
.settings-row {
@@ -2577,28 +2918,28 @@ body.is-pane-resizing * {
background: var(--miss);
}
.settings-dot.on {
- background: var(--ok);
+ background: var(--success);
}
.settings-default-tag {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
- color: var(--muted);
+ color: var(--muted-foreground);
background: var(--surface-hover);
border-radius: 4px;
padding: 1px 5px;
}
.settings-input.is-default {
- color: var(--muted);
+ color: var(--muted-foreground);
}
.settings-key {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.settings-hint {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.5;
}
.settings-control {
@@ -2678,7 +3019,7 @@ body.is-pane-resizing * {
border: 1px solid var(--line);
border-radius: 7px;
font-size: 13px;
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
background: var(--panel);
}
.settings-input:focus {
@@ -2763,7 +3104,7 @@ body.is-pane-resizing * {
}
.picker-path {
font-size: 11px;
- color: var(--muted);
+ color: var(--muted-foreground);
overflow-wrap: anywhere;
}
.picker-list {
@@ -2823,7 +3164,7 @@ body.is-pane-resizing * {
gap: 0.3rem;
border: 0;
background: transparent;
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.72rem;
font-weight: 600;
padding: 0.2rem 0.45rem;
@@ -2951,7 +3292,7 @@ body.is-pane-resizing * {
.cmp-reports-lede {
margin: 0.2rem 0 0;
font-size: 0.8rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-reports-actions {
@@ -3001,7 +3342,7 @@ body.is-pane-resizing * {
padding: 0.12rem 0.4rem;
border-radius: 999px;
background: var(--surface-hover);
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-reports-item-label {
@@ -3060,7 +3401,7 @@ body.is-pane-resizing * {
.cmp-modality span {
font-size: 0.68rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-modality.on {
@@ -3088,12 +3429,12 @@ body.is-pane-resizing * {
padding: 0.28rem 0.8rem;
font-size: 0.72rem;
font-weight: 600;
- color: var(--muted);
+ color: var(--muted-foreground);
cursor: pointer;
}
.cmp-seg button.on {
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
}
@@ -3114,7 +3455,7 @@ body.is-pane-resizing * {
.cmp-models-count {
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -3188,7 +3529,7 @@ body.is-pane-resizing * {
align-items: center;
gap: 0.6rem;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -3208,7 +3549,7 @@ body.is-pane-resizing * {
.cmp-caveat {
font-size: 0.62rem;
- color: var(--muted);
+ color: var(--muted-foreground);
max-width: 28rem;
overflow: hidden;
text-overflow: ellipsis;
@@ -3234,7 +3575,7 @@ body.is-pane-resizing * {
display: flex;
justify-content: space-between;
font-size: 0.64rem;
- color: var(--muted);
+ color: var(--muted-foreground);
margin-bottom: 0.25rem;
}
@@ -3330,7 +3671,7 @@ body.is-pane-resizing * {
width: 1.4rem;
height: 1.4rem;
border-radius: 999px;
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
font-size: 0.72rem;
font-weight: 700;
@@ -3339,7 +3680,7 @@ body.is-pane-resizing * {
.vote-card-reveal {
font-size: 0.68rem;
font-weight: 600;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.vote-card-body {
@@ -3372,7 +3713,7 @@ body.is-pane-resizing * {
.vote-card-pick {
border: 1px solid var(--brand);
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
border-radius: 8px;
padding: 0.4rem 0.75rem;
@@ -3400,7 +3741,7 @@ body.is-pane-resizing * {
.vote-card-out {
border: 1px solid var(--line);
background: transparent;
- color: var(--muted);
+ color: var(--muted-foreground);
border-radius: 8px;
padding: 0.4rem 0.75rem;
font-size: 0.75rem;
@@ -3417,7 +3758,7 @@ body.is-pane-resizing * {
.vote-card-badge {
font-size: 0.68rem;
font-weight: 700;
- color: var(--brand);
+ color: var(--brand-deep);
}
.cmp-pref-nav {
@@ -3436,7 +3777,7 @@ body.is-pane-resizing * {
.cmp-pref-mode {
border: 1px solid var(--line);
background: var(--surface-sunken);
- color: var(--fg);
+ color: var(--ink);
border-radius: 999px;
padding: 0.3rem 0.7rem;
font-size: 0.73rem;
@@ -3446,7 +3787,7 @@ body.is-pane-resizing * {
.cmp-pref-mode.on {
border-color: var(--brand);
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
}
@@ -3476,7 +3817,7 @@ body.is-pane-resizing * {
.cmp-pref-order li::before {
counter-increment: pref-order;
content: counter(pref-order) '.';
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -3494,7 +3835,7 @@ body.is-pane-resizing * {
}
.cmp-pref-order-note {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.7rem;
}
@@ -3536,7 +3877,7 @@ body.is-pane-resizing * {
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.04em;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-route-stats dd {
@@ -3548,7 +3889,7 @@ body.is-pane-resizing * {
.cmp-route-stats span {
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.4;
}
@@ -3572,7 +3913,7 @@ body.is-pane-resizing * {
}
.gen-notice-detail {
- color: var(--muted);
+ color: var(--muted-foreground);
}
.gen-notice-error {
@@ -3606,7 +3947,7 @@ body.is-pane-resizing * {
.gen-desc {
margin: 0;
font-size: 0.78rem;
- color: var(--muted);
+ color: var(--muted-foreground);
line-height: 1.5;
}
@@ -3655,13 +3996,13 @@ body.is-pane-resizing * {
.gen-template-meta {
font-size: 0.68rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.gen-template-id {
font-size: 0.66rem;
- color: var(--muted);
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ color: var(--muted-foreground);
+ font-family: var(--font-mono);
}
.gen-detail-head {
@@ -3682,7 +4023,7 @@ body.is-pane-resizing * {
.gen-subtitle {
margin: 0.15rem 0 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
/* ---- Generate: getting the output out of the page ---- */
@@ -3729,7 +4070,7 @@ body.is-pane-resizing * {
margin: 0;
font-size: 0.7rem;
line-height: 1.55;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.gen-locale-row {
@@ -3798,7 +4139,7 @@ body.is-pane-resizing * {
.gen-locale.on {
border-color: var(--brand);
background: var(--brand-tint);
- color: var(--brand-ink);
+ color: var(--brand-deep);
}
.gen-count {
@@ -3806,7 +4147,7 @@ body.is-pane-resizing * {
align-items: center;
gap: 0.35rem;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.gen-count input {
@@ -3876,7 +4217,7 @@ body.is-pane-resizing * {
.gen-seed {
font-size: 0.65rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -3992,21 +4333,21 @@ body.is-pane-resizing * {
}
.theme-switch-option:focus-visible {
- outline: 2px solid var(--brand);
+ outline: 2px solid var(--ring);
outline-offset: 2px;
}
.theme-switch-option.on {
border-color: var(--brand);
background: var(--brand-tint);
- color: var(--brand-ink);
+ color: var(--brand-deep);
font-weight: 600;
}
.theme-switch-note {
margin: 0;
font-size: 0.7rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
/* ---- Compare · tool routing ---- */
@@ -4033,13 +4374,13 @@ body.is-pane-resizing * {
gap: 0.5rem;
font-size: 0.72rem;
line-height: 1.4;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-legend-key {
flex: none;
min-width: 4.5rem;
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
font-size: 0.68rem;
font-weight: 700;
color: var(--brand-deep);
@@ -4077,7 +4418,7 @@ body.is-pane-resizing * {
align-items: center;
gap: 0.4rem;
font-size: 0.72rem;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-endpoint-dot {
@@ -4085,11 +4426,11 @@ body.is-pane-resizing * {
width: 7px;
height: 7px;
border-radius: 50%;
- background: var(--muted);
+ background: var(--muted-foreground);
}
.cmp-endpoint-dot.ok {
- background: var(--ok);
+ background: var(--success);
}
.cmp-endpoint-dot.warn {
@@ -4117,7 +4458,7 @@ body.is-pane-resizing * {
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
- color: var(--muted);
+ color: var(--muted-foreground);
}
/* Secondary detail folded away, so the card leads with the result. */
@@ -4146,7 +4487,7 @@ body.is-pane-resizing * {
content: '▸';
display: inline-block;
width: 1em;
- color: var(--muted);
+ color: var(--muted-foreground);
}
.cmp-details[open] > summary::before {
@@ -4155,7 +4496,7 @@ body.is-pane-resizing * {
/* A cell that stands in for numbers that were never measured. */
.cmp-cell-muted {
- color: var(--muted);
+ color: var(--muted-foreground);
font-style: italic;
}
@@ -4230,7 +4571,7 @@ body.is-pane-resizing * {
.cmp-prompt-stack h4 {
margin: 0 0 0.35rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.66rem;
text-transform: uppercase;
}
@@ -4277,7 +4618,7 @@ body.is-pane-resizing * {
.cmp-prompt-grid h4 {
margin: 0 0 0.35rem;
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.66rem;
text-transform: uppercase;
}
@@ -4338,10 +4679,10 @@ body.is-pane-resizing * {
border: 1px solid var(--line);
border-radius: 8px;
background: var(--surface-sunken);
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-family: var(--font-mono);
font-size: 0.68rem;
line-height: 1.5;
- color: var(--muted);
+ color: var(--muted-foreground);
font-variant-numeric: tabular-nums;
}
@@ -4376,7 +4717,7 @@ body.is-pane-resizing * {
.az-filters select {
border: 1px solid var(--line);
background: var(--surface-sunken);
- color: var(--fg);
+ color: var(--ink);
border-radius: 8px;
padding: 0.3rem 0.55rem;
font-size: 0.75rem;
@@ -4406,9 +4747,9 @@ body.is-pane-resizing * {
}
.az-status-done { color: var(--ok); }
.az-status-failed { color: var(--danger); }
-.az-status-running { color: var(--brand); }
+.az-status-running { color: var(--brand-deep); }
.az-status-cancelled,
-.az-status-queued { color: var(--muted); }
+.az-status-queued { color: var(--muted-foreground); }
/* Tabs have no bottom edge and top-only corners, so they are meant to sit on the
panel they open. The page gap under them is cancelled, not inherited. */
@@ -4422,7 +4763,7 @@ body.is-pane-resizing * {
border: 1px solid var(--line);
border-bottom: none;
background: var(--surface-sunken);
- color: var(--fg);
+ color: var(--ink);
border-radius: 8px 8px 0 0;
padding: 0.4rem 0.9rem;
font-size: 0.78rem;
@@ -4430,7 +4771,7 @@ body.is-pane-resizing * {
cursor: pointer;
}
.az-tab.on {
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
border-color: var(--brand);
}
@@ -4444,7 +4785,7 @@ body.is-pane-resizing * {
.az-chip {
border: 1px solid var(--line);
background: var(--surface-sunken);
- color: var(--fg);
+ color: var(--ink);
border-radius: 999px;
padding: 0.25rem 0.6rem;
font-size: 0.72rem;
@@ -4452,7 +4793,7 @@ body.is-pane-resizing * {
}
.az-chip.on {
border-color: var(--brand);
- background: var(--brand-solid);
+ background: var(--brand);
color: var(--on-solid);
}
.az-chip-recoverable {
@@ -4469,17 +4810,17 @@ body.is-pane-resizing * {
}
.az-annot,
.az-was {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.68rem;
margin-left: 0.3rem;
}
.az-lang {
- color: var(--muted);
+ color: var(--muted-foreground);
font-size: 0.66rem;
margin-left: 0.35rem;
}
.az-detail-cell {
- color: var(--muted);
+ color: var(--muted-foreground);
max-width: 30rem;
overflow: hidden;
text-overflow: ellipsis;
@@ -4494,7 +4835,7 @@ body.is-pane-resizing * {
align-items: start;
}
.az-kv-key {
- color: var(--muted);
+ color: var(--muted-foreground);
text-transform: uppercase;
font-size: 0.64rem;
padding-top: 0.15rem;
@@ -4528,7 +4869,7 @@ body.is-pane-resizing * {
.az-reclassify select {
border: 1px solid var(--line);
background: var(--surface-sunken);
- color: var(--fg);
+ color: var(--ink);
border-radius: 8px;
padding: 0.3rem 0.55rem;
font-size: 0.75rem;
@@ -4541,7 +4882,7 @@ body.is-pane-resizing * {
margin: 0;
}
.az-facts dt {
- color: var(--muted);
+ color: var(--muted-foreground);
text-transform: uppercase;
font-size: 0.62rem;
}
@@ -4551,5 +4892,5 @@ body.is-pane-resizing * {
}
.az-warn {
- color: var(--muted);
+ color: var(--muted-foreground);
}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 7ba6ca2..2fafac2 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -7,6 +7,18 @@ import { ThemeProvider } from '@/components/ThemeProvider';
import { LOCALE_SCRIPT } from '@/lib/i18n/locale';
import { THEME_SCRIPT } from '@/lib/theme';
+/**
+ * Pretendard, from the same CDN and at the same pin as every other Redrob product, so the
+ * type cannot shift under one of them without a commit saying so. This is the one place the
+ * version is named; `--font-sans` in `globals.css` names the family it registers.
+ *
+ * The variable dynamic subset, specifically. It carries the whole weight axis the design
+ * uses and splits the face into slices the browser fetches only when a page actually renders
+ * glyphs from them, where the full file is mostly Hangul that an English screen never draws.
+ */
+const PRETENDARD_CSS =
+ 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css';
+
export const metadata: Metadata = {
title: 'redrob-eval',
description:
@@ -29,10 +41,11 @@ export default function RootLayout({
that, because effects run after paint. */}