/* ===================================================================
   The Bloc design tokens
   The ONLY file in this tree permitted to contain a colour literal.
   tests/test_token_contract.py enforces that.

   Two forms of every colour:
     --c-<name>  space-separated RGB channels. Use for alpha:
                 rgb(var(--c-bloc-500) / .12). Tailwind's config reads
                 these so `bg-danger-900/50` keeps working.
     --<name>    a ready colour, for the common opaque case.

   This is the indigo. Spec values come from
   docs/superpowers/specs/2026-08-17-panel-redesign-design.md §2; the
   remaining steps are derived so the light text shade and dark fill the
   templates pair together keep their contrast on indigo. The orange is
   retired. base.html's Tailwind config reads these channels, so a
   utility class and a var() can no longer disagree.
   =================================================================== */
/* --- Self-hosted faces ---------------------------------------------
   Declared here because tokens.css is the one stylesheet every page
   loads, authenticated or not. base.html used to @import Space Grotesk
   from Google on every page load: render-blocking, and it told Google
   the IP of everyone who opened a family server's panel.

   The url() is relative to this file, so it does NOT pass through
   url_for and does NOT get app/assets.py's ?v= hash. That matches what
   dashboard.css and public.css already did; the font files never
   change. ------------------------------------------------------------ */
@font-face {
    font-family: 'Space Grotesk';
    src: url('../fonts/space-grotesk-var.woff2') format('woff2-variations'),
         url('../fonts/space-grotesk-var.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Overpass Mono';
    src: url('../fonts/overpass-mono-var.woff2') format('woff2-variations'),
         url('../fonts/overpass-mono-var.woff2') format('woff2');
    font-weight: 300 700;
    font-style: normal;
    font-display: swap;
}

:root {
    /* --- Surface scale: the indigo neutral, named for elevation ------
       Replaces the old near-black ground. Every structural fill and
       border is a flat step now, not a saturated blue at low alpha:
       a desaturated tint over indigo is very nearly invisible. */
    --c-surface-950:  20  20  43;   /* #14142b  sidebar, wells, deepest ground */
    --c-surface-900:  25  25  52;   /* #191934  page background */
    --c-surface-800:  35  34  71;   /* #232247  cards, panels, raised surface */
    --c-surface-700:  42  41  82;   /* #2a2952  inputs, hover states */
    --c-surface-600:  48  47  92;   /* #302f5c  borders */
    --c-surface-500:  61  59 112;   /* #3d3b70  dividers on raised surfaces */
    --c-surface-400: 117 115 158;   /* #75739e  disabled text, placeholders */
    --c-surface-300: 155 153 198;   /* #9b99c6  secondary text */
    --c-surface-100: 233 232 246;   /* #e9e8f6  primary text */
    /* Pure white, and the reason it is a token rather than a keyword.
       Thirteen filled primaries in the markup are inked with Tailwind's
       `text-white`, so white is already in this panel's vocabulary — it
       just had no name in CSS, which forced the two filled controls that
       live in a stylesheet (`.shell-ctl--primary`, `.nav-item--active`)
       onto surface-100 instead. On bloc-600 that is 4.00:1 and white is
       4.84:1: a barely visible difference in ink that decides whether the
       control passes the 4.5:1 text floor. One vocabulary needs one name. */
    --c-white:       255 255 255;   /* #ffffff  ink on a filled control */

    /* --- Accent ------------------------------------------------------
       bloc-800 is deliberately the bloc-600 value: `border-bloc-800` is
       a border over `bg-bloc-900/30`, where a true 800 measures 1.16:1.
       These numbers are legacy addresses, not a lightness ordering. */
    --c-bloc-200: 206 201 248;  /* #cec9f8  hover text */
    --c-bloc-300: 167 157 250;  /* #a79dfa  accent text on dark, links */
    --c-bloc-400: 139 127 240;  /* #8b7ff0 */
    /* 500 is NOT a fill for anything carrying words. White on it reads 3.93:1
       and surface-100 reads 3.24:1, both under the 4.5:1 text floor, and the
       only ink that clears it is surface-950 at 4.59:1 — a margin of 0.09,
       which one retune erases. It stays as the brand hue, the stat-icon square
       (a graphic, 3:1) and the mark chip (dark ink, 4.59:1). Filled controls
       carrying text use 600/700 below. */
    --c-bloc-500: 124 110 240;  /* #7c6ef0  brand hue, graphics, borders */
    --c-bloc-600: 109  92 231;  /* #6d5ce7  filled control at rest (white, 4.84:1) */
    --c-bloc-700:  79  63 208;  /* #4f3fd0  filled control on hover (white, 7.11:1);
                                   also the gradient partner for brand marks */
    --c-bloc-800: 109  92 231;  /* #6d5ce7  == 600, see note above */
    --c-bloc-900:  28  21  80;  /* #1c1550  deep tint, only as bg-bloc-900/30 */

    /* Five sites still write rgb(var(--c-accent) / a). Accent tints keep
       their alpha — unlike the structure blue this palette replaced, an
       accent tint over indigo still reads. */
    --c-accent: var(--c-bloc-500);

    /* --- Semantic: state only, never decorative --------------------- */
    --c-success-300: 180 228 187;  /* #b4e4bb */
    --c-success-400: 143 214 153;  /* #8fd699 */
    --c-success-500:  63 185  80;  /* #3fb950 */
    --c-success-600:  54 160  69;  /* #36a045 */
    --c-success-700:  43 126  54;  /* #2b7e36 */
    --c-success-800:  31  91  39;  /* #1f5b27 */
    --c-success-900:  26  76  33;  /* #1a4c21 */

    --c-warning-300: 239 216 169;  /* #efd8a9 */
    --c-warning-400: 231 197 126;  /* #e7c57e */
    --c-warning-500: 210 153  34;  /* #d29922 */
    --c-warning-600: 184 134  30;  /* #b8861e */
    --c-warning-700: 145 106  23;  /* #916a17 */
    --c-warning-800: 105  77  17;  /* #694d11 */
    --c-warning-900:  88  64  14;  /* #58400e */

    --c-danger-300: 240 168 170;   /* #f0a8aa */
    --c-danger-400: 233 124 127;   /* #e97c7f */
    --c-danger-500: 229  72  77;   /* #e5484d */
    --c-danger-600: 188  27  32;   /* #bc1b20 */
    --c-danger-700: 147  21  25;   /* #931519 */
    --c-danger-800: 107  15  18;   /* #6b0f12 */
    --c-danger-900:  89  13  15;   /* #590d0f */

    --c-info-300: 168 209 240;     /* #a8d1f0 */
    --c-info-400: 125 185 232;     /* #7db9e8 */
    --c-info-500:  90 169 230;     /* #5aa9e6 */
    --c-info-600:  28 117 186;     /* #1c75ba */
    --c-info-700:  22  92 146;     /* #165c92 */
    --c-info-800:  16  67 106;     /* #10436a */
    --c-info-900:  13  56  89;     /* #0d3859 */

    /* Channel form of each family's 500 — dashboard.css writes
       rgb(var(--c-danger) / a) and rgb(var(--c-warning) / a). */
    --c-success: var(--c-success-500);
    --c-warning: var(--c-warning-500);
    --c-danger:  var(--c-danger-500);
    --c-info:    var(--c-info-500);

    /* --- Roles ------------------------------------------------------- */
    --ground:    rgb(var(--c-surface-900));
    --text:      rgb(var(--c-surface-100));
    --muted:     rgb(var(--c-surface-300));
    --accent:    rgb(var(--c-bloc-500));

    /* The lighter accent, for text and hover borders on a dark surface. Lives
       here rather than in the five scopes that used to declare it: it is a
       role, and the shell (sidebar, top bar, mobile drawer) is inside none of
       them, so a scoped-only declaration left every shell consumer computing
       black. */
    --accent-secondary: var(--bloc-300);

    /* The accent as a ROLE, hoisted on the same reasoning and the same
       measurement: five scopes (.bloc-dashboard, .bloc-crash, .bloc-podium,
       .bloc-quests, .bloc-public) each declared exactly `var(--accent)` and
       nothing anywhere declared anything else, so one declaration here is the
       colour all 27 call sites were already painting. shell.css reaches for
       `--accent` directly rather than this, because until now it had to. */
    --accent-primary: var(--accent);

    --border:        rgb(var(--c-surface-600));
    --border-strong: rgb(var(--c-surface-500));

    --success: rgb(var(--c-success-500));
    --warning: rgb(var(--c-warning-500));
    --danger:  rgb(var(--c-danger-500));
    --info:    rgb(var(--c-info-500));

    --success-soft: rgb(var(--c-success-500) / .16);
    --warning-soft: rgb(var(--c-warning-500) / .16);
    --danger-soft:  rgb(var(--c-danger-500) / .16);
    --info-soft:    rgb(var(--c-info-500) / .16);


    /* --- Ready colours for the scale --------------------------------- */
    --surface-950: rgb(var(--c-surface-950));
    --surface-900: rgb(var(--c-surface-900));
    --surface-800: rgb(var(--c-surface-800));
    --surface-700: rgb(var(--c-surface-700));
    --surface-600: rgb(var(--c-surface-600));
    --surface-500: rgb(var(--c-surface-500));
    --surface-400: rgb(var(--c-surface-400));
    --surface-300: rgb(var(--c-surface-300));
    --surface-100: rgb(var(--c-surface-100));
    --white:       rgb(var(--c-white));

    /* Ready forms of the categorical hues. Both forms per house rule; the
       channel form is what base.html's config reads for `bg-cat-blue-900/50`,
       and these are here for the opaque case a stylesheet may need. */
    --cat-blue-300:   rgb(var(--c-cat-blue-300));
    --cat-blue-400:   rgb(var(--c-cat-blue-400));
    --cat-blue-800:   rgb(var(--c-cat-blue-800));
    --cat-blue-900:   rgb(var(--c-cat-blue-900));
    --cat-yellow-300: rgb(var(--c-cat-yellow-300));
    --cat-yellow-400: rgb(var(--c-cat-yellow-400));
    --cat-yellow-800: rgb(var(--c-cat-yellow-800));
    --cat-yellow-900: rgb(var(--c-cat-yellow-900));
    --cat-purple-400: rgb(var(--c-cat-purple-400));
    --cat-purple-900: rgb(var(--c-cat-purple-900));
    --cat-cyan-400:   rgb(var(--c-cat-cyan-400));
    --cat-cyan-900:   rgb(var(--c-cat-cyan-900));
    --cat-green-400:  rgb(var(--c-cat-green-400));
    --cat-green-900:  rgb(var(--c-cat-green-900));
    --cat-orange-400: rgb(var(--c-cat-orange-400));
    --cat-orange-900: rgb(var(--c-cat-orange-900));

    --bloc-300: rgb(var(--c-bloc-300));
    --bloc-500: rgb(var(--c-bloc-500));
    --bloc-600: rgb(var(--c-bloc-600));
    --bloc-700: rgb(var(--c-bloc-700));

    /* Shape. Milo chose these against a rendered A/B on 2026-08-20;
       dashboard.css was square everywhere before it. Defined here and
       consumed in Plan C, so nothing changes shape in this plan. */
    --radius-card: 14px;
    --radius-control: 10px;
    /* 9999px, not the 999px this was, and the number is the whole point.
       `rounded-full` is Tailwind's own utility, it is 9999px, and 15 sites
       across the templates carry it — avatars, status dots, pills. This token
       said 999px, so the panel had two spellings of "fully round". Both round
       any real element completely, so nothing on screen moves; what goes is the
       chance of a future reader treating them as two different decisions.
       Renaming 15 call sites onto the token would have cost more and shown the
       same screen, so the token moved to the utility instead. */
    --radius-pill: 9999px;

    /* --- Off-palette colours ------------------------------------------
       These are NOT part of the scale: each one carries meaning that the
       surface steps cannot, so they keep their values through the flip. */

    /* Leaderboard medals — .bloc-podium */
    --c-gold:   244 201 93;  /* #f4c95d */
    --c-silver: 200 208 216; /* #c8d0d8 */
    --c-bronze: 183 121 74;  /* #b7794a */
    --gold:   rgb(var(--c-gold));
    --silver: rgb(var(--c-silver));
    --bronze: rgb(var(--c-bronze));

    /* Categorical identity hues — the 2026-05 decision, recorded here because
       spec §2's palette has no room for it and taken literally would erase it.
       Semantic means STATE (danger, warning, success, info); these six mean
       IDENTITY. The audit log encodes six action types and four semantic
       families cannot carry six meanings, so login-blue and backup-purple say
       "which kind", never "how bad". NOT semantic. NOT for state. Nothing here
       may be reached for to colour an outcome.

       The values are the stock Tailwind shades the badges have always rendered,
       MEASURED off /audit/ and /scheduler/ in a browser on 2026-08-21 rather
       than copied from a palette table: Ruling C4 moves them at their current
       values, so the audit log looks the same the day after as the day before.
       Stock `blue` is a family this config never declared, which is exactly why
       these had to become tokens — invisible to the token guard, invisible to
       the shade guard, and impossible to retheme.

       The steps are the ones the panel actually paints and no more. blue and
       yellow carry four because the scheduler's type badges use a 300 text and
       an 800 border the audit log's action badges do not. A step that is not
       here was never rendered: measure it off stock Tailwind before adding one,
       and scripts/check_tailwind_shades.py will say MISSING until you do. */
    --c-cat-blue-300:   147 197 253;  /* #93c5fd  scheduler Backup label */
    --c-cat-blue-400:    96 165 250;  /* #60a5fa  audit login */
    --c-cat-blue-800:    30  64 175;  /* #1e40af  scheduler Backup border */
    --c-cat-blue-900:    30  58 138;  /* #1e3a8a  audit login fill */
    --c-cat-yellow-300: 253 224  71;  /* #fde047  scheduler Restart label */
    --c-cat-yellow-400: 250 204  21;  /* #facc15  audit server */
    --c-cat-yellow-800: 133  77  14;  /* #854d0e  scheduler Restart border */
    --c-cat-yellow-900: 113  63  18;  /* #713f12  audit server fill */
    --c-cat-purple-400: 192 132 252;  /* #c084fc  audit backup */
    --c-cat-purple-900:  88  28 135;  /* #581c87  audit backup fill */
    --c-cat-cyan-400:    34 211 238;  /* #22d3ee  audit file */
    --c-cat-cyan-900:    22  78  99;  /* #164e63  audit file fill */
    --c-cat-green-400:   74 222 128;  /* #4ade80  audit user */
    --c-cat-green-900:   20  83  45;  /* #14532d  audit user fill */
    --c-cat-orange-400: 251 146  60;  /* #fb923c  audit schedule */
    --c-cat-orange-900: 124  45  18;  /* #7c2d12  audit schedule fill */

    /* Quest state — .bloc-quests */
    --c-quest-green:        52 211 153; /* #34d399 */
    --c-quest-green-strong: 16 185 129; /* #10b981 */
    --c-quest-blue:         96 165 250; /* #60a5fa */
    --c-quest-amber:        245 158 11; /* #f59e0b */
    --c-quest-wash:         20 184 166; /* #14b8a6  corner gradient wash */
    --quest-green:        rgb(var(--c-quest-green));
    --quest-green-strong: rgb(var(--c-quest-green-strong));
    --quest-blue:         rgb(var(--c-quest-blue));
    --quest-amber:        rgb(var(--c-quest-amber));

    /* --- Structural neutrals, now on the scale -------------------------
       These were near-black only because the ground was near-black. Left
       alone they punch black holes in an indigo panel. */

    /* Recessed wells — code blocks, podium plinths, chapter cards */
    --c-inset: var(--c-surface-950);

    /* Server-action modal chrome. Lifted off the page rather than
       matching it, which is now a step up the scale, not a darker black. */
    --c-modal-surface:  var(--c-surface-800);
    --c-modal-backdrop: var(--c-surface-950);
    --c-modal-footer:   var(--c-surface-900);
    --c-modal-hairline: var(--c-surface-100); /* close button + progress groove */
    --modal-surface: rgb(var(--c-modal-surface));

    /* Progress bar groove. Flat, not a tint: drawn at the .16 alpha it
       used to carry, surface-600 over a surface-800 card measures
       1.029:1 and the empty part of the bar disappears. */
    --c-progress-track: var(--c-surface-600);
    --progress-track: rgb(var(--c-progress-track));

    /* Drop shadows */
    --c-shadow: 0 0 0;

    /* Ink tones that are a shade of a semantic colour rather than the
       semantic colour itself — the light text shade of each family. */
    --danger-text:  rgb(var(--c-danger-300));
    --warning-text: rgb(var(--c-warning-300));
    --danger-ink:   #110f12; /* text ON a filled danger button */
    --code-text:    #d6d3c8; /* pre/code inside a well */
}
