/* ==========================================================================
   app-one-theme.css  —  design tokens + legacy token bridge
   --------------------------------------------------------------------------
   Load order (index.html):
       <link rel="stylesheet" href="/styles.css?v=NNN">
       <link rel="stylesheet" href="/mobile-dialogs.css?v=NNN">
       <link rel="stylesheet" href="/app-one-theme.css?v=NNN">      <-- this
       <link rel="stylesheet" href="/app-one-components.css?v=NNN">

   This file adds NOTHING to styles.css. It only:
     1. declares the new --a1-* token set,
     2. re-points the existing tokens (--bg, --panel, --text, --cyan, ...)
        at the new values, so the 39k lines inherit the new palette.

   Both themes ship. Day is the default; an explicit Day, Night, or System
   choice is stored by app-one-theme.js and resolved to
   <html data-theme="light|dark"> before first paint.
   ========================================================================== */

/* ---- Self-hosted webfonts -------------------------------------------------
   The design system asks for Poppins and Nunito. They cannot come from Google
   Fonts here: this origin's CSP declares no `font-src`, so font loading falls
   back to `default-src 'self'` and any cross-origin file is blocked outright.
   Serving them from /fonts/ satisfies that same-origin rule, so no CSP change
   is needed.

   Split the way Google splits them, by `unicode-range`: a page with no
   latin-ext characters never fetches the latin-ext files. Latin-only comes to
   ~77 KB of the 138 KB on disk. `font-display: swap` means text paints
   immediately in the fallback and reflows when the file lands, rather than
   holding the first paint hostage.

   Poppins is static on Google Fonts, hence one file per weight actually used
   (400 body, 500/600/700 UI, 800 stat-tile values). Nunito is variable, so one
   file covers 400-700 — including the 500 that .a1-t-meta asks for.

   CJK is deliberately NOT covered. Both families would balloon, and the next
   stack entry (PingFang SC on Apple, then the system UI font) is what Chinese
   text should render in anyway.

   Both are SIL Open Font License 1.1; see fonts/OFL.txt.
   Refresh with the script recorded in docs/design/HANDOFF-UI-REFRESH.md.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/poppins-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/poppins-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/poppins-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/poppins-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/poppins-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/poppins-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/poppins-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/poppins-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/fonts/poppins-800-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/fonts/poppins-800-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/nunito-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/nunito-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ---- Neutrals. The whole UI is built from these. ---------------------- */
  --a1-bg:            #F5F5F4;
  --a1-surface:       #FFFFFF;
  --a1-surface-2:     #FAFAF9;
  --a1-surface-3:     #F0F0EE;
  --a1-border:        #E6E6E3;
  --a1-border-strong: #D5D5D1;

  --a1-text:          #1A1A19;
  --a1-text-2:        #5C5C58;
  --a1-text-3:        #6E6E69;   /* 5.1:1 on white — safe for 11–13px meta */

  /* ---- One accent. That is the entire brand color budget. --------------- */
  --a1-accent:        #4C5BD4;
  --a1-accent-hover:  #3F4DC0;
  --a1-accent-soft:   #ECEEFB;
  --a1-accent-text:   #3A47AE;
  --a1-on-accent:     #FFFFFF;

  /* ---- Semantic. ONLY urgency / state. Never decoration. ---------------- */
  --a1-urgent-bg: #FBE9E6;  --a1-urgent-fg: #A2352A;  --a1-urgent-line: #E8B6AE;
  --a1-soon-bg:   #FBF2DE;  --a1-soon-fg:   #8A6412;  --a1-soon-line:   #E8D3A6;
  --a1-ok-bg:     #E6F1EA;  --a1-ok-fg:     #2C6B4E;  --a1-ok-line:     #B6D8C4;
  --a1-muted-bg:  #F0F0EE;  --a1-muted-fg:  #6E6E69;  --a1-muted-line:  #E0E0DC;

  /* ---- Flat-illustration fills. Desaturated on purpose. ----------------
     --a1-illus-ink is an ILLUSTRATION STROKE weight (decorative, the scenes
     are aria-hidden). It is deliberately soft and measures only 2.95:1 on
     --a1-illus-1, so it must never be used for text. .a1-avatar initials are
     content and use --a1-text instead — see app-one-components.css. */
  --a1-illus-1: #D9DDF7;
  --a1-illus-2: #E7E2D4;
  --a1-illus-3: #D6E4DC;
  --a1-illus-ink: #7C7F92;   /* soft blue-grey outline, not black */
  --a1-illus-paper: #FFFFFF;

  /* ---- Type ------------------------------------------------------------- */
  --a1-font: 'Poppins', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', system-ui, sans-serif;
  --a1-font-alt: 'Nunito', 'PingFang SC', system-ui, sans-serif;

  --a1-fs-display: 26px;
  --a1-fs-h1: 21px;
  --a1-fs-h2: 17px;
  --a1-fs-h3: 15px;
  --a1-fs-body: 15px;
  --a1-fs-sm: 13px;
  --a1-fs-xs: 11.5px;

  --a1-lh-tight: 1.25;
  --a1-lh-body: 1.55;

  /* ---- Space. The old screens ran at roughly half of these. ------------- */
  --a1-s1: 4px;   --a1-s2: 8px;   --a1-s3: 12px;  --a1-s4: 16px;
  --a1-s5: 20px;  --a1-s6: 24px;  --a1-s7: 32px;  --a1-s8: 40px;

  --a1-gutter:   20px;  /* screen side padding */
  --a1-card-pad: 16px;  /* inside a panel */
  --a1-card-gap: 16px;  /* BETWEEN panels — must stay > --a1-row-gap */
  --a1-row-gap:  4px;   /* between rows inside one panel */

  /* ---- Shape & depth. Flat: shadows are for lift, never glow. ----------- */
  --a1-r-sm: 10px;  --a1-r-md: 14px;  --a1-r-lg: 20px;  --a1-r-xl: 26px;
  --a1-r-pill: 999px;

  --a1-shadow-1: 0 1px 2px rgba(24,24,22,.05);
  --a1-shadow-2: 0 4px 16px rgba(24,24,22,.07);
  --a1-shadow-3: 0 12px 40px rgba(24,24,22,.12);

  --a1-hit: 44px;
  --a1-ease: cubic-bezier(.32,.72,.35,1);

  --a1-scrim: rgba(20, 20, 18, .45);
}

/* ==========================================================================
   SWEEP TOKENS  (TASK 1b) — the only tokens styles.css itself references
   --------------------------------------------------------------------------
   387 declarations in styles.css were rewritten from dark literals to
       background: var(--a1-sw-surface, <the original literal>);
   Two properties of that form matter, and both are deliberate:

   1. THE FALLBACK IS THE ORIGINAL VALUE. styles.css is global — index.html
      does not load this file, so --a1-sw-* is undefined there and every swept
      rule resolves to exactly what it painted before. Without the fallback a
      bare var() resolves to nothing and paints transparent, which would have
      broken the live app for anyone whose service worker missed the cache.

   2. THEY ARE DEFINED ONLY UNDER [data-a1-skin]. Aliasing --a1-surface
      directly would have made the sweep unconditional, because --a1-* live on
      :root — and the kill switch would no longer revert anything. Gating the
      aliases here keeps `remove the attribute → old look` true even though
      the edit lives inside a 39k-line file other sessions also work in.
   ========================================================================== */
:root[data-a1-skin] {
  --a1-sw-surface:   var(--a1-surface);
  --a1-sw-surface-2: var(--a1-surface-2);
  --a1-sw-surface-3: var(--a1-surface-3);
  --a1-sw-scrim:     var(--a1-scrim);
  --a1-sw-text:      var(--a1-text);
  --a1-sw-line:      var(--a1-border);
  /* Course chips carry per-course near-white fg via inline style; on the light
     skin the chip text must drop to ink or it vanishes on the light tint. */
  --a1-sw-chip-fg:   var(--a1-text);
  /* The old nav glow was a 40px black smear; on light it reads as dirt. */
  --a1-sw-shadow-up: 0 -4px 16px rgba(24, 24, 22, 0.07);
  --a1-sw-accent-soft: var(--a1-accent-soft);
  /* Overlay tint for the time-wheel selection band: must stay translucent. */
  --a1-sw-wheel-band: color-mix(in srgb, var(--a1-accent) 10%, transparent);
}

/* Explicit dark choice. */
html[data-theme="dark"] {
  --a1-bg:            #131314;
  --a1-surface:       #1B1B1D;
  --a1-surface-2:     #212124;
  --a1-surface-3:     #292A2D;
  --a1-border:        #2E2E32;
  --a1-border-strong: #3E3E44;

  --a1-text:          #F4F4F2;
  --a1-text-2:        #A8A8A4;
  --a1-text-3:        #8B8B87;   /* 5.0:1 on --a1-surface */

  --a1-accent:        #8E9BF0;
  --a1-accent-hover:  #A3AEF5;
  --a1-accent-soft:   #262B45;
  --a1-accent-text:   #A9B3F5;
  --a1-on-accent:     #16161A;

  --a1-urgent-bg: #3A2320;  --a1-urgent-fg: #EFA79B;  --a1-urgent-line: #5A3630;
  --a1-soon-bg:   #38301C;  --a1-soon-fg:   #E6C27C;  --a1-soon-line:   #574828;
  --a1-ok-bg:     #1E3228;  --a1-ok-fg:     #8FCCAC;  --a1-ok-line:     #2F4C3C;
  --a1-muted-bg:  #262629;  --a1-muted-fg:  #A2A29E;  --a1-muted-line:  #35353A;

  /* Dark: the outline is LIGHTER than the fills, and "paper" is a dark
     surface — a white sheet blazing on a dark screen was the problem. */
  --a1-illus-1: #3A4270;
  --a1-illus-2: #403C2F;
  --a1-illus-3: #2C3B34;
  --a1-illus-ink: #9096AE;
  --a1-illus-paper: #24252B;

  --a1-shadow-1: 0 1px 2px rgba(0,0,0,.40);
  --a1-shadow-2: 0 4px 16px rgba(0,0,0,.45);
  --a1-shadow-3: 0 12px 40px rgba(0,0,0,.55);
}

/* Accent palettes stay deliberately narrow: a color choice changes actions,
   active states, and focus rings, while surfaces and semantic status colors
   continue to come from Day/Night. The stored values keep compatibility with
   the original five-theme picker even though their user-facing names are now
   Indigo, Sage, Violet, Amber, and Rose. */
:root[data-a1-skin][data-app-theme="aurora"] {
  --a1-accent:       #4f7b5f;
  --a1-accent-hover: #456e55;
  --a1-accent-soft:  #e7f0e9;
  --a1-accent-text:  #3e674d;
}

:root[data-a1-skin][data-app-theme="violet"] {
  --a1-accent:       #7556c9;
  --a1-accent-hover: #6345b3;
  --a1-accent-soft:  #f0ebfa;
  --a1-accent-text:  #5e43ad;
}

:root[data-a1-skin][data-app-theme="solar"] {
  --a1-accent:       #986414;
  --a1-accent-hover: #895b12;
  --a1-accent-soft:  #faf0da;
  --a1-accent-text:  #815611;
}

:root[data-a1-skin][data-app-theme="crimson"] {
  --a1-accent:       #b14f69;
  --a1-accent-hover: #963e56;
  --a1-accent-soft:  #f8e8ed;
  --a1-accent-text:  #923c55;
}

html[data-a1-skin][data-theme="dark"][data-app-theme="aurora"] {
  --a1-accent:       #82be98;
  --a1-accent-hover: #9acaaa;
  --a1-accent-soft:  #203429;
  --a1-accent-text:  #a8d8b8;
  --a1-on-accent:    #102017;
}

html[data-a1-skin][data-theme="dark"][data-app-theme="violet"] {
  --a1-accent:       #b8a0ef;
  --a1-accent-hover: #c8b5f4;
  --a1-accent-soft:  #31294a;
  --a1-accent-text:  #c9b8f5;
}

html[data-a1-skin][data-theme="dark"][data-app-theme="solar"] {
  --a1-accent:       #e2b963;
  --a1-accent-hover: #edc97f;
  --a1-accent-soft:  #3a311f;
  --a1-accent-text:  #f0cc82;
  --a1-on-accent:    #261b08;
}

html[data-a1-skin][data-theme="dark"][data-app-theme="crimson"] {
  --a1-accent:       #e69aae;
  --a1-accent-hover: #efafbf;
  --a1-accent-soft:  #432731;
  --a1-accent-text:  #f0b5c4;
  --a1-on-accent:    #2a1118;
}

/* ==========================================================================
   LEGACY TOKEN BRIDGE  —  audited against public/styles.css, not guessed
   --------------------------------------------------------------------------
   SCOPE: :root[data-a1-skin], NOT html[data-a1-skin].

   The handoff specified html[data-a1-skin] and called it (0,2,0). It is not.
   Element selectors count in the third column, never the second:

       html[data-a1-skin]                 -> (0,1,1)
       :root[data-app-theme="aurora"]     -> (0,2,0)      WINS
       :root[data-a1-skin]                -> (0,2,0)      ties, later = wins

   With the html- form, every user who has ever picked one of the five colour
   themes keeps the old dark palette — exactly the failure the scope was added
   to prevent. :root[data-a1-skin] carries identical kill-switch semantics
   (drop the attribute on <html> and the whole refresh reverts) at the
   specificity the handoff intended.

   MEASURED FACTS:
   · styles.css consumes 68 distinct tokens. They are NOT all colours:
       46 colour / shadow tokens          -> bridged below
       21 geometry or data, set inline by JS -> DELIBERATELY NOT BRIDGED
     The excluded 21: --lanes --lane --w2m-cols --mg-cols --mg-rows --st-count
     --sg-size --requirement-depth --quick-memory-size --learn-pdf-split
     --workspace-indent --ts-angle --ts-distance --nd-hue --sr-dx --sr-dy
     --sr-scene-image --option-fill --mn-pad-cols --calendar-now-offset
     --calendar-reminder-offset. They carry lane counts, angles, pixel offsets,
     percentages and nesting depth. Following "map every hit to an --a1-*
     token" literally collapses the Calendar lanes, the When2meet grid and the
     Workspace indents.
   · A further 6 (--course-accent --calendar-accent --friend-course-accent
     --friend-course-bg --friend-course-line --friend-course-chip-fg) are
     already declared as var(--cyan) / var(--user-*) in styles.css and follow
     the bridge for free.
   · Bridging caps out at ~44% of colour decisions: 1,914 var() references vs
     2,507 hardcoded literals (756 hex + 1,751 rgba). The literal sweep is a
     separate pass — see TASK 1b at the bottom of this file.
   · The gradient and the glow are literals, not tokens. They die in the
     component layer and in the sweep, never here.
   ========================================================================== */

:root[data-a1-skin] {
  /* -- surfaces ---------------------------------------------------------- */
  --bg:             var(--a1-bg);
  --panel:          var(--a1-surface);
  --panel-2:        var(--a1-surface-2);
  --panel-strong:   var(--a1-surface-3);
  --app-background: var(--a1-bg);      /* was three stacked dark gradients */

  /* -- lines. --line-soft is the third most referenced token in the whole
        file (280 hits). --line and --line-soft stay two distinct weights;
        collapsing both onto --a1-border flattens every nested border. ---- */
  --line:           var(--a1-border-strong);
  --line-soft:      var(--a1-border);
  --line-strong:    var(--a1-border-strong);

  /* -- text -------------------------------------------------------------- */
  --text:           var(--a1-text);
  --muted:          var(--a1-text-2);

  /* -- accent. The fluorescent cyan was doing four unrelated jobs — brand,
        link, active nav, primary CTA — and --violet was a second brand
        colour. Both collapse into the single accent. --------------------- */
  --cyan:           var(--a1-accent);
  --violet:         var(--a1-accent);
  --accent:         var(--a1-accent);
  --accent-strong:  var(--a1-accent-hover);

  /* -- status. Colour is reserved for urgency, so these land on the
        semantic ramp instead of staying decorative. ---------------------- */
  --green:             var(--a1-ok-fg);
  --red:               var(--a1-urgent-fg);
  --amber:             var(--a1-soon-fg);
  --amber-pill:        var(--a1-soon-fg);
  --amber-pill-border: var(--a1-soon-line);

  --shadow:         var(--a1-shadow-2);

  /* -- derived families. These were hard-coded rgba() of the old cyan.
        color-mix is already used elsewhere in styles.css, so it adds no new
        browser requirement. ---------------------------------------------- */
  --course-bg:      var(--a1-accent-soft);
  --course-line:    color-mix(in srgb, var(--a1-accent) 38%, transparent);
  --course-chip-bg: var(--a1-accent-soft);
  --course-chip-fg: var(--a1-accent-text);

  --calendar-bg:    var(--a1-accent-soft);
  --calendar-line:  color-mix(in srgb, var(--a1-accent) 38%, transparent);

  --chat-avatar-bg: var(--a1-illus-1);
  --sr-duel-answer-color: var(--a1-accent-text);

  /* Numeric triple — read as rgba(var(--metric-rgb), .38). It must stay a
     triple; a colour here yields an invalid rgba() and the rule is dropped. */
  --metric-rgb:     110, 110, 105;
  --metric-accent:  var(--a1-text-3);
  --metric-subtle:  var(--a1-surface-3);
  --metric-ink:     var(--a1-text);
}

/* ==========================================================================
   LEGACY PALETTE CONTAINMENT
   --------------------------------------------------------------------------
   styles.css carries TWO independent theme systems, both entirely dark:

     :root[data-app-theme="graphite|aurora|violet|solar|crimson"]  (l.38–131)
       — each is a full 14-token palette override, not just a background
     .user-page[data-user-theme="aurora|campus|solar|violet|crimson"] (l.17503+)
       — a SECOND system the handoff does not mention, scoped to the User page

   The current picker changes only the --a1-accent family above. This block
   keeps the old stored theme values from reviving their former dark surfaces,
   text, shadows, and semantic colors when the app is in Day mode.

   Specificity: (0,3,0) — beats :root[data-app-theme="aurora"] (0,2,0)
   outright rather than relying on load order.
   ========================================================================== */

:root[data-a1-skin][data-app-theme] {
  --bg:             var(--a1-bg);
  --panel:          var(--a1-surface);
  --panel-2:        var(--a1-surface-2);
  --line:           var(--a1-border-strong);
  --line-soft:      var(--a1-border);
  --text:           var(--a1-text);
  --muted:          var(--a1-text-2);
  --cyan:           var(--a1-accent);
  --violet:         var(--a1-accent);
  --green:          var(--a1-ok-fg);
  --red:            var(--a1-urgent-fg);
  --amber:          var(--a1-soon-fg);
  --amber-pill:     var(--a1-soon-fg);
  --amber-pill-border: var(--a1-soon-line);
  --shadow:         var(--a1-shadow-2);
  --app-background: var(--a1-bg);
}

/* The second theme system. Base and themed variants at matching weight. */
:root[data-a1-skin] .user-page,
:root[data-a1-skin] .user-page[data-user-theme] {
  --user-text:         var(--a1-text);
  --user-muted:        var(--a1-text-2);
  --user-line:         var(--a1-border);   /* two of its three declarations
                                              were literal rgba() */
  --user-accent:       var(--a1-accent);
  --user-accent-2:     var(--a1-accent-hover);
  --user-surface-bg:   var(--a1-surface);
  --user-surface-soft: var(--a1-surface-2);
  --user-panel-bg:     var(--a1-surface);
  --user-course-bg:    var(--a1-surface-2);
  --user-primary-ink:  var(--a1-on-accent);
}
/* --friend-course-* already derive from --user-accent / --user-line /
   --user-text in styles.css, so they follow the block above for free. */

/* KNOWN HOLDOUTS — not fixable from this file, they need a code patch:
   · index.html inline <style> pins html background to var(--bg, #0c1018),
     the PWA white-edge fix in commit 09f05c1. Per DECISION 1 the fallback
     must become theme-aware. Coordinate with the owner of that commit — it
     edits their fix, it does not revert it.
   · TASK 1b, the literal sweep. ~2,507 hardcoded colours; the ones that
     matter, by count:
         rgba(53,212,255,*) + #35d4ff  441   the fluorescent cyan
         rgba(255,255,255,*)           193   light overlays
         rgba(0,0,0,*)                 106   dark overlays
         #101722 / #0f1520              48   dark panel grounds
         text-transform: uppercase      62 rules
         cyan glow box-shadow           21 rules
     87 unique literals cover 978 occurrences (39%). Fills map to
     --a1-accent-soft, hairlines to --a1-border. One reviewed commit per
     colour family, never one big sed.
*/

/* ---- Base elements ------------------------------------------------------ */

/* Every a1- component is border-box, whether or not the host page has a
   global reset. Without this, `width:100%` + padding overflows to the right
   and the trailing pill loses its inset. */
[class^="a1-"], [class*=" a1-"],
[class^="a1-"]::before, [class*=" a1-"]::before,
[class^="a1-"]::after, [class*=" a1-"]::after { box-sizing: border-box; }

.a1, .a1 *, .a1-scope, .a1-scope * { box-sizing: border-box; }

/* Both themes ship; Day is the safe default. color-scheme also makes form
   controls and scrollbars follow the resolved theme. */
html { color-scheme: light; }
html[data-theme="light"] { color-scheme: light; }
html[data-theme="dark"]  { color-scheme: dark; }

body {
  background: var(--a1-bg);
  color: var(--a1-text);
  font-family: var(--a1-font);
  font-size: var(--a1-fs-body);
  line-height: var(--a1-lh-body);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

a { color: var(--a1-accent-text); text-decoration: none; }
a:hover { color: var(--a1-accent); text-decoration: underline; }

:focus-visible { outline: 2px solid var(--a1-accent); outline-offset: 2px; }

/* Text controls already flip their border to the accent (plus a soft glow) on
   focus; stacking the offset outline on top drew two concentric rings. */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: none;
}

/* Type roles. Use these instead of ad-hoc font-size declarations. */
.a1-t-display { font-size: var(--a1-fs-display); font-weight: 800; line-height: var(--a1-lh-tight); letter-spacing: -.015em; }
.a1-t-title   { font-size: var(--a1-fs-h1); font-weight: 700; line-height: var(--a1-lh-tight); }
.a1-t-section { font-size: var(--a1-fs-h2); font-weight: 700; }
.a1-t-body    { font-size: var(--a1-fs-body); font-weight: 500; }
.a1-t-meta    { font-size: var(--a1-fs-sm); font-weight: 500; color: var(--a1-text-2); font-family: var(--a1-font-alt); }
.a1-t-micro   { font-size: var(--a1-fs-xs); font-weight: 600; color: var(--a1-text-3); letter-spacing: .04em; text-transform: uppercase; }
.a1-dim       { color: var(--a1-text-2); }
.a1-dimmer    { color: var(--a1-text-3); }
.a1-truncate  { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
