/* KarateShowCase design tokens — the platform layer.
 *
 * ONE identity, from the public homepage through every dojo: red, white, blue
 * and navy, bright and light, with a dojo's own accent layered ON TOP rather
 * than replacing it. Layouts, spacing, type, elevation, radius and motion come
 * from here and are the same everywhere. A dojo brings its logo, its hero
 * imagery and its accent colour; it does not bring its own design system.
 *
 * THREE LAYERS, and the separation is the whole point:
 *
 *   1. PRIMITIVES   --ks-*      raw brand values. Never used directly in a
 *                               component; they exist to be referenced below.
 *   2. SEMANTICS    --surface,  what a component actually asks for. A card
 *                   --ink,      wants "the raised surface", not "#FFFFFF".
 *                   --accent    Swap a semantic and the whole platform moves.
 *   3. TENANT       --tn-*      a dojo's accent, set per tenant, which feeds
 *                               the semantic layer. Set in
 *                               ks-tenant-theme.v1.css.
 *
 * A component that reads a primitive directly is a bug: it cannot be themed.
 */

:root {
  /* ══ 1 · PRIMITIVES ═════════════════════════════════════════════════════ */

  /* brand — taken from the KarateShowCase mark */
  --ks-red: #E11133;
  --ks-red-deep: #B00C27;
  --ks-red-bright: #FF2449;
  --ks-blue: #1552D8;
  --ks-blue-deep: #0F3AA0;
  --ks-blue-bright: #2E6BF0;
  --ks-navy: #0C1730;
  --ks-navy-2: #14213C;
  --ks-navy-3: #1D2E4E;

  /* neutrals — one cool ramp; the platform is light */
  --ks-white: #FFFFFF;
  --ks-n-50: #F7F8FB;
  --ks-n-100: #EEF1F6;
  --ks-n-200: #DFE4EC;
  --ks-n-300: #C9D1DE;
  --ks-n-400: #8C99AF;
  --ks-n-500: #64738D;
  --ks-n-700: #3A4860;
  --ks-n-900: #0A1020;

  /* warm neutrals — reserved for a dojo's PUBLIC page, so the front door can
     feel different from the workspace without leaving the system */
  --ks-warm: #FBF9F5;
  --ks-warm-2: #F4EFE7;
  --ks-warm-line: #E7E0D5;

  /* status */
  --ks-ok: #0F7B4F;
  --ks-warn: #A85B00;
  --ks-danger: var(--ks-red-deep);

  /* ══ 2 · SEMANTICS — what components ask for ════════════════════════════ */

  --surface: var(--ks-white);          /* the page                           */
  --surface-sunken: var(--ks-n-50);    /* a band that steps back             */
  --surface-raised: var(--ks-white);   /* a card                             */
  --surface-inverse: var(--ks-navy);   /* the one dark band                  */

  --ink: var(--ks-n-900);              /* body text                          */
  --ink-2: var(--ks-n-700);            /* secondary                          */
  --ink-3: var(--ks-n-500);            /* tertiary, labels                   */
  --ink-4: var(--ks-n-400);            /* placeholder                        */
  --ink-inverse: var(--ks-white);

  --line: var(--ks-n-200);
  --line-strong: var(--ks-n-300);

  /* ACCENT — the one semantic a dojo may move. Defaults to the platform's
     own blue, so an untheme'd tenant is on-brand rather than unstyled. */
  --accent: var(--tn-accent, var(--ks-blue));
  --accent-deep: var(--tn-accent-deep, var(--ks-blue-deep));
  --accent-soft: color-mix(in oklab, var(--accent) 10%, var(--ks-white));

  /* Text ON the accent, derived rather than trusted. A dojo picking a pale
     gold would otherwise ship white-on-yellow buttons nobody can read. The
     relative-colour form flips to dark text once the accent's lightness passes
     0.62; the fallback below assumes white, which is correct for every colour
     dark enough to be a sane brand accent. */
  --accent-ink: var(--ks-white);

  /* THE ACCENT PLAYS THREE ROLES AND ONLY ONE OF THEM IS THE RAW COLOUR.
     A fill can be any lightness — --accent-ink adapts to sit on it. Text is
     the hard case, because text must clear 4.5:1 against whatever is behind
     it, and the accent alone cannot promise that on either surface:

       --accent            the fill. Buttons, active states, washes.
       --accent-on-inverse the accent AS TEXT on the dark band. Lightness is
                           floored — #1552D8 on #0C1730 is 1.6:1, unreadable.
       --accent-on-surface the accent AS TEXT on the light page. Lightness is
                           capped — a pale gold on white is 1.9:1, equally bad.

     Both are derived rather than trusted, exactly as --accent-ink is. A dojo
     cannot pick a colour that makes its own labels disappear. */
  --accent-on-inverse: color-mix(in oklab, var(--accent) 55%, var(--ks-white));
  --accent-on-surface: color-mix(in oklab, var(--accent) 70%, #000000);

  /* RED IS RESERVED. It means deadline, destructive, or the primary ask — so a
     dojo's accent never becomes red by accident, and a red thing on screen
     always means the same thing.

     PRIMARY IS NOT DANGER. --primary is the brand red and is FILLED: Enter,
     Save, Join. Danger is a darker oxblood and is OUTLINED by default, with an
     icon, because the shape gap survives a colour-blind user and a hurried
     glance where a hue gap does not. Danger is deliberately NOT declared here
     yet: the only --danger on the platform today is the legacy #D64545 in the
     tenant foundation layer, which loads after this file and would shadow
     anything declared here. Introducing it means settling its treatment on the
     inverse surface, which belongs with the button system rather than the nav.
     A token that is silently overridden is worse than no token. */
  --primary: var(--ks-red);
  --primary-deep: var(--ks-red-deep);
  --primary-soft: #FFEBEF;

  /* ══ elevation — ring + contact + navy-tinted ambient ═══════════════════
     Black shadows on a light page read as dirt; tinted ones read as light in
     a room. Every level also carries a 1px top highlight, which is what makes
     a rectangle look like an object. */
  --ks-ring: 0 0 0 1px rgba(12,23,48,.06);
  --ks-e1: var(--ks-ring), 0 1px 2px rgba(12,23,48,.05), 0 2px 8px -3px rgba(12,23,48,.08);
  --ks-e2: var(--ks-ring), 0 2px 4px rgba(12,23,48,.05), 0 14px 34px -14px rgba(12,23,48,.20);
  --ks-e3: var(--ks-ring), 0 4px 8px rgba(12,23,48,.07), 0 32px 64px -26px rgba(12,23,48,.32);
  --ks-e4: 0 0 0 1px rgba(12,23,48,.10), 0 8px 16px rgba(12,23,48,.10), 0 56px 100px -36px rgba(12,23,48,.45);
  --ks-e-dark: 0 0 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(0,0,0,.30), 0 48px 90px -34px rgba(0,0,0,.62);
  --ks-sheen: inset 0 1px 0 rgba(255,255,255,.9);
  --ks-sheen-dark: inset 0 1px 0 rgba(255,255,255,.10);

  /* ══ radius ═════════════════════════════════════════════════════════════ */
  --ks-r-xs: 6px;
  --ks-r-sm: 9px;
  --ks-r-md: 12px;
  --ks-r-lg: 16px;
  --ks-r-xl: 22px;
  --ks-r-pill: 999px;

  /* CONTROLS GET A SEMANTIC, NOT A STEP, and the measurement is why.

     A control — a button, an input, a select — is the most repeated object on
     any screen, so its corner is the single loudest statement the design system
     makes. On 2026-08-31 the platform had SEVENTY-FIVE non-pill control radius
     declarations across 34 stylesheets, using TWENTY-TWO distinct values: 2, 3,
     4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 18, 20 and 30px, plus six competing token
     names for the same idea. One page — the demo dojo's home — rendered .btn at
     3px, .ks-btn at 4px, another .btn at 8px and modals at 6px and 16px, all at
     once. That is not a scale; it is what a cascade does when every component
     picks its own corner and nothing names the right one.

     The cause was that there was no obvious name to reach for. --ks-r-sm is a
     STEP; it says how round, not what for, so an author choosing between it and
     --radius-sm (a different family, 4px, same word) has no reason to prefer
     either. These two say what for, which is the layer a component is supposed
     to ask at — the same reason --surface exists instead of #FFFFFF.

     TWO STEPS, because controls genuinely come in two densities and pretending
     otherwise would just push dense toolbars back into ad-hoc values.
     Deliberately no third: a pill is --ks-r-pill and is a different statement. */
  --ks-r-control: var(--ks-r-sm);      /* buttons, inputs, selects — the default */
  --ks-r-control-sm: var(--ks-r-xs);   /* dense: table rows, toolbars, chips-that-are-not-pills */

  /* ══ type ═══════════════════════════════════════════════════════════════
     One type system platform-wide. Raleway for display, system stack for text.
     A serif display face was retired from the platform layer deliberately:
     inscriptional serifs read ceremonial, and the brief is Apple/Tesla. */
  --ks-display: 'Raleway', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --ks-text: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --ks-t-hero: clamp(2.6rem, 5.4vw, 4.4rem);
  --ks-t-h2: clamp(1.9rem, 3.6vw, 2.9rem);
  --ks-t-h3: clamp(1.15rem, 1.6vw, 1.35rem);
  --ks-t-lead: clamp(1.08rem, 1.3vw, 1.22rem);
  --ks-t-body: 1.0625rem;
  --ks-t-sm: .92rem;
  --ks-t-xs: .8rem;
  --ks-t-label: .68rem;
  --ks-track-hero: -.028em;
  --ks-track-h2: -.022em;
  --ks-track-label: .1em;

  /* ══ space — one scale, so gaps across the platform agree ═══════════════ */
  --ks-s-1: .25rem;  --ks-s-2: .5rem;   --ks-s-3: .75rem;  --ks-s-4: 1rem;
  --ks-s-5: 1.5rem;  --ks-s-6: 2rem;    --ks-s-7: 3rem;    --ks-s-8: 4rem;
  --ks-s-section: clamp(3.4rem, 6.5vw, 5.6rem);

  /* ══ layers — named, so nothing reaches for 9999 again ══════════════════ */
  --ks-z-base: 1;
  --ks-z-sticky: 60;
  --ks-z-dropdown: 200;
  --ks-z-overlay: 800;
  --ks-z-modal: 900;
  --ks-z-toast: 1000;

  /* ══ motion ═════════════════════════════════════════════════════════════ */
  --ks-ease: cubic-bezier(.2, .7, .3, 1);
  --ks-ease-out: cubic-bezier(.16, 1, .3, 1);
  --ks-fast: .16s;
  --ks-med: .28s;
  --ks-slow: .5s;
}

/* Derive readable text on ANY accent a dojo chooses. Guarded, because where
   relative colour syntax is missing the fallback above already stands. */
@supports (color: oklch(from #000 l c h)) {
  :root {
    /* The switchover is 0.57, not 0.62. Above the crossover white ink stops
       clearing 4.5:1 well before 0.62 does: a mid grey at l≈0.60 measured
       3.95:1 and a hot pink at l≈0.58 measured 4.42:1, both shipped with white
       labels. Sweeping lightness shows white safe up to ~0.57 and black safe
       from ~0.57 — there is no gap, only a threshold that was set too high.
       Guarded by verify-accent-contract-contrast.mjs, which tests both sides
       of the boundary. */
    /* The runtime's answer first, this threshold second.
       A lightness threshold cannot separate every accent, and that is a model
       error rather than a badly chosen number: #E11133 and #7A7A7A share an
       oklch lightness of 0.578 and need OPPOSITE inks -- white reaches 4.85:1
       on the red and 4.29:1 on the grey. Perceptual lightness and WCAG relative
       luminance diverge once chroma is high, so no threshold sits between them.

       Luminance decides exactly, and the branding runtime already holds the
       accent, so it publishes the winner as --tn-accent-ink. This stays as the
       fallback: it is right for most accents and wrong only in a narrow band,
       which is the correct shape for a fallback. */
    --accent-ink: var(--tn-accent-ink, oklch(from var(--accent) clamp(0, (0.57 - l) * 1000, 1) 0 0));

    /* Floor or cap the lightness and keep the hue, so a dojo's colour stays
       recognisably its own rather than washing to grey. An accent already in
       the safe range passes through untouched. */
    --accent-on-inverse: oklch(from var(--accent) max(l, 0.78) c h);
    --accent-on-surface: oklch(from var(--accent) min(l, 0.45) c h);
  }
}

/* Motion tokens collapse rather than each component remembering to. Anything
   built on these is reduced-motion correct by construction. */
@media (prefers-reduced-motion: reduce) {
  :root { --ks-fast: 0s; --ks-med: 0s; --ks-slow: 0s; }
}
