/* ==========================================================================
   EV Clinic — design tokens.

   White, glass, and the cell mosaic. The dark master-franchise page and this
   are one brand seen from two sides: same three accents, same typography,
   opposite ground.

   The page is built in six layers, and the order is the whole design:

     1  white            #FFFFFF, the ground
     2  cell mosaic      a very pale grid on the same 19px pitch the video
                         backdrop samples at — the brand's own motif, used as
                         structure rather than decoration
     3  colour washes    cold / mint / amber radials at 6-10% alpha
     4  WebGL glass      the hero object, refracting layers 1-3
     5  frosted panels   where the content sits
     6  ink              text and hairlines

   Layers 2 and 3 are not ornament. Frosted glass is only glass if there is
   something behind it to blur; over flat white a backdrop-filter panel is an
   expensive way to draw a grey rectangle. The mosaic and the washes are what
   the glass is made of.

   Two rules govern the colour:

     Accents come in two strengths. The mint and amber that read beautifully
     as a fill fail contrast as text, so each has an -ink variant taken to at
     least 4.5:1. Fills and graphics use the pure colour, type uses -ink.

     These figures are computed, not estimated: run tools/check_contrast.py,
     which measures every token against --paper AND checks that this comment
     still agrees with the file. The first version of this block was wrong on
     all four numbers.

     On white: --mute 5.70:1, --mint-ink 5.69:1, --amber-ink 6.61:1,
     --cold 6.77:1.

     Nothing downstream hard-codes a hex value. Everything reads from here,
     which is what makes the relationship to the dark sibling a matter of
     swapping one file rather than auditing every rule.
   ========================================================================== */

:root{
  /* ------------------------------------------------------------- surfaces */
  --paper:#FFFFFF;

  /* The opaque surface, for everything glass cannot be.
     An SVG `fill` takes a colour and not a backdrop-filter, so the map's
     marker discs and popup grounds are painted rather than frosted. Same for
     a form field, where a translucent input over a moving mosaic is a
     legibility problem rather than an effect. */
  --panel:#FFFFFF;

  --raise:#F5F7FA;        /* bands that separate by ground, not by border */
  --sink:#EDF1F6;         /* pressed states, table stripes, inert wells */
  --line:#E2E8EF;
  --line-soft:#EDF1F6;
  --line-strong:#CBD5E0;

  /* ----------------------------------------------------------------- type */
  --ink:#0E1620;
  --mute:#59687A;         /* 5.70:1 on white */
  --faint:#8895A6;        /* decorative only — never body text */

  /* -------------------------------------------------------------- accents */
  --cold:#2B4BD8;
  --cold-ink:#2B4BD8;
  --mint:#3BE0A0;
  --mint-ink:#0E7553;
  --amber:#FFB020;
  --amber-ink:#875000;

  /* Written as rgb triples so a rule can choose its own alpha without a
     second variable for every opacity. */
  --cold-rgb:43,75,216;
  --mint-rgb:59,224,160;
  --amber-rgb:255,176,32;
  --ink-rgb:14,22,32;
  --white-rgb:255,255,255;

  /* ---------------------------------------------------------------- glass
     One recipe, four variables, so a panel, a header and a popup cannot
     drift apart. See .glass in base.css for how they combine. */
  --glass-top:rgba(255,255,255,.74);
  --glass-bottom:rgba(255,255,255,.52);
  --glass-edge:rgba(255,255,255,.78);      /* the 1px border */
  --glass-sheen:rgba(255,255,255,.92);     /* inset highlight on the top edge */
  --glass-shadow:0 10px 34px rgba(14,22,32,.07);
  --glass-blur:18px;
  --glass-saturate:1.5;

  /* --------------------------------------------------------------- mosaic
     The cell grid. 19px is not arbitrary: it is CELL in evc-backdrop.js, so
     the static mosaic and the video mosaic land on the same lattice and read
     as one surface rather than two overlapping grids. */
  --cell:19px;
  --cell-ink:rgba(14,22,32,.038);
  --cell-dot:rgba(14,22,32,.055);

  /* ------------------------------------------- pipeline and network status
     Weakest to strongest. Shared with the map so a legend chip and a country
     fill cannot disagree. */
  --st-soon:#7C8B9C;
  --st-signed:#2B4BD8;
  --st-boarding:#9A5B00;   /* 5.43:1 — #B26A00 measured 4.24 and failed */
  --st-open:#0E7553;

  /* Franchise classes. */
  --ty-hq:#875000;
  --ty-mothership:#0E7553;
  --ty-legacy:#4A6C88;

  /* ------------------------------------------------------------- the map
     Inverted from the dark sibling: there, land is a lighter shape on a
     near-black sea. Here the sea is the page and land is a shade darker, so
     the continents read as objects lying on the paper rather than as holes
     cut out of it. */
  --map-sea:transparent;
  --map-land:#E7ECF3;
  --map-land-hi:#D7DFE9;
  --map-stroke:#B6C3D1;

  /* ---------------------------------------------------------- typography */
  --display:'Archivo',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'IBM Plex Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,monospace;

  /* ------------------------------------------------------------ geometry */
  --gut:clamp(20px,5vw,64px);
  --evc-gut:var(--gut);                /* the name theme.json refers to */
  --content:760px;
  --wide:1280px;
  --radius:3px;                        /* glass wants a hair more than a box */

  --ease-out:cubic-bezier(.2,.7,.2,1);
  --ease-in-out:cubic-bezier(.4,0,.2,1);

  /* The header's floor. A CONSTANT, and it has to stay one.
     --header-min is what .header-inner's min-height reads. --header-h is a
     measurement of the rendered header, written by JavaScript. They must
     never be the same variable: feeding a measurement back into the rule
     that produces it is a loop with no fixed point. See --header-h below. */
  --header-min:66px;

  /* Set at runtime. Declared here with defaults so a rule that reads one
     before its script has run gets a usable number rather than an invalid
     value that kills the whole declaration.

     --header-h is the MEASURED height of .site-header, published by
     evc-chrome.js. Read it only from things the header's own size cannot
     depend on — scroll-padding-top, and the top edge of the mobile drawer,
     which is position:fixed and therefore out of flow.

     It was once read by .header-inner's min-height, and that shipped. The
     header carries a 1px bottom border, so the measured box was always one
     pixel taller than the inner box it was being fed back into: every
     notification added a pixel, forever, and the whole page walked down the
     screen at roughly sixty pixels a second. tools/probe_header.py exists to
     catch exactly this and is the regression test for it. */
  --header-h:66px;
  --scroll-v:0;
  --bd-cell:19px;
  --bd-opacity:.26;
}

/* ========================================================================
   When glass must stop being glass.

   backdrop-filter is the most expensive thing on this page. Every element
   carrying one becomes its own compositing layer, and on a mid-range phone a
   handful of them turns a smooth scroll into a slideshow. It is also a real
   accessibility problem: translucency over moving content is exactly what
   prefers-reduced-transparency exists to switch off.

   Three escapes, all of which land on the same opaque fallback, so the
   design degrades to flat panels rather than to nothing.
   ======================================================================== */

/* 1. The visitor asked for it. */
@media (prefers-reduced-transparency: reduce){
  :root{
    --glass-top:#FFFFFF;
    --glass-bottom:#FFFFFF;
    --glass-edge:var(--line);
    --glass-sheen:transparent;
    --glass-blur:0px;
    --glass-saturate:1;
  }
}

/* 2. The screen is small, which correlates with the slow GPU. */
@media (max-width: 900px){
  :root{
    --glass-top:rgba(255,255,255,.97);
    --glass-bottom:rgba(255,255,255,.97);
    --glass-blur:0px;
    --glass-saturate:1;
  }
}

/* 3. The browser cannot do it at all. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  :root{
    --glass-top:#FFFFFF;
    --glass-bottom:#FFFFFF;
    --glass-sheen:transparent;
  }
}

/* --------------------------------------------------------------- contrast
   Windows high-contrast and forced-colors: hand the palette back to the OS
   rather than fighting it. A design built from space and hairlines survives
   this better than most, because the structure is in the spacing and the
   type rather than in the colour. */
@media (forced-colors: active){
  :root{
    --paper:Canvas;
    --raise:Canvas;
    --sink:Canvas;
    --ink:CanvasText;
    --mute:CanvasText;
    --faint:CanvasText;
    --line:CanvasText;
    --line-soft:CanvasText;
    --line-strong:CanvasText;
    --glass-top:Canvas;
    --glass-bottom:Canvas;
    --glass-edge:CanvasText;
    --glass-sheen:transparent;
    --glass-shadow:none;
    --glass-blur:0px;
    --cell-ink:transparent;
    --cell-dot:transparent;
  }
}

/* ==========================================================================
   THE SECOND GROUND
   ==========================================================================

   The same brand, seen from the other side. While the story is on screen the
   page is slate; before and after it is paper.

   WHY A SECOND SCHEME AND NOT A SECOND STYLESHEET

   Everything downstream of this file reads var(--ink), var(--panel),
   var(--line). So the second ground is these same names given different
   values under one attribute — and the whole theme follows it without a
   single rule being written twice. A stylesheet that restated the design in
   dark would be a second design to keep in step with the first.

   WHY THE STORY AND NOT THE PAGE

   The metals in the WebGL scene are aluminium and steel at high metalness.
   On white paper they are pale silver on near-white: the geometry is there
   and the eye has nothing to catch, which is the whole of Phase 1.3's
   problem and only half solved by drawing edges on them. Against slate they
   have something to shine against.

   The rest of the page does not have that problem and does not get the
   treatment. The header stays glass, the masthead stays paper, and whatever
   follows the story returns to paper.

   EVERY VALUE HERE IS MEASURED. tools/check_contrast.py runs the same table
   against both grounds; the ratios quoted in the comments are its output.
   ========================================================================== */

:root[data-ground="deep"]{
  /* ---- ground ---------------------------------------------------------- */
  --paper:#111A25;
  --panel:#18222E;
  --raise:#1C2733;        /* bands that separate by ground, not by border */
  --sink:#0C141D;         /* pressed states, inert wells */
  --line:#2A3746;
  --line-soft:#1E2A37;
  --line-strong:#3C4C5E;

  /* ---- type ------------------------------------------------------------ */
  --ink:#F2F6FA;          /* 16.14:1 */
  --mute:#A9B6C6;         /* 8.51:1 */
  --faint:#6D7D90;        /* decorative only — never body text */

  /* ---- accents --------------------------------------------------------- */
  /* The cold is a deep blue chosen for white paper; on slate it is almost
     the ground. The -ink variants are what carry text, so those are the ones
     that move. */
  --cold:#8FA8FF;
  --cold-ink:#8FA8FF;     /* 7.69:1 */
  --mint:#3BE0A0;
  --mint-ink:#3BE0A0;     /* 10.31:1 */
  --amber:#FFB020;
  --amber-ink:#FFC45C;    /* 11.10:1 */

  --cell-ink:rgba(242,246,250,.045);
  --cell-dot:rgba(242,246,250,.065);

  /* ---- glass ----------------------------------------------------------- */
  /* Glass on a dark ground is a film of light, not a film of white: the top
     gradient and the sheen drop by an order of magnitude or every panel
     becomes a grey box. */
  --glass-top:rgba(255,255,255,.07);
  --glass-bottom:rgba(255,255,255,.03);
  --glass-edge:rgba(255,255,255,.10);
  --glass-sheen:rgba(255,255,255,.14);
  --glass-shadow:0 10px 34px rgba(0,0,0,.34);

  /* ---- status and class ------------------------------------------------ */
  --st-soon:#9FB0C4;      /* 7.91:1 */
  --st-signed:#8FA8FF;    /* 7.69:1 */
  --st-boarding:#F0A93C;  /* 8.72:1 */
  --st-open:#3BE0A0;      /* 10.31:1 */

  --ty-hq:#FFC45C;        /* 11.10:1 */
  --ty-mothership:#3BE0A0;/* 10.31:1 */
  --ty-legacy:#9BB6D0;    /* 8.34:1 */

  /* ---- the map --------------------------------------------------------- */
  --map-land:#1B2733;
  --map-land-hi:#243343;
  --map-stroke:#36475A;
}

/* The ground itself, and everything that has to cross with it.

   700 ms, because the attribute flips at a scroll position and a hard cut
   between two full colour schemes reads as a page reloading. Not on the
   WebGL canvas, which has no colour of its own to cross. */
:root[data-ground="deep"],
:root[data-ground="deep"] body{background:var(--paper)}

html{transition:background-color .7s var(--ease-out, ease)}

body,
.glass,
.card,
.site-header,
.site-footer{
  transition:
    background-color .7s var(--ease-out, ease),
    border-color .7s var(--ease-out, ease),
    color .7s var(--ease-out, ease);
}

@media (prefers-reduced-motion:reduce){
  html,body,.glass,.card,.site-header,.site-footer{transition:none}
}
