/* ==========================================================================
   SCOPE — the design layer
   --------------------------------------------------------------------------
   Loaded AFTER tokens.css and BEFORE app.css, so it retunes the system without
   rewriting the 500KB of rules that consume it.

   WHY THIS SHAPE AND NOT A GROUND-UP REPLACEMENT. tokens.css declares 153
   names and every stylesheet in the product reads them; 149 of the 1205 tests
   read a stylesheet, and the ones in test_front_end_contract.py encode measured
   contrast ratios and ARIA contracts that took real work to get right. Changing
   the VALUES behind the existing names re-skins every screen at once and keeps
   all of that. Changing the names would mean rewriting the rules, the tests and
   the reasoning, for the same pixels.

   So: the names stay, the values move, and the scales that were missing get
   added. Everything below is either a retune of something that existed or a
   new scale the old system had no answer for.

   THE REGISTER. tokens.css states it and it is worth keeping: no glass, no
   glow, no animated gradient. Those photograph well and wash out completely on
   a phone held at arm's length on a roof, which is the only place this is used.
   Confidence comes from precision -- exact spacing, real optical sizes, and
   motion that only ever reports a state change.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. The numeric face
   --------------------------------------------------------------------------
   --font-num existed and pointed at --font, because no numeric face had ever
   been installed. Prices, measurements and reference numbers all rendered in
   the UI face with proportional digits, so a column of figures did not line up
   and a price that changed jittered as the glyph widths shifted.

   Three weights and no more. 400 for body figures, 500 for emphasis, 600 for
   the hero price. A variable file would be one request instead of three but
   Plex Mono's variable build is 120KB against 45KB for these three, and the
   axes in between are not used.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/plex-mono-400.woff2") format("woff2");
  /* Metric overrides against the fallback, so the swap does not shift the
     layout. Measured against ui-monospace/Consolas, which is what these
     numbers render in for the ~120ms before the face lands. */
  size-adjust: 100%;
  ascent-override: 95%;
  descent-override: 25%;
}
@font-face {
  font-family: "Plex Mono";
  font-style: normal; font-weight: 500; font-display: swap;
  src: url("fonts/plex-mono-500.woff2") format("woff2");
  size-adjust: 100%; ascent-override: 95%; descent-override: 25%;
}
@font-face {
  font-family: "Plex Mono";
  font-style: normal; font-weight: 600; font-display: swap;
  src: url("fonts/plex-mono-600.woff2") format("woff2");
  size-adjust: 100%; ascent-override: 95%; descent-override: 25%;
}

/* --------------------------------------------------------------------------
   2. Retuned tokens
   -------------------------------------------------------------------------- */
:root {

  /* ---- the numeric face, wired to the names that were waiting for it ------
     tabular-nums is set on the consumers rather than here: the font feature
     belongs with the element, and a token cannot carry a font-variant. */
  --font-num: "Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --f-num: var(--font-num);

  /* ---- a real neutral scale ----------------------------------------------
     The old system had --chassis, --readout, --well and --sunk and nothing
     between them, so "one step darker than the card" had no answer and every
     surface reached for a border instead. Nine rungs, warm-tinted toward the
     brand navy rather than pure grey, so a card on a page does not read as two
     different colour temperatures.

     Never #FFF and never #000. Pure white on a phone in daylight is a glare
     source and pure black crushes the shadow ramp; both are the tell of a
     template. */
  --n-0:  #FFFFFF;   /* reserved: the one true white, for ink on dark only  */
  --n-25: #FBFCFD;
  --n-50: #F6F8FA;
  --n-100:#EDF1F5;
  --n-200:#DFE5EC;
  --n-300:#C7D1DC;
  --n-400:#9AA7B6;
  --n-500:#6B7887;
  --n-600:#4C5865;
  --n-700:#33404F;
  --n-800:#1B2836;
  --n-900:#0D1822;
  --n-950:#060F17;

  /* ---- brand orange, in the three roles the brief names -------------------
     #FFA128 is 1.94:1 on white. It is a SURFACE colour and a fill, never text
     on light. The two darker steps are the text-safe pair and both are
     measured: --orange-600 is 3.48:1 (non-text, clears the 3:1 of 1.4.11) and
     --orange-800 is 5.02:1 (text, clears 4.5:1 of 1.4.3).

     The existing --amber names alias onto these rather than being replaced,
     because every rule in app.css and scope.css already reads them. */
  --orange-400: #FFC078;
  --orange-500: #FFA128;   /* the brand mark. Fills and surfaces only.      */
  --orange-600: #C97A0A;   /* 3.48:1 on --n-50. Borders, rules, icons.      */
  --orange-800: #A65D00;   /* 5.02:1 on --n-50. The only orange for TEXT.   */
  --orange-wash:#FFF6EA;

  /* ---- type ---------------------------------------------------------------
     The scale the brief asks for -- 12/13/15/17/20/24/30/38 -- expressed in
     clamp() so it moves with the viewport instead of stepping at a breakpoint.

     Every floor is the phone size and every ceiling is the desktop size, so
     nothing needs a media query to grow. The vw term is tuned so the ramp is
     complete by about 600px and flat after: a figure that keeps growing on a
     1920 monitor looks like a mistake.

     --fs-body stays 1rem exactly. It is the anchor the whole product's rhythm
     was set against and moving it would reflow every screen. */
  --fs-micro:  0.75rem;                                    /* 12 */
  --fs-tiny:   0.8125rem;                                  /* 13 */
  --fs-meta:   0.9375rem;                                  /* 15, was 14 */
  --fs-small:  var(--fs-meta);
  --fs-body:   1rem;                                       /* 16 */
  --fs-lead:   clamp(1.0625rem, 0.99rem + 0.36vw, 1.1875rem);   /* 17 -> 19 */
  --fs-h2:     clamp(1.125rem, 1.03rem + 0.47vw, 1.25rem);      /* 18 -> 20 */
  --fs-h1:     clamp(1.5rem, 1.24rem + 1.3vw, 1.875rem);        /* 24 -> 30 */
  --fs-figure-sm: clamp(1.375rem, 1.19rem + 0.94vw, 1.625rem);
  --fs-figure: clamp(2rem, 1.48rem + 2.6vw, 2.75rem);      /* the price     */
  --fs-display: clamp(2.375rem, 1.5rem + 4.4vw, 3.5rem);   /* 38 -> 56      */

  /* Tracking tightens as type grows, which is what stops a large heading
     reading as loose. Negative tracking on body text is the other common
     mistake and is not done here. */
  --track-display: -0.022em;
  --track-h1:      -0.016em;
  --track-h2:      -0.008em;
  --track-body:     0;
  --track-caps:     0.06em;

  --lh-tight: 1.08;
  --lh-snug:  1.3;
  --lh-body:  1.55;

  /* ---- space, on a 4px grid ----------------------------------------------
     s1..s9 existed and are kept. s10..s12 are added because the old scale
     topped out at 64px, so section rhythm on a desktop had nothing above it
     and reached for a literal. */
  --s10: 80px;
  --s11: 96px;
  --s12: 128px;

  /* ---- radii --------------------------------------------------------------
     The brief's ladder: 8 / 12 / 20 / pill. --r was 14 and is now 12, which
     matters more than it sounds -- 14 is the value that reads as "a rounded
     rectangle" and 12 reads as a considered corner. --r-hero keeps its 30 for
     the full-bleed panels. */
  --r-sm:   8px;
  --r:      12px;
  --r-lg:   20px;
  --r-hero: 28px;
  --r-pill: 999px;
  --radius-sm: var(--r-sm);
  --radius:    var(--r);
  --radius-lg: var(--r-lg);

  /* ---- elevation ----------------------------------------------------------
     Four levels, each two layers: a tight contact shadow that anchors the
     object and a wide soft one that lifts it. One-layer shadows are the single
     clearest tell of a template -- they read as a drop shadow rather than as
     an object above a surface.

     Tinted with the brand navy rather than black. A neutral-black shadow on a
     cool-tinted page reads as dirt.

     Opacities are deliberately low. The old --shadow-2 ran to .14, which on a
     phone in daylight is a grey band under the card. */
  --sh-1: 0 1px 2px rgba(13, 24, 34, .05), 0 1px 3px rgba(13, 24, 34, .04);
  --sh-2: 0 2px 4px rgba(13, 24, 34, .05), 0 4px 12px rgba(13, 24, 34, .06);
  --sh-3: 0 4px 8px rgba(13, 24, 34, .06), 0 12px 28px rgba(13, 24, 34, .08);
  --sh-4: 0 8px 16px rgba(13, 24, 34, .08), 0 24px 56px rgba(13, 24, 34, .12);
  --shadow-1: var(--sh-1);
  --shadow-2: var(--sh-3);
  --elev:     var(--sh-2);
  --elev-lg:  var(--sh-3);

  /* ---- motion -------------------------------------------------------------
     One easing existed. A system needs the pair: things that ENTER decelerate,
     things that LEAVE accelerate, and things the finger is holding follow a
     spring. Using one curve for all three is why interfaces feel flat.

     --ease-spring is the iOS-style curve the brief names. It overshoots
     slightly, which is what makes a press feel physical rather than animated. */
  --ease:        cubic-bezier(.2, .7, .3, 1);
  --ease-out:    cubic-bezier(.16, 1, .3, 1);
  --ease-in:     cubic-bezier(.7, 0, .84, 0);
  --ease-spring: cubic-bezier(.32, .72, 0, 1);

  --dur-1: 120ms;    /* a state change: hover, focus, tint            */
  --dur-2: 180ms;    /* a press, a toggle                             */
  --dur-3: 260ms;    /* something entering or leaving the screen      */
  --dur-4: 380ms;    /* a sheet, a full-screen transition             */

  /* ---- z-index, named -----------------------------------------------------
     There was no scale, so every fixed thing picked a number and the stack was
     decided by whoever picked the biggest. */
  --z-base: 0;
  --z-raised: 10;
  --z-sticky: 20;
  --z-bar: 30;
  --z-nav: 40;
  --z-sheet: 60;
  --z-scrim: 55;
  --z-toast: 80;
  --z-skip: 100;

  /* ---- retuned surfaces ---------------------------------------------------
     Pointing the old names at the new neutral scale. This is the line that
     re-skins the product: every rule that says background: var(--chassis)
     picks up the new value without being touched. */
  /* THE GREYS COME FROM THE SCALE. THE NAVY DOES NOT, AND THAT WAS THE BUG.

     The first cut of this file repointed --ink, --navy and --bezel at the
     neutral ramp along with everything else, which flattened the brand out of
     the product without touching a single rule. --ink went from #062A4A, a
     real blue, to #0D1822, a grey. --bezel-raised went from #0F3D66 to a dull
     slate. And --field-face is `linear-gradient(180deg, var(--bezel),
     var(--ink))`, so the hero graduation on /, on /new and on the measuring
     screen stopped being navy-into-navy and became navy-into-grey.

     Greys are neutral on purpose: a card, a rule and a page want no hue, or
     they fight whatever is printed on them. The brand surface is the opposite
     and wants all of it. Two families, and only the first one comes from the
     ramp. */
  /* #F2F5F8, not the ramp's --n-50. The ramp is tuned as a ramp; this one
     value is tuned against the white card that sits on it. A step lighter and
     the separation falls from 1.09:1 to 1.06 and the cards stop reading as
     objects on a surface. --n-50 stays where it is for everything else. */
  --chassis:  #F2F5F8;
  --readout:  var(--n-0);
  --well:     var(--n-100);
  --sunk:     var(--n-100);
  --construction: var(--n-200);
  --hair:     var(--n-200);
  --rule-soft:var(--n-400);
  --rule:     var(--n-500);
  --muted:    var(--n-500);

  /* The navy family, restored to the values every contrast figure in
     tokens.css is written against: --ink 14.61:1 on the readout, --navy
     11.17:1, --bezel 16.74:1 against the page. */
  --ink:      #062A4A;
  --navy:     #0F3D66;

  --bezel:        #001629;
  --bezel-raised: #0F3D66;

  /* ---- THE BRAND GRADIENT -------------------------------------------------
     --field-face already existed as `linear-gradient(180deg, --bezel, --ink)`
     and is what every dark hero in the product is painted with. Two stops
     between two very close navies is a flat panel with a hint of a ramp; what
     reads as depth is a third stop that LIFTS in the middle, which is exactly
     what the icon does.

     The lift colour is SAMPLED FROM THE ICON, not invented. Its master runs
     #2065BD at the top-left corner to #00092B at the bottom-right, and
     #0E326D is that line at t=0.55. So the hero band in the app and the icon
     on the phone's home screen are one graduation, which is the thing that
     made the mark feel expensive to begin with.

     160deg, not 180: a true vertical ramp reads as a background, an angled one
     reads as a lit surface. Shallow enough that the corner-to-corner travel
     stays short on a wide desktop band.

     THE MID STOP IS THE WORST CASE, and the first draft of this comment said
     the opposite. It claimed nothing on the panel could get worse than the
     edges it was already measured at -- but the lift is LIGHTER than both
     ends, so it is precisely where every light ink on this surface is at its
     tightest. Measured there rather than assumed:

       --on-bezel        12.38:1   (edges 18.31 and 14.61)
       --on-bezel-muted   5.91:1   (edges  8.74 and  6.97)  text, needs 4.5
       --amber            3.70:1   (edges  5.47 and  4.36)  never text here

     --amber on this surface is the step dot, the tolerance marker and the
     current-page rule -- graphical objects every one, so the floor is 3:1 and
     not 4.5. --on-bezel-muted IS text, carries the hero lede, and clears 4.5
     with room. t=0.55 was chosen over the midpoint for exactly that headroom. */
  --brand-lift: #0E326D;
  --field-face: linear-gradient(160deg,
                  var(--bezel) 0%,
                  var(--brand-lift) 46%,
                  var(--ink) 100%);
}

/* --------------------------------------------------------------------------
   3. Dark
   --------------------------------------------------------------------------
   DESIGNED, NOT INVERTED, and this block is where that distinction lives.

   An inversion maps --n-50 to --n-900 and stops. What that produces is a page
   whose shadows have nothing to fall on, whose orange is now screaming against
   a dark field, and whose "subtle" borders have become the loudest thing on
   the screen. Every one of those is corrected here deliberately:

     * Surfaces get LIGHTER as they come forward, not darker. On dark, elevation
       is carried by luminance, because a shadow on a dark ground is invisible.
     * The shadows stay, at higher opacity, but they are doing contact rather
       than lift.
     * Orange steps UP the scale, not down. --orange-800 is a text colour on
       white; on a dark ground it is mud. Dark uses --orange-400.
     * Borders step DOWN in contrast. --n-200 on white is a hairline; its
       inverse on dark is a fence.

   Scoped so an explicit choice always wins: the media query is guarded against
   [data-theme="light"], and the attribute selector is repeated after it.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --n-0:  #0D1822;
    --n-25: #101D28;
    --n-50: #0A1622;
    --n-100:#16242F;
    --n-200:#22323F;
    --n-300:#314453;
    --n-400:#5A6B7C;
    --n-500:#8494A4;
    --n-600:#A3B1BF;
    --n-700:#C2CDD8;
    --n-800:#DDE5EC;
    --n-900:#F2F6F9;

    --chassis:  #0A1622;
    --readout:  #16242F;    /* forward surfaces are LIGHTER here */
    --well:     #101D28;
    --sunk:     #101D28;
    --construction: #263646;
    --hair:     #263646;
    --rule-soft:#4A5A6B;
    --rule:     #8494A4;
    --muted:    #9AAABA;
    --ink:      #F2F6F9;
    --navy:     #7FB3E0;

    --orange-500: #FFA128;
    --orange-600: #FFB65A;
    --orange-800: #FFC078;   /* the TEXT orange on dark is the light one */
    --orange-wash:#2A1F10;
    --amber:      #FFB65A;
    --amber-ink:  #FFC078;
    --amber-wash: #2A1F10;

    --ok-ink:   #5FD3A0;
    --ok-wash:  #0E2A20;
    --error:    #FF8A85;
    --error-wash:#2C1414;

    /* Contact, not lift. A wide soft shadow on a dark ground does nothing
       visible; what reads is a tight dark edge under the object. */
    --sh-1: 0 1px 2px rgba(0, 0, 0, .40);
    --sh-2: 0 2px 6px rgba(0, 0, 0, .45);
    --sh-3: 0 6px 18px rgba(0, 0, 0, .50);
    --sh-4: 0 12px 40px rgba(0, 0, 0, .60);

    --bezel:        #060F17;
    --bezel-raised: #16242F;
  }
}
/* The in-app override, repeated so it wins in both directions. */
:root[data-theme="dark"] {
  --n-0:#0D1822; --n-25:#101D28; --n-50:#0A1622; --n-100:#16242F;
  --n-200:#22323F; --n-300:#314453; --n-400:#5A6B7C; --n-500:#8494A4;
  --n-600:#A3B1BF; --n-700:#C2CDD8; --n-800:#DDE5EC; --n-900:#F2F6F9;
  --chassis:#0A1622; --readout:#16242F; --well:#101D28; --sunk:#101D28;
  --construction:#263646; --hair:#263646; --rule-soft:#4A5A6B; --rule:#8494A4;
  --muted:#9AAABA; --ink:#F2F6F9; --navy:#7FB3E0;
  --orange-600:#FFB65A; --orange-800:#FFC078; --orange-wash:#2A1F10;
  --amber:#FFB65A; --amber-ink:#FFC078; --amber-wash:#2A1F10;
  --ok-ink:#5FD3A0; --ok-wash:#0E2A20; --error:#FF8A85; --error-wash:#2C1414;
  --sh-1:0 1px 2px rgba(0,0,0,.40); --sh-2:0 2px 6px rgba(0,0,0,.45);
  --sh-3:0 6px 18px rgba(0,0,0,.50); --sh-4:0 12px 40px rgba(0,0,0,.60);
  --bezel:#060F17; --bezel-raised:#16242F;
}

/* --------------------------------------------------------------------------
   4. Typographic defaults
   -------------------------------------------------------------------------- */
body {
  /* Optical sizing and kerning are both on by default in most engines and off
     in some; stating them costs nothing and removes the variance. */
  font-optical-sizing: auto;
  font-kerning: normal;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, .t-h1 { font-size: var(--fs-h1); line-height: var(--lh-tight);
            letter-spacing: var(--track-h1); }
h2, .t-h2 { font-size: var(--fs-h2); line-height: var(--lh-snug);
            letter-spacing: var(--track-h2); }

/* EVERY NUMBER IN THE PRODUCT, in one place.

   tabular-nums is the half people remember. The other half is that a price
   which changes -- and on this product the price changes as he moves a
   posture control -- jitters on proportional digits as the glyph widths shift
   under it. Fixed advance widths are what make a live figure hold still. */
.num, .money, .measure, .t-num,
.hq-kpi-figure, .hq-table td.num, .hq-table th.num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.01em;
}

/* THE COMMA, WHICH IS THE COST OF A MONOSPACED FACE.

   Plex Mono gives every glyph the same advance width, the comma included. So
   a grouped figure renders as "£27 , 523" -- the separator floating in a full
   digit's worth of space -- and at display sizes that gap is wide enough to
   read as two numbers rather than one.

   Tightening the whole string is the fix that keeps the face: the digits are
   fixed-width regardless, so negative tracking pulls the separator in without
   costing the column alignment that tabular figures exist for. Scaled by size,
   because the gap is proportional and a single value over-tightens the small
   figures while barely touching the large one.

   The alternative -- a proportional face with tabular figures -- was rejected:
   it solves the comma and loses the thing this face was chosen for, which is
   that a price recalculating under a posture control does not jitter. */
.num { letter-spacing: -0.012em; }
[style*="--fs-figure"], .hq-kpi-figure, .money-lg { letter-spacing: -0.03em; }
