ui/_tokens.scss
// =====================================================================
// Vehicle Prototyping — UI design tokens (CANONICAL SOURCE)
// Onyx dark-glass theme + signal-cyan accent.
//
// IMPORTANT — why panels INLINE these instead of `@import`-ing this file:
// scss `@import` of a shared partial is engine-supported at RUNTIME but is
// NOT resolved by the headless `dotnet build` — a bad path ships a broken
// panel that builds GREEN.
// Recommendation: prefer self-contained *.razor.scss and reserve
// `@import` consolidation for the post-screenshot pass. Until someone
// screenshot-verifies `@import` resolution in a GAME project, every panel
// inlines the literal values below. THIS FILE IS THE ONE PLACE TO EDIT A
// TOKEN — then drift-sweep the literal into the panels.
//
// FONT / GLYPH TRAP: many selectors each declaring
// `font-size` (and ESPECIALLY `letter-spacing`) corrupt glyph fill for the
// whole panel. Safe config = NO letter-spacing anywhere + font-size ONLY on a
// handful of hero numerals per panel. font-family is glyph-SAFE.
// =====================================================================
// --- accent ---
$cyan: #4AD9F2; // accent
$cyan-hi: #58E1F7; // hover
$cyan-deep: #3AC4DE; // gradient dark end (bars)
$ink: #0A161A; // text on cyan
// --- surfaces ---
$panel-bg: rgba( 15, 17, 21, 0.90 ); // glass card
$panel-bg-strong: rgba( 15, 17, 21, 0.94 ); // modal card
$panel-bg-soft: rgba( 15, 17, 21, 0.75 ); // hint bar
$border: rgba( 255, 255, 255, 0.07 );
$border-hi: rgba( 255, 255, 255, 0.10 );
$border-hi2: rgba( 255, 255, 255, 0.12 );
$track: rgba( 255, 255, 255, 0.12 ); // slider/bar background
$track-soft: rgba( 255, 255, 255, 0.10 );
$fill-soft: rgba( 255, 255, 255, 0.05 ); // inset control bg
$fill-soft2: rgba( 255, 255, 255, 0.06 );
// --- text ---
// OWNER CONTRAST RULE (2026-07-17): no dim gray instructional text — tips, hints, keycaps,
// and labels must be white/bright. Hierarchy comes from size/weight/uppercase, NOT dimness.
// text-body/label/ghost formerly held mid/dark grays (#A6ADB8/#8B93A0/#6E7480) that read as
// "disabled"; all three now match text-hi. Kept as separate tokens (not collapsed to one) so
// semantic intent stays greppable even though the values are currently identical.
$text-hi: #F2F4F7; // headings / hero numerals
$text: #E8EAED; // body values
$text-body: #F2F4F7; // secondary body (was #A6ADB8)
$text-label: #F2F4F7; // labels (was #8B93A0)
$text-ghost: #F2F4F7; // mono captions / kbd (was #6E7480)
// --- state (house code: grip green / working yellow / sliding red / air grey) ---
$grip: #8DF06B; // grip · PASS
$working: #F2C94A; // working · WARN · out-of-band
$sliding: #FF6B6B; // sliding · FAIL · limiter
$air: #464C56; // airborne
$ink-pass: #0F1A0C; // text on green
$ink-fail: #1A0C0C; // text on red
// --- elevation / shape ---
$shadow: 0 20px 60px rgba( 0, 0, 0, 0.6 );
$radius-card: 18px;
$radius-mid: 16px;
$radius-chip: 12px;
$radius-sm: 10px;
$radius-pill: 99px;
// --- type ---
// Instrument Sans -> Poppins (proven engine face in this repo); JetBrains Mono
// -> "Roboto Mono" (design-note substitute, engine precedent, glyph-safe).
$font: Poppins, sans-serif;
$mono: "Roboto Mono", monospace;