/* ============================================================================
   20/20 (StarSCAR) -- shared foundation.

   Linked FIRST in every page, before that page's own <style>, so page rules win
   on equal specificity without needing !important. Order matters: see the
   comment on each page's <link>.

   Scope note: supervisor.html DOES link this file, at its line 20, and has for
   some time. The note that used to sit here said it did not "yet", which was
   read by two separate reviews as licence to write a second copy of things that
   are already here. Everything in this file is written so that page can carry
   it -- hence no @media print block and no bare `svg` rule, both of which would
   collide with the print stylesheet that produces every PDF report.
   ========================================================================== */

:root {
  /* ---- type ---------------------------------------------------------------
     The root font-size is deliberately left at the browser default (see the
     `html` rule below), so every rem here tracks:
       - the browser's default-font-size setting (Chrome desktop, Android, Firefox)
       - Android's system font scale
       - page zoom, everywhere
     Note iOS Dynamic Type does NOT resize ordinary web text -- it only affects
     `font: -apple-system-body`. On iOS the lever that works is page zoom, which
     is why worker.html must not pin maximum-scale.

     Numeric scale rather than t-shirt sizes: supervisor.html carries ~140 sizes
     on a half-pixel grid (10.5/11/11.5/12/13/13.5/14/14.5/15…) and will need a
     lossless mapping onto this in pass 2. */
  --fs-100: .75rem;      /* 12 -- UPPERCASE micro-chips only, never sentences */
  --fs-200: .8125rem;    /* 13 -- the floor for prose. Replaces 11 and 11.5    */
  --fs-300: .875rem;     /* 14 -- meta, hints, captions                        */
  --fs-400: .9375rem;    /* 15 -- secondary copy                               */
  --fs-500: 1rem;        /* 16 -- body                                         */
  --fs-600: 1.0625rem;   /* 17 -- primary action label                         */
  --fs-700: 1.125rem;    /* 18                                                */
  --fs-800: 1.1875rem;   /* 19 -- card headings, wordmark                      */
  --fs-900: 1.3125rem;   /* 21                                                */

  /* Display sizes are the only fluid ones, and BOTH bounds are rem on purpose.
     A clamp with px bounds would cancel the user's text-size setting outright:
     someone on a 24px base would be clamped straight back down to the ceiling.
     With rem bounds a large base still yields a large heading, and the vw term
     only steers between them (~2px of travel across the phone range). */
  --fs-d1: clamp(1.4375rem, 1.36rem + .40vw, 1.5625rem);  /* 23 → 25 */
  --fs-d2: clamp(1.5rem,    1.40rem + .50vw, 1.6875rem);  /* 24 → 27 */
  --fs-d3: clamp(1.625rem,  1.45rem + .80vw, 2rem);       /* 26 → 32 */

  --lh-tight: 1.2;  --lh-snug: 1.3;  --lh-base: 1.5;  --lh-loose: 1.6;

  /* "Inter" was in the old stacks but is never loaded -- no @font-face, no font
     file, no <link> anywhere in the repo -- so it only ever applied for the few
     users with Inter installed locally, making rendering less consistent rather
     than more. Dropped, with real fallbacks added in its place. */
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --font-display: Georgia, "Times New Roman", serif;

  /* ---- space --------------------------------------------------------------
     rem, so vertical rhythm and control padding breathe with the user's text. */
  --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;  --sp-4: 1rem;
  --sp-5: 1.25rem; --sp-6: 1.5rem;  --sp-8: 2rem;    --sp-10: 2.5rem;

  /* The page gutter is px on purpose: it is a device constant, not a
     typographic one. In rem, a 24px-base user would surrender 24px of a 320px
     screen to margins. */
  --gutter: clamp(16px, 4.5vw, 28px);

  /* Also px on purpose: 44px is a FINGER. Fingers do not get bigger when you
     raise your font size. It does scale under page zoom, which is correct.
     Keeping it px is also what keeps .mic a circle -- in rem, a large base would
     push min-height past its 58px width and turn it into an ellipse. */
  --tap: 44px;

  --r-sm: 8px; --r-md: 10px; --r-lg: 12px; --r-xl: 16px;

  /* ---- colour -------------------------------------------------------------
     A superset of all five pages, so supervisor.html can adopt this file whole
     in pass 2.

     These MUST stay on :root. worker.html's open_() reads them off
     document.documentElement to colour the Send button:
       getComputedStyle(document.documentElement).getPropertyValue('--green')
     Move them to body or a class and the Send button goes transparent. */
  --paper:      #FAF8F4;   /* action surfaces: launcher, login, worker        */
  --paper-read: #F7F5F0;   /* reading surfaces: about, supervisor            */
  --card:       #FFFFFF;
  --ink:        #1A1A18;
  --mut:        #6B6862;
  --faint:      #6F6A61;  /* was #9B9790 (~2.6:1) -- carries scale/legend text, now ~4.7:1 AA */
  --line:       #E4E0D8;   /* borders only -- never text (1.3:1 on white)     */
  --wash:       #EFEEEA;   /* was #EFEBE2: the beige read yellowish inside white cards */
  --panel:      #F6F6F4;   /* in-card section panels: neutral, never cream. --paper is
                              the PAGE tone; inside a white card it reads yellow.      */
  --accent:     #1F4E46;
  --green:      #3F7A5E;
  --red:        #B0533F;
  /* the proportional bands take the object's oxide, not this red: #B0533F sits at L* 46.7 and
     --green at L* 46.7, so a boundary drawn between them is invisible without colour vision.
     #6E2A21 is L* 27.0 -- a gap of 19.7, past the 15 the die's own palette law requires. */
  --redband:    #6E2A21;
  --amber:      #B8860B;   /* dark goldenrod: reads YELLOW, not brown; same lightness
                              class as the old #B08A3F so no contrast regressions   */
  --au: #1F4E46;  --uk: #2F4A6E;  --iso: #6E2F55;
  --on-accent:  #FFFFFF;   /* replaces the hardcoded #fff on coloured fills  */

  /* Declares the light-only intent explicitly, which stops Android WebView
     force-dark and iOS control auto-darkening from improvising -- most visibly
     by inverting form fields into white-on-white. The QR entry path lands in a
     WebView, so this is not hypothetical. */
  color-scheme: light;
}

/* ---- reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  /* Stops iOS inflating text in landscape. Never `none` -- that breaks pinch
     zoom scaling and is the WCAG 1.4.4 trap. */
  -webkit-text-size-adjust: 100%;
          text-size-adjust: 100%;
  /* Branded rather than transparent: `transparent` removes all touch feedback
     from plain text links, which the :active transforms on buttons don't cover. */
  -webkit-tap-highlight-color: rgba(31, 78, 70, .15);
  /* No font-size here, deliberately. Setting one -- in px OR in clamp() --
     silently overrides the user's browser default-font-size setting, which is
     the single most important accessibility lever on the platform. */
}

body {
  font-family: var(--font-ui);
  font-size: var(--fs-500);
  line-height: var(--lh-base);
  color: var(--ink);
  background: var(--paper);
  /* Gentle: only breaks a word when it genuinely cannot fit. `anywhere` is
     applied surgically where arbitrary user text is rendered. */
  overflow-wrap: break-word;
}

/* Scoped to img/video on purpose. A bare `svg { max-width:100%; height:auto }`
   would distort supervisor.html's fixed-ratio timeline SVGs in pass 2. */
img, video { max-width: 100%; height: auto; }

/* ---- form controls --------------------------------------------------------
   Nothing in this app inherited the UI font before this rule, so every <button>
   computed to Arial and every <input>/<textarea> to the UA control font. Two
   pages also carried `font: 16px inherit`, which is invalid -- `inherit` is not
   a legal family inside the shorthand, so the whole declaration was dropped and
   those fields rendered at ~13.3px monospace. This is the fix for both. */
button, input, select, textarea, optgroup {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

/* iOS Safari zooms the viewport whenever a focused control computes under 16px,
   and does not zoom back out afterwards. 1rem is normally 16px -- but a user who
   SHRANK their base would fall under it and get the zoom back. max() pins the
   floor while still growing with a large base. The bare 1rem is the fallback for
   engines without max(); everything we target has it (Safari 11.1+). */
input, select, textarea {
  font-size: 1rem;
  font-size: max(1rem, 16px);
}

/* Text fields only. Deliberately NOT <select>: dropping its appearance loses the
   native iOS wheel picker affordance and forces a hand-drawn chevron for no gain. */
input:where([type="text"], [type="password"], [type="number"], [type="email"]),
textarea {
  -webkit-appearance: none;
          appearance: none;
}

::placeholder { color: var(--mut); opacity: 1; }   /* Firefox defaults to .54 */

/* Removes the 300ms double-tap-zoom delay WITHOUT disabling pinch zoom. This is
   the correct replacement for the `maximum-scale=1` that worker.html used to
   carry -- that blocked zoom entirely in Android Chrome and in-app WebViews. */
a, button, summary, label, [role="button"] { touch-action: manipulation; }
/* the grey tap-flash and long-press text selection on controls are the two biggest
   "this is a web page" tells on a phone; without them taps feel native */
html { -webkit-tap-highlight-color: transparent; }
button, [role="button"], summary { -webkit-user-select: none; user-select: none; }

/* Stops the iOS long-press selection/callout when a gloved hand rests on a
   control. Most visible on .mic, which is held down. */
button, summary, [role="button"] {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}

button, [role="button"] { cursor: pointer; }
button[disabled] { cursor: default; }

/* ---- focus ---------------------------------------------------------------- */

:where(a, button, summary, input, select, textarea):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
@supports not selector(:focus-visible) {           /* Safari < 15.4 */
  :where(a, button, summary, input, select, textarea):focus {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
  }
}

/* ---- touch targets --------------------------------------------------------
   min-height alone leaves text top-aligned in the taller box, so text-styled
   controls also need centring -- pages add `display:inline-flex; align-items:
   center` where that applies. */
button, summary, [role="button"] { min-height: var(--tap); }

/* ---- shared components ----------------------------------------------------- */

.mark {
  font-family: var(--font-display);
  font-size: var(--fs-800);
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}
.mark b { color: var(--accent); font-weight: 600; }

/* ---- the entry plate -------------------------------------------------------
   EVERY ENTRY SCREEN IS THIS ONE OBJECT WITH DIFFERENT WORDS IN IT: the front
   page, the sign-in, both intro pages and the setup link. It is written once,
   here, so there is no second copy to drift.

   It exists because three consecutive entry screens used to arrive with three
   unrelated geometries -- the wordmark landing at four different x-positions
   across one flow, and a block that moved a quarter of a screen down when a
   back link appeared. The rules below are the fixes, applied mechanically:

   1. ONE COLUMN, at every width, and it never becomes a grid. No
      grid-template-columns, no grid-auto-rows, no full-width children. That is
      what makes the "adding one element pushes everything down a whole row"
      fault impossible to reintroduce rather than merely absent.
   2. TOP-ANCHORED, NEVER CENTRED, and the top padding is a fixed 5rem rather
      than a viewport clamp. Centring, and clamping against viewport height, are
      exactly what make a block jump when the copy or the card count changes.
   3. THE MASTHEAD ROW IS 44px TALL WHETHER OR NOT ITS RIGHT CELL IS FULL, and
      .mark above already carries that floor, so this needs no new box. The
      wordmark is the first element OF THE COLUMN rather than of a full-width
      bar, so its left edge IS the cards' left edge by construction.
   4. THE HEAD BLOCK HAS A FLOOR AND THE CONTENTS MAY BE SHORTER, never taller.
   5. Which gives THE CONSTANT: the first card's top edge sits at
      5rem + 44px + 1.5rem + 11.5rem + 1.5rem = 356px at a 16px root, on every
      entry screen, at every width, with any copy, with two cards or four, with
      or without a control in the masthead's right cell. One assertion tests it:
        document.querySelector('.entrycard').getBoundingClientRect().top === 356
   6. THE PAGE SCROLLS. min-height, never height; no overflow:hidden; nothing
      measures a line and clamps it. If copy does not fit, the copy is wrong.

   Copy budgets, which are what hold rule 5: headline 48 characters, standfirst
   165, card blurb 120. The moment one card takes a third line at 544px the
   constant is gone. */

.plate {
  /* 34rem = 544px, the width the door screens already used, so nothing else in
     the product has to move. On a phone it is the viewport less two gutters. */
  width: min(34rem, 100% - 2 * var(--gutter));
  margin-inline: auto;
  min-height: 100svh;
  display: flex; flex-direction: column;
  padding-block: 5rem var(--sp-6);
}
/* flex:none on both: a flex item may shrink below its content, and these two
   are the ones the constant is measured through. */
.plate > .masthead { flex: none; min-height: var(--tap); display: flex;
                     align-items: center; justify-content: space-between;
                     gap: var(--sp-3); }
.plate > .head     { flex: none; min-height: 11.5rem; margin-top: var(--sp-6); }
.plate > .body     { margin-top: var(--sp-6); display: flex;
                     flex-direction: column; gap: var(--sp-3); }
.plate > .foot     { margin-top: auto; padding-top: var(--sp-6);
                     border-top: 1px solid var(--line);
                     font-size: var(--fs-200); color: var(--faint); }
.plate > .foot p + p { margin-top: var(--sp-2); }

/* The type ladder. Four sizes and one serif, and no screen picks its own. */
.plate h1 { font-family: var(--font-display); font-size: var(--fs-d3);
            line-height: var(--lh-tight); font-weight: 600; }
.plate .standfirst { font-size: var(--fs-600); color: var(--mut);
                     line-height: var(--lh-base); margin-top: var(--sp-2); }
.plate .quiet, .plate .foot a, .plate .onpath { font-size: var(--fs-300); }
/* Links INSIDE PROSE, and only those. Vertical padding on an inline element
   grows the tap area without breaking the sentence it sits in, which
   inline-flex would.

   `.plate a` was wrong twice over and both were visible: it caught the
   wordmark, which is an <a> in the masthead, so the whole 20/20 went accent
   instead of just the /20, and the .6rem padding pushed .mark past its 44px
   floor to 47.7px -- which is the masthead row, which is the constant. The
   first card landed at 359.7 instead of 356. It also caught the second entry
   card, which is an <a> too, and overrode its own vertical padding. */
.plate p a { color: var(--accent); padding-block: .6rem; }
.plate .foot a { color: inherit; }

.entrycard {
  display: block; width: 100%; text-align: left; font: inherit;
  min-height: 6.75rem;
  padding: var(--sp-4) var(--sp-5);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-xl);
}
/* No icons and no tint on any of these, ever. Colour in this product means a
   sentiment value or an obligation state, and a door is neither: the lock and
   globe marks said private and public, which is the distinction the entry
   stopped making. */
.entrycard h2 { font-family: var(--font-display); font-size: var(--fs-800);
                font-weight: 600; margin: 0; }
.entrycard p  { font-size: var(--fs-400); color: var(--mut);
                line-height: 1.5; margin-top: 6px; }
@media (hover: hover) {
  .entrycard:hover { box-shadow: 0 4px 14px rgba(26, 26, 24, .10); }
}

/* ---- loading skeletons -----------------------------------------------------
   A page that has painted but is still waiting on a fetch should SAY so. An
   empty area reads as "there is nothing here", which is a different and wrong
   message, and it also jumps when the data lands.

   The rule that makes them worth having: a placeholder must occupy the box its
   real content will. `.skel` on an inline element holding &nbsp; keeps the line
   box identical to the text that replaces it, so a figure swapping in moves
   nothing. Give it an explicit width; the caller knows how wide the answer is.

   The pulse is switched off by the reduced-motion block below, which is why
   this lives here rather than in three page stylesheets. supervisor.html also
   declares its own .skel with the width its two-digit figures need; that local
   rule simply wins there, which is the intended shape. */
.skel {
  display: inline-block;
  color: transparent;
  border-radius: var(--r-sm);
  background: var(--line);
  animation: skelpulse 1.4s ease-in-out infinite;
}
/* A bar standing in for one line of text. height:1em keeps it on the text's own
   line box rather than growing the row. */
.skel-line { display: inline-block; height: 1em; vertical-align: middle; }
/* ...and one standing in for a control, which owns a full tap target. */
.skel-btn { display: inline-block; height: var(--tap); border-radius: var(--r-md); }
@keyframes skelpulse { 0%, 100% { opacity: .45 } 50% { opacity: .85 } }

/* ---- motion ----------------------------------------------------------------
   Covers worker.html's infinite recording pulse. The 24-bar audio meter is
   driven by requestAnimationFrame and needs its own guard in JS. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* No @media print block here, deliberately. supervisor.html carries a large
   self-contained print stylesheet that produces every PDF report; a shared print
   rule is the easiest way to silently break it when that page adopts this file. */
