/* ═══════════════════════════════════════════════════════════════════════════
   ARBA·EDGE — DESIGN TOKENS
   "Hangar-Grade" — aerospace operations panel × cryptographic spec sheet

   THE ONE SOURCE OF TRUTH FOR COLOUR, TYPE AND SPACING IN apps/web.

   ── WHY THIS FILE EXISTS ──────────────────────────────────────────────────
   There used to be two competing "systems". `assets/arba-system.css` defined a
   navy/instrument-cyan palette on Geist and dressed seven pages — the landing
   page, sign-in, the account page and the demonstrator — and it is genuinely
   good. `app/styles.css` defined a SECOND, unrelated palette (indigo #6366f1 on
   charcoal, Inter) and dressed twelve more. The customer portals defined a
   THIRD, inline, per generated file.

   So a customer signed in through a hangar-grade console and landed, one click
   later, on a generic indigo dashboard. Both sheets were internally consistent;
   nothing was broken; the product just looked like two products.

   This file is the merge. Both sheets now import it and neither declares a
   palette of its own. Change a value here and it moves everywhere — which is
   the property neither sheet had, and the reason the drift was invisible.

   ── WHAT IS DELIBERATE ────────────────────────────────────────────────────
   - The grounds and the ink ramp are UNCHANGED from arba-system.css. The seven
     pages that already looked right must not shift; this is a levelling-up of
     the other thirty-three, not a redesign of the good ones.
   - Indigo is GONE, not remapped to a nicer indigo. It was the generic accent
     and it fought the instrument cyan on every page that showed both.
   - `--accent` is a LEGACY ALIAS pointing at cyan. That single line re-skins
     every `var(--accent)` in app/styles.css. The aliases at the bottom are
     load-bearing for exactly that reason — do not "tidy" them away until the
     selectors that read them are gone.

   ── COLOUR IS SIGNAL, NOT DECORATION ──────────────────────────────────────
   Four meanings, and nothing else may borrow them:
     cyan   → interactive. Links, focus, the thing you can click.
     amber  → the primary action, and attention. High-vis, as on a flight line.
     green  → verified. A record that checked out.
     red    → failed. A record that did not.
   `--warn` is deliberately NOT the CTA amber: a warning next to a primary
   button must not read as a second button.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Archivo carries the display lockups (it has real width and a technical
   bearing at 44px+); IBM Plex Sans is the reading and UI face; IBM Plex Mono is
   reserved for EVIDENCE — hashes, IDs, timestamps, counts, anything a customer
   might have to read back to someone. Plex Sans and Plex Mono are a designed
   pair, which is why the mono sits in dense tables without looking pasted in.
   Inter is not here on purpose: it was on 23 pages and is the house face of
   nothing. */
@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@300;400;500;600&family=IBM+Plex+Sans:wght@300;400;500;600;700&display=swap');

:root {
  /* ── surfaces ─────────────────────────────────────────────────────────── */
  --bg-deep:      #070b14;
  --bg:           #0b1220;
  --bg-surface:   #0f1729;
  --bg-surface-2: #131d35;
  --bg-rib:       #1a2541;

  /* ── ink ──────────────────────────────────────────────────────────────── */
  --ink:     #e8f0ff;
  --ink-2:   #a9b8d9;
  /* --ink-3 was #7183a9, which measured 4.40:1 on --bg-surface-2 — under the
     4.5:1 floor, and it is the ramp step the micro-labels use: telemetry units,
     column heads, the access-gate message, placeholder text. It failed on the
     SECOND-level panel only, which is why it survived a first pass that checked
     it against --bg. This value clears every ground it is used on:
     5.09:1 on --bg-surface-2, 5.43:1 on --bg-surface, 5.98:1 on --bg-deep. */
  --ink-3:   #7c8eb4;
  /* --ink-4 is DECORATION AND NOTHING ELSE — 2.78:1 on --bg-surface. Registration
     marks, rules, disabled outlines. Never put a word a customer has to read on
     it; the step above is --ink-3 and it is there for exactly that. */
  --ink-4:   #4f5f7d;

  /* ── hairlines ────────────────────────────────────────────────────────── */
  /* The engineering grid is drawn at HALF a hairline. At --hair it stops being
     a texture and becomes graph paper competing with the content — the landing
     page has always used 0.035 and this is that value, named.
     No apps/web page draws the grid any more but the public Terms of Use and
     Privacy Policy (legal/terms.html and privacy.html): the rest sit on
     --page-backdrop, below. Those two are tenant portal templates rendered
     (apps/api/templates/portal/), and they draw the grid, as the templates
     do, until the portals' own release, so --grid-line and --grid-cell stay
     for them and for the templates. */
  --grid-line: rgba(227,237,255,0.035);
  --hair:    rgba(227,237,255,0.07);
  --hair-2:  rgba(227,237,255,0.14);
  --hair-3:  rgba(227,237,255,0.22);

  /* ── page backdrop ────────────────────────────────────────────────────────
     The ground every apps/web page sits on, in place of the engineering grid
     (the owner, 11 October 2026: "I think we should put this new background
     everywhere instead of the blue grid"). Dark: a sunrise over a planet's
     limb under a star field, DRAWN here in gradients and one small SVG (the
     owner, 13 October 2026: "Can we make the similar background with CSS or
     SVG?"), under a scrim of --bg. Light: none, so the page is plain --bg
     (the light block below). A page reads --page-backdrop and nothing else,
     on a layer of its own, and gives body plain --bg under it:
       body { background: var(--bg); }
       body::before {
         content: ""; position: fixed; inset: 0; z-index: -1;
         pointer-events: none; background: var(--page-backdrop, none);
         will-change: transform;
       }
     The layer is fixed to the window and never repainted while the page
     scrolls over it. The scene was a stack of fixed BACKGROUNDS on body
     (background-attachment: fixed), and a fixed background is repainted on
     every frame of a scroll: the whole window, every layer, each frame (the
     review of aab4af7 measured the scroll cost; docs/FRONTEND-CHANGES.md
     §122). So no layer below says `fixed`: the box they are drawn in is.
     The fallback is a plain ground if this file is ever served stale. Nothing
     here is fetched: the star tile is a data: URI, so the backdrop costs no
     request and moves with this file's own ?v= stamp.

     THE SCRIM IS --bg AT 92%, AND THAT NUMBER IS A CONTRAST BUDGET. Words sit
     on this ground directly (eyebrows, footers, the landing page's lede) at
     every ink step down to --ink-3, and the ground is fixed, so once a page
     scrolls any word can pass over any pixel. The brightest pixel the drawing
     has is white, the sun's core. Under 92% of --bg it comes out #1e2531, and
     --ink-3 on that is 4.69:1; at 90% it would be 4.42:1, under the floor.
     Every signal colour clears 5.5:1 there. Spelled as rgba() for the reason
     the washes are. test/web-page-backdrop.test.js redoes this sum from the
     values in this file. It holds everywhere but the band, below. The stars
     stop at 80% of --ink, so --ink-3 over one keeps 4.97:1 under 92%, and
     4.5:1 down to 88%.

     THE BAND SPENDS THAT BUDGET, ON PURPOSE, ON THREE PAGES, AND ONLY WHERE
     NO WORD OF THEIRS SITS. Around the sunrise the scrim can open to
     --backdrop-band, --bg at 35%, so the sun, the planet's lit rim and the
     stars beside them show, and over the core no ink step passes. It opens
     only on a page that asks for it, with data-backdrop="band" on its <html>:
     the landing page (index.html), sign-in (app/index.html) and the public
     verifier (app/verify/index.html), the three it was placed for. Everywhere
     else it is SHUT, the scrim's 92% across the whole window (the defaults
     below): the band was first open on every page, and pages whose words
     reach the window's lower part at rest lost them to the sun, down to
     1.00:1 (Verification History with records, both Validation Records
     pages, AE-PX2's Access Mode line; the review of 29345ec). A page that
     opts in must keep its words out of the band's rows at rest, at every
     window size, as these three do: the public verifier's footer ends 601px
     from the top whatever the window's height, the landing page's hero is
     centred and ends 176px below the window's middle, and the landing page's
     and sign-in's footers start 41px above its bottom (measured at 34 window
     sizes, 360px to 3440px wide, 657px to 1440px tall). The band keeps 16px
     clear of each:
       it ends 57px above the window's bottom (--backdrop-band-end);
       it is --backdrop-band-h tall: 0.10 W, or the room left where that is
         less, 100vh - 674px under the verifier's footer (601 + 16 + 57) and
         50vh - 249px under the hero (176 + 16 + 57);
       the scrim opens in an ellipse that fills it: as tall as the band and as
         wide as the scene (W), --bg at 35% across its inner 40%, back to 92%
         at its edge, so the band closes sideways too, the rim fades away from
         the sun, and no stripe of brighter stars crosses the window;
       the flare sits 0.6 of the band up from its end, so the rim's crest,
         0.02 W below the flare, clears the landing page's footer bar.
     On those three, where less than 80px is left (a window under 754px
     tall), on a phone, 560px and narrower, and for a visitor who asks for
     more contrast, the band stays shut (the rules after the light block). A
     word that scrolls into the band loses its contrast while it is there.

     WHAT IS DRAWN. W, --backdrop-width, is the scene's width: the window's,
     or 125vh where the window is narrower than 5:4. Every size below is a
     fraction of W, so the scene keeps its proportions at any size, and they
     are the photo's this drawing replaced (1672 x 941, measured). The flare
     sits on the window's centre line, in the band.
     Top layer first, all of them on the one fixed layer:
       the glare (--backdrop-glare), the core's white-hot point, 0.032 W
         across, or less where the band is thin, so that it stays inside it
         (0.4 of the band, the room above the sun): the one layer over the
         scrim, and only where the band is fully open, since under the band's
         35% no core can be brighter than (170,172,177), a grey disc; where the
         band is soft or shut, and on every page that does not open it, it is
         none;
       the scrim, opening in the band;
       the ring, 0.083 W from the core: the photo's faint red-pink circle;
       the sun: white for its first 3%, blooming in --ink to 0.06 W, slowly,
         so that it reads as glare and not as a disc;
       the haze over the crest, --ink fading through --ink-2: the lit
         atmosphere, over the planet's edge and just inside it;
       the rim: a line of --ink at the limb, --ink-2 haze fading into the
         body, a --cyan glow fading into space;
       the planet: a disc of --bg-deep, radius 0.5865 W (--backdrop-planet-r),
         its crest (--backdrop-crest-y) 0.0204 W below the core; it hides the
         stars behind it, and the horizon's glow below the limb;
       the horizon's glow: an ellipse 0.84 W by 0.064 W along the crest, --ink
         through --ink-2 to a --cyan edge, under the planet, so the light
         hugs the horizon and the limb stays sharp, as in the photo;
       the glow: a flatter ellipse, 0.6 W by 0.17 W, centred on the crest:
         the soft light over the horizon, blue-white;
       the stars: one tile, --backdrop-stars, at two scales, 360px and 523px,
         whose repeats only coincide every 188,280px;
       the sky: --bg-deep, one step deeper than --bg, which the scrim lifts
         back to --bg outside the band.
     The three lens-flare discs the first drawing had below the core are
     gone: the pink one sat on the planet's dark face and read as a smudge,
     the two cyan ones could not be seen at 1x, and the band no longer
     reaches down to them.
     Every rgba() below is a palette colour at an alpha: --ink 232,240,255,
     --ink-2 169,184,217, --cyan 125,211,252, --cyan-2 56,189,248. Two are not
     the palette's, as the photo's were not: --backdrop-sun, the core's white,
     and --backdrop-ring, the ring's red-pink, which is not --red: that one is
     a signal. The stars are --ink, spelled in the SVG, which cannot read a
     custom property. Nothing moves. */
  --backdrop-scrim:    rgba(11,18,32,0.92);    /* --bg, #0b1220, at 92% */
  --backdrop-band:     var(--backdrop-scrim);  /* shut: opened only by data-backdrop="band", below */
  --backdrop-sun:      #ffffff;                /* the core: the one colour hotter than --ink */
  --backdrop-ring:     rgba(255,122,150,0.22); /* the ring's red-pink: not --red, a signal */
  --backdrop-width:    max(100vw, 125vh);
  --backdrop-band-end: calc(100vh - 57px);
  --backdrop-band-h:   calc(0.10 * var(--backdrop-width));   /* shut, at its full height */
  --backdrop-flare-y:  calc(var(--backdrop-band-end) - 0.6 * var(--backdrop-band-h));
  --backdrop-crest-y:  calc(var(--backdrop-flare-y) + 0.0204 * var(--backdrop-width));
  --backdrop-planet-r: calc(0.5865 * var(--backdrop-width));
  --backdrop-planet-y: calc(var(--backdrop-flare-y) + 0.6069 * var(--backdrop-width));
  /* 140 dots of --ink in a 360-unit tile, in eight steps: seven of 1.42px
     dots at 20%, 30%, 40%, 50%, 60%, 70% and 80% of --ink, the faint ones the
     most numerous as in a real field (30, 26, 22, 18, 15, 14, 10), and five
     larger, 2.1px, at 80%. Every centre is on a pixel's centre at 1:1, and
     1.42px is just wide enough to cover that pixel whole, so each step's
     brightest pixel IS its alpha, in both engines, at 1x and 2x (a 1px dot
     covers about 79% of it, and the engines antialias that part
     differently: WebKit's 66% step came out at 148, Chromium's at 127).
     ONE PATH PER STEP, ITS ALPHA THE PATH'S OWN `opacity`, AND NO
     stroke-opacity ANYWHERE. A dot is a zero-length stroke with a round cap,
     and WebKit draws it as two caps, one over the other: with stroke-opacity
     each cap took the alpha and the overlap reached 96% of --ink, past the
     cap the budget above counts on (Safari, and every browser on iOS).
     `opacity` composites the path once, after its caps are drawn, so both
     engines stop at 80%: (186,192,204) over black at 1x and 2x, measured.
     One line: a data: URI the CSS parser must see whole. */
  --backdrop-stars: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 360'%3E%3Cg transform='translate(.5 .5)' stroke='%23e8f0ff' stroke-linecap='round'%3E%3Cpath stroke-width='1.42' opacity='.2' d='M353 3h0M249 14h0M217 40h0M208 58h0M38 67h0M166 72h0M324 81h0M112 110h0M143 112h0M199 115h0M164 120h0M71 129h0M203 134h0M3 141h0M231 158h0M37 162h0M198 197h0M332 199h0M62 205h0M195 206h0M112 229h0M228 239h0M29 243h0M71 257h0M153 258h0M268 266h0M323 269h0M257 317h0M301 328h0M104 348h0'/%3E%3Cpath stroke-width='1.42' opacity='.3' d='M135 7h0M173 10h0M296 10h0M37 11h0M182 23h0M18 38h0M280 45h0M183 46h0M89 60h0M297 72h0M24 77h0M100 126h0M231 130h0M93 147h0M254 153h0M69 177h0M50 188h0M140 213h0M42 216h0M119 248h0M340 251h0M103 268h0M325 286h0M263 303h0M325 317h0M210 343h0'/%3E%3Cpath stroke-width='1.42' opacity='.4' d='M33 21h0M273 35h0M235 68h0M100 84h0M252 93h0M131 104h0M338 145h0M141 175h0M98 204h0M70 217h0M327 223h0M99 232h0M205 234h0M300 237h0M211 285h0M234 289h0M352 289h0M218 308h0M337 318h0M292 325h0M322 342h0M76 350h0'/%3E%3Cpath stroke-width='1.42' opacity='.5' d='M291 58h0M120 60h0M49 109h0M45 133h0M40 146h0M18 161h0M47 162h0M126 170h0M315 171h0M73 233h0M241 234h0M353 245h0M12 246h0M201 250h0M228 305h0M193 321h0M122 330h0M311 331h0'/%3E%3Cpath stroke-width='1.42' opacity='.6' d='M54 8h0M322 10h0M20 13h0M98 51h0M56 58h0M330 62h0M335 83h0M269 92h0M133 125h0M306 158h0M310 191h0M214 198h0M46 237h0M41 249h0M278 327h0'/%3E%3Cpath stroke-width='1.42' opacity='.7' d='M310 18h0M191 25h0M312 43h0M100 97h0M263 100h0M13 110h0M265 150h0M186 162h0M151 200h0M265 246h0M229 258h0M356 270h0M172 339h0M5 342h0'/%3E%3Cpath stroke-width='1.42' opacity='.8' d='M218 27h0M75 49h0M110 72h0M316 106h0M240 170h0M172 201h0M303 250h0M22 265h0M24 307h0M289 337h0'/%3E%3Cpath stroke-width='2.1' opacity='.8' d='M40 41h0M200 96h0M218 111h0M315 130h0M191 274h0'/%3E%3C/g%3E%3C/svg%3E");
  --backdrop-glare:    none;                   /* only where the band is open, below */
  --page-backdrop:
    var(--backdrop-glare),
    radial-gradient(calc(0.5 * var(--backdrop-width)) calc(0.5 * var(--backdrop-band-h)) at 50% calc(var(--backdrop-band-end) - 0.5 * var(--backdrop-band-h)),
      var(--backdrop-band) 40%, var(--backdrop-scrim) 100%),
    radial-gradient(circle calc(0.09 * var(--backdrop-width)) at 50% var(--backdrop-flare-y),
      transparent 88.5%, var(--backdrop-ring) 92.2%, transparent 96%),
    radial-gradient(circle calc(0.06 * var(--backdrop-width)) at 50% var(--backdrop-flare-y),
      var(--backdrop-sun) 3%, rgba(232,240,255,0.85) 9%, rgba(232,240,255,0.55) 20%, rgba(232,240,255,0.28) 38%, rgba(232,240,255,0.1) 62%, transparent),
    radial-gradient(calc(0.26 * var(--backdrop-width)) calc(0.06 * var(--backdrop-width)) at 50% calc(var(--backdrop-flare-y) + 0.028 * var(--backdrop-width)),
      rgba(232,240,255,0.88), rgba(232,240,255,0.66) 30%, rgba(169,184,217,0.32) 65%, transparent),
    radial-gradient(circle var(--backdrop-planet-r) at 50% var(--backdrop-planet-y),
      transparent 93%, rgba(169,184,217,0.18) 96.5%, rgba(169,184,217,0.5) 98.7%, rgba(232,240,255,0.88) 99.65%,
      rgba(125,211,252,0.36) 100.3%, rgba(125,211,252,0.13) 101.3%, transparent 103.4%),
    radial-gradient(circle var(--backdrop-planet-r) at 50% var(--backdrop-planet-y),
      var(--bg-deep) 99.75%, transparent 100%),
    radial-gradient(calc(0.42 * var(--backdrop-width)) calc(0.032 * var(--backdrop-width)) at 50% calc(var(--backdrop-crest-y) - 0.004 * var(--backdrop-width)),
      rgba(232,240,255,0.7), rgba(232,240,255,0.45) 22%, rgba(169,184,217,0.26) 50%, rgba(125,211,252,0.08) 78%, transparent),
    radial-gradient(calc(0.3 * var(--backdrop-width)) calc(0.085 * var(--backdrop-width)) at 50% calc(var(--backdrop-crest-y) - 0.006 * var(--backdrop-width)),
      rgba(232,240,255,0.6), rgba(232,240,255,0.32) 35%, rgba(169,184,217,0.16) 55%, rgba(125,211,252,0.06) 78%, transparent),
    var(--backdrop-stars) 0 0 / 360px 360px,
    var(--backdrop-stars) 157px 211px / 523px 523px,
    linear-gradient(var(--bg-deep), var(--bg-deep));

  /* ── signal ───────────────────────────────────────────────────────────── */
  --cyan:    #7dd3fc;   /* instrument cyan — interactive */
  --cyan-2:  #38bdf8;
  --cyan-3:  #0ea5e9;
  --amber:   #f8b24b;   /* signal amber — primary action */
  --amber-2: #fbbf24;
  --green:   #4ade80;   /* verified */
  --green-2: #22c55e;
  --red:     #ff6b6b;   /* critical / failed */
  --red-2:   #ef4444;
  --warn:    #fbbf24;   /* attention that is NOT an action */
  --magenta: #e879f9;   /* accent highlight, rare use */

  /* Tinted washes. Spelled out because rgba() of a var() is not expressible in
     CSS without colour-mix, and colour-mix is newer than some of the browsers
     this ships to. */
  /* ── INK ON A FILLED ACCENT ────────────────────────────────────────────
     These are not decoration, they are the reason filled buttons are readable.
     --cyan and --amber INVERT between themes: light-on-dark-ground means a PALE
     accent, dark-on-light-ground means a DEEP one. So the ink that sits on top
     has to invert with them. It was white in both, which was survivable while
     --accent was the old indigo (white on #6366f1 is 4.46:1) and became a
     1.67:1 failure the moment --accent became pale cyan — every filled button
     on twelve pages at once. Measured: --bg-deep on --cyan is 11.80:1 dark,
     #ffffff on --cyan is 5.93:1 light. */
  --on-accent:   #070b14;
  --on-amber:    #14100a;

  --cyan-wash:   rgba(125,211,252,0.10);
  --cyan-edge:   rgba(125,211,252,0.28);
  --amber-wash:  rgba(248,178,75,0.10);
  --amber-edge:  rgba(248,178,75,0.30);
  --green-wash:  rgba(74,222,128,0.10);
  --green-edge:  rgba(74,222,128,0.30);
  --red-wash:    rgba(255,107,107,0.10);
  --red-edge:    rgba(255,107,107,0.30);

  /* ── type ─────────────────────────────────────────────────────────────── */
  --font-display: 'Archivo', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-ui:      'IBM Plex Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* Spec-doc scale. The two clamps are the only fluid steps — operational
     screens are read at a desk, so body copy does not need to breathe. */
  --fs-micro:  10.5px;
  --fs-small:  12px;
  --fs-base:   14px;
  --fs-lead:   16px;
  --fs-h4:     18px;
  --fs-h3:     22px;
  --fs-h2:     clamp(28px, 3.6vw, 42px);
  --fs-h1:     clamp(44px, 7vw, 92px);

  /* Mono eyebrows and column heads are tracked out; display lockups are tracked
     in. Both are signature and both are easy to get wrong by hand. */
  --track-eyebrow: 0.18em;
  --track-label:   0.10em;
  --track-display: -0.03em;

  /* ── layout ───────────────────────────────────────────────────────────── */
  --radius-xs: 3px;
  --radius-sm: 6px;
  --radius:    10px;
  --radius-lg: 14px;

  --frame-pad: clamp(18px, 2.2vw, 28px);
  --grid-cell: 56px;   /* the engineering grid pitch: the portals' now (see --grid-line) */

  /* ── depth ────────────────────────────────────────────────────────────── */
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.34);
  --shadow-md: 0 4px 14px rgba(0,0,0,0.36);
  --shadow-lg: 0 18px 48px rgba(0,0,0,0.46);

  /* ── timing ───────────────────────────────────────────────────────────── */
  --ease:       cubic-bezier(.22,1,.36,1);
  --ease-sharp: cubic-bezier(.3,.7,.1,1);
  --dur-fast:   .14s;
  --dur:        .22s;
  --dur-slow:   .42s;
}

/* ── LEGACY ALIASES ───────────────────────────────────────────────────────
   Every name here is READ BY SELECTORS THAT ALREADY EXIST — 107 var() calls in
   app/styles.css alone, plus the per-surface sheets. Mapping them onto the
   tokens above is what re-skins twelve pages without touching their markup.
   Deleting one of these silently drops a colour to `unset`, which on a dark
   ground usually means black text on black. Check for readers first. */
:root {
  --panel:          var(--bg-surface);
  --panel-2:        var(--bg-surface-2);
  --surface-low:    var(--bg-deep);
  --text:           var(--ink);
  --text-secondary: var(--ink-2);
  --muted:          var(--ink-2);
  --accent:         var(--cyan);     /* was #6366f1 — the indigo dies here */
  --accent-2:       var(--cyan-2);
  --border:         var(--hair);
  --border-strong:  var(--hair-2);
  --success:        var(--green);
  --good:           var(--green);
  --bad:            var(--red);
  --radius-md:      var(--radius-sm);
  --radius-lg:      var(--radius);
}

/* ═══════════════════════════════════════════════════════════════════════════
   LIGHT
   Only app/styles.css pages expose the toggle (theme.js), but the tokens are
   defined for every surface so a page that gains the toggle later does not need
   a second palette invented for it.

   NOT the dark values lightened. On a light ground the signal colours have to
   be DARKENED to stay legible — #4ade80 verified-green on white is about 1.6:1,
   which is invisible, and it is the colour that tells someone a record checked
   out. Each one here is chosen against --bg, not derived.
   ═══════════════════════════════════════════════════════════════════════════ */
[data-theme="light"] {
  --bg-deep:      #e7eaf2;
  --bg:           #eef0f6;
  --bg-surface:   #ffffff;
  --bg-surface-2: #f5f7fb;
  --bg-rib:       #e2e7f0;

  --ink:     #0a0f1a;
  --ink-2:   #33415a;
  /* Was #5b6b85 = 4.49:1 on --bg-deep — one hundredth under, which is still
     under. 5.05:1 on --bg-deep, 6.08:1 on --bg-surface, 4.90:1 on --bg-rib. */
  --ink-3:   #54637c;
  --ink-4:   #8695ad;   /* decoration only, as in dark */

  --grid-line: rgba(10,15,26,0.045);
  --hair:    rgba(10,15,26,0.09);
  --hair-2:  rgba(10,15,26,0.16);
  --hair-3:  rgba(10,15,26,0.26);

  /* No backdrop in the light theme, and no grid: the page is plain --bg (the
     owner's choice, "Photo in dark mode only", which the drawing keeps). */
  --page-backdrop: none;

  --cyan:    #0369a1;
  --cyan-2:  #0284c7;
  --cyan-3:  #075985;
  --amber:   #b45309;
  --amber-2: #92400e;
  --green:   #15803d;
  --green-2: #166534;
  --red:     #b91c1c;
  --red-2:   #991b1b;
  --warn:    #92400e;
  --magenta: #a21caf;

  /* Both accents are DEEP in this theme, so the ink on top goes white. */
  --on-accent:   #ffffff;
  --on-amber:    #ffffff;

  --cyan-wash:   rgba(3,105,161,0.08);
  --cyan-edge:   rgba(3,105,161,0.30);
  --amber-wash:  rgba(180,83,9,0.08);
  --amber-edge:  rgba(180,83,9,0.30);
  --green-wash:  rgba(21,128,61,0.08);
  --green-edge:  rgba(21,128,61,0.30);
  --red-wash:    rgba(185,28,28,0.08);
  --red-edge:    rgba(185,28,28,0.30);

  --shadow-sm: 0 1px 3px rgba(10,15,26,0.10);
  --shadow-md: 0 4px 14px rgba(10,15,26,0.12);
  --shadow-lg: 0 18px 48px rgba(10,15,26,0.16);
}

/* The band, open: only on the three pages that ask for it on their <html>,
   data-backdrop="band" (the landing page, sign-in and the public verifier;
   the page backdrop's comment above). Every rule from here to the shut ones
   names that selector, so that each wins over :root's shut defaults by its
   specificity and over the one before it by its place: open, then soft at
   960px and narrower, then shut on a short window or a phone, then shut for
   more contrast, last. --bg at 35%, 0.10 W tall or the room the three
   pages' words leave, and the core's glare inside it. */
:root[data-backdrop="band"] {
  --backdrop-band:   rgba(11,18,32,0.35);    /* --bg at 35%: the band around the sunrise */
  --backdrop-band-h: max(0px, min(0.10 * var(--backdrop-width), 100vh - 674px, 50vh - 249px));
  --backdrop-glare:
    radial-gradient(circle min(0.016 * var(--backdrop-width), 0.4 * var(--backdrop-band-h)) at 50% var(--backdrop-flare-y),
      rgba(255,255,255,0.96), rgba(255,255,255,0.75) 14%, rgba(232,240,255,0.32) 38%, rgba(232,240,255,0.08) 70%, transparent);
}

/* At 960px and narrower the landing page stacks its entry panel (frosted
   glass: 55% over a 10px blur) under the hero, into the lower screen and over
   the flare, and its --ink-3 lines cannot take the 35% band through the glass.
   There the band opens only to 82%: the sun, the glow and the rim still show,
   softly. */
@media (max-width: 960px) {
  :root[data-backdrop="band"] { --backdrop-band: rgba(11,18,32,0.82); --backdrop-glare: none; }
}

/* Shut: the scrim is 92% everywhere, and the sunrise shows only faintly
   through it. Under 754px tall less than 80px is left for the band
   (100vh - 674px), and 80px is the least that still reads as a sunrise over
   the rim. At 560px and narrower, a phone, the lower half of the window at
   rest is the landing page's frosted entry panel, wherever the band goes, and
   its --ink-3 lines measured 4.44:1 through the glass over the soft band in
   WebKit (every browser on an iPhone). Last, so it wins over the narrow band
   above. The band keeps a height here, its full 0.10 W: under 674px tall the
   formula gives 0px, and WebKit paints a radial gradient whose ellipse has no
   height as nothing at all, not as its last colour, so the scrim itself would
   vanish (measured: 1.00:1 on every word at 1366x657). These are :root's own
   shut defaults, restated for the pages that open the band. */
@media (max-height: 753px), (max-width: 560px) {
  :root[data-backdrop="band"] {
    --backdrop-band: var(--backdrop-scrim);
    --backdrop-band-h: calc(0.10 * var(--backdrop-width));
    --backdrop-glare: none;
  }
}

/* A visitor who asks for more contrast (prefers-contrast: more; macOS's
   Increase contrast, for one) gets the scrim whole: the band shut, as on a
   short window, with its height kept for WebKit's sake, and no glare, on the
   three pages that open it (every other page has it shut already). Last, so
   it wins over every rule above. */
@media (prefers-contrast: more) {
  :root[data-backdrop="band"] {
    --backdrop-band: var(--backdrop-scrim);
    --backdrop-band-h: calc(0.10 * var(--backdrop-width));
    --backdrop-glare: none;
  }
}

/* The logo ships as a white PNG only. Inverting is cheaper and more reliable
   than a JS src-swap, which has to know the theme before first paint. */
[data-theme="light"] img.site-logo,
[data-theme="light"] img.logo-img { filter: invert(1) brightness(0.15); }

/* ── Reduced motion ───────────────────────────────────────────────────────
   The signature page-load is a staggered reveal. Honour the OS setting: keep
   the end state, drop the travel. Written once, here, so no surface has to
   remember it — the reveal utilities in arba-ui.css all key off these names. */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 0.01ms; --dur: 0.01ms; --dur-slow: 0.01ms; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* animation-delay MUST be zeroed too, and it is not a nicety. The reveal
       utilities animate FROM opacity 0 with fill-mode `both`, so an element
       holds the from-state for the whole delay — up to .35s of deliberately
       invisible content. Collapsing the duration alone leaves that wait intact,
       which on this preference is exactly the flash-of-nothing the user asked
       not to see. A negative value starts the animation already finished. */
    animation-delay: -1ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
