@layer tokens {
  :root {
    /* ---- Surfaces -------------------------------------------------------
       Near-black, tilted violet rather than neutral grey, because every other
       colour in this game is an orb colour and a truly neutral ground made the
       three of them look like UI state rather than magic.

       Three levels, and level never carries meaning: it separates the card you
       are reading from the frame around it, and nothing else. */
    --void: #0e0b14; /* the page */
    --panel: #171320; /* cards, the spell frame, the reference grid */
    --raised: #221c30; /* orb slots, buttons, anything you can press */
    --edge: rgb(200 162 74 / 22%); /* gold hairline, the only border colour */

    /* ---- Ink ------------------------------------------------------------
       Measured against all three surfaces (WCAG 2.1 contrast):

         ink        void 16.87  panel 15.79  raised 14.22
         ink-soft   void  7.67  panel  7.18  raised  6.47   AA at any size
         ink-faint  void  3.69  panel  3.45  raised  3.11   large text only

       ink-faint is for the combo pips under a reference card and for disabled
       chrome. It is illegal at body size on every surface here — if a string
       has to be read under time pressure it gets ink-soft or better. */
    --ink: #f3eee4;
    --ink-soft: #a79fb4;
    --ink-faint: #6f6880;

    /* ---- Gold -----------------------------------------------------------
       Borders, the rank badge, the wordmark. void 8.10, panel 7.58,
       raised 6.83 — legal as text anywhere, which matters because the rank the
       player earned is set in it. */
    --gold: #c8a24a;
    --gold-bright: #e9c874;

    /* ---- The three orbs -------------------------------------------------
       These are the load-bearing colours: a player reads the orb tray by hue
       before they read anything else on the screen.

         quas   void 8.60  panel 8.04  raised 7.25
         wex    void 6.80  panel 6.36  raised 5.73
         exort  void 8.20  panel 7.67  raised 6.91

       All three pass AA at body size on every surface, so the orb letters can
       sit directly on a slot. Hue is never the only signal anyway — each orb
       is also labelled with its key and named in its title. */
    --orb-quas: #5fb6e0;
    --orb-wex: #c07fdd;
    --orb-exort: #f0913f;
    --orb-quas-deep: #16374b;
    --orb-wex-deep: #331546;
    --orb-exort-deep: #4d2409;

    /* ---- Outcome --------------------------------------------------------
       hit   void 9.03  panel 8.45  raised 7.61
       miss  void 6.07  panel 5.68  raised 5.12
       Both legal at body size everywhere. Neither is ever the only signal: a
       hit also swaps the icon, a miss also writes "Wrong spell" to a live
       region. */
    --hit: #63c47d;
    --miss: #f0605d;

    /* ---- Type -----------------------------------------------------------
       Dota's own Radiance is proprietary. Cinzel is the closest honest
       substitute for the display voice — inscriptional roman caps, arcane
       without tipping into novelty. Barlow carries the UI. DM Mono runs the
       clock, because a proportional face makes the digits jitter as they tick
       and the clock is on screen for the entire run.

       Every stack has real fallbacks: the game is playable with no network. */
    --font-display: "Cinzel", "Trajan Pro", "Palatino Linotype", Palatino, Georgia, serif;
    --font-ui: "Barlow", "Segoe UI", system-ui, -apple-system, sans-serif;
    --font-mono: "DM Mono", "Cascadia Mono", Consolas, ui-monospace, monospace;

    --text-hero: clamp(2.4rem, 7vw, 4.25rem);
    --text-rank: clamp(2rem, 6vw, 3.5rem);
    --text-spell: clamp(1.35rem, 3.6vw, 2rem);
    --text-clock: clamp(1.6rem, 4.5vw, 2.4rem);
    --text-body: 1rem;
    --text-small: 0.8125rem;
    --text-micro: 0.6875rem;

    /* ---- Space ----------------------------------------------------------
       A 4px base. Only these steps are used anywhere. */
    --s1: 0.25rem;
    --s2: 0.5rem;
    --s3: 0.75rem;
    --s4: 1rem;
    --s5: 1.5rem;
    --s6: 2rem;
    --s7: 3rem;

    --radius: 12px;
    --radius-sm: 8px;

    /* ---- Motion ---------------------------------------------------------
       Short, because every millisecond of animation during a run is a
       millisecond the player is waiting instead of playing. animations.css
       zeroes these under prefers-reduced-motion and substitutes a held static
       cue rather than nothing. */
    --tempo-fast: 90ms;
    --tempo: 160ms;
    --tempo-slow: 320ms;
    --ease: cubic-bezier(0.2, 0.7, 0.3, 1);

    --shadow: 0 2px 4px rgb(0 0 0 / 30%), 0 12px 32px rgb(0 0 0 / 34%);
    --glow-gold: 0 0 0 1px rgb(200 162 74 / 45%), 0 0 26px rgb(200 162 74 / 18%);
  }

  /* Per-orb theming: a slot or pip sets data-orb and reads --orb / --orb-deep,
     so no component ever branches on which orb it is showing. */
  [data-orb="Q"] {
    --orb: var(--orb-quas);
    --orb-deep: var(--orb-quas-deep);
  }
  [data-orb="W"] {
    --orb: var(--orb-wex);
    --orb-deep: var(--orb-wex-deep);
  }
  [data-orb="E"] {
    --orb: var(--orb-exort);
    --orb-deep: var(--orb-exort-deep);
  }
}
