/* Transcripts — the token layer. THE single source of truth for colour, type, space,
   radius, elevation, motion, layering and layout.

   Every page links this file before its own <style>; no page redeclares a token.
   It used to be inlined six times and had already drifted — index.html and shared.html
   disagreed on --line, index.html and login.html on --warm. test/design-tokens.test.js
   enforces the single copy.

   The rules these values encode (why cyan splits in two, why yellow is never text, why
   green means only "synced") are in docs/plans/2026-08-20-cyan-design-system-adaptive-shell.md.
   Read them before editing a colour. */

:root{
  /* ---------------------------------------------------------------- colour */
  --bg:#f5f8f9;
  --surface:#ffffff;
  --surface-sunk:#eaf0f2;
  --ink:#16181d;
  --muted:#565d6a;
  /* Darkened from the palette draft #878e9b (3.1:1). This token carries real text —
     card meta, section labels, timestamps — so it has to clear 4.5:1 on every surface
     it lands on, including the sunk segmented-control track. Measured in
     test/design-tokens.test.js. */
  --faint:#686d77;
  --line:#e2e8ea;
  --line-strong:#c6d0d4;

  /* Cyan does two jobs and they are not interchangeable. --brand is 3.7:1 on white:
     enough for a graphic, NOT enough for text. Anything that carries text uses
     --brand-fill (white on it = 4.6:1); cyan text on a light surface uses --brand-deep. */
  --brand:#0891b2;
  --brand-fill:#0c7fa3;
  --brand-deep:#0b7490;
  --on-brand:#ffffff;

  /* Graphic-only progress colours. The cool sequence moves from file handling to
     voice analysis to the cyan core task; amber means a temporary wait, not failure.
     Each clears 3:1 against --line, the unfilled part of the progress track. */
  --progress-prepare:#2563eb;
  --progress-voices:#7c3aed;
  --progress-transcribe:#0c7fa3;
  --progress-retry:#9a6700;

  /* Fills only, never text and never under text. --zest-deep is the star glyph, and it
     needs the --zest-edge hairline to clear the 3:1 floor for a UI component on white. */
  --zest:#ffcf33;
  --zest-deep:#f5b700;
  --zest-edge:#b98900;

  /* Green is no longer the brand. It means synced / success and nothing else. */
  --success:#17a34a;
  --success-deep:#0e7a39;
  --danger:#c13e3e;   /* was #d64545 = 4.38:1; delete/error copy needs 4.5:1 */
  --notice-bg:#fffbeb; --notice-line:#ffe9a3; --notice-ink:#8a6d1a;

  color-scheme:light;

  /* ------------------------------------------------------------------ type */
  --text-xs:11.5px;
  --text-sm:12.5px;
  --text-base:14px;
  --text-md:15px;
  --text-lg:17px;
  --text-xl:21px;
  --text-display:clamp(22px, 1.1vw + 18px, 30px);
  --text-read:18px;              /* transcript prose — content, not UI */
  --font-action:14px;            /* reader action chips */

  /* landing.html is marketing typography and does not belong on the UI scale. */
  --display-1:clamp(30px, 4.4vw, 46px);
  --display-2:26px;
  --display-3:20px;
  --display-figure:40px;

  --leading-tight:1.25;
  --leading-ui:1.55;
  --leading-read:1.82;
  --weight-normal:400; --weight-medium:500; --weight-semi:600; --weight-bold:700;

  /* No --letter-spacing token exists, at any size, on purpose: tracking breaks Arabic
     and Persian letter joining, and a token would make that reusable. */

  /* ----------------------------------------------------------------- space */
  --space-1:4px;  --space-2:8px;  --space-3:12px; --space-4:16px;
  --space-5:20px; --space-6:24px; --space-8:32px; --space-10:40px;
  --space-12:48px; --space-16:64px;
  --turn-gap:var(--space-6);

  --radius-sm:8px; --radius-control:12px; --radius-card:14px;
  --radius-page:18px; --radius-sheet:20px; --radius-pill:999px;

  --shadow-sm:0 1px 2px rgba(26,24,21,.04), 0 2px 8px rgba(26,24,21,.05);
  --shadow-lift:0 2px 10px rgba(26,24,21,.08);
  --shadow-dialog:0 24px 60px rgba(26,24,21,.18);
  --shadow-sheet:0 -8px 40px rgba(26,24,21,.18);
  /* Marketing elevation. landing.html needs a genuinely lifted card where the app wants
     a flat one; it lived in that page's own :root until this file became the SSOT. */
  --shadow-flat:0 1px 2px rgba(16,24,40,.05);
  --shadow-card:0 12px 32px rgba(16,24,40,.09), 0 1px 3px rgba(16,24,40,.05);
  --shadow-card-hover:0 18px 44px rgba(16,24,40,.12), 0 1px 3px rgba(16,24,40,.05);

  --dur-fast:.15s; --dur-base:.26s;
  --ease-out:cubic-bezier(.32,.72,0,1);

  --z-reader:15; --z-header:20; --z-scrim:40; --z-sheet:50;
  --z-toast:80; --z-lock:90; --z-gate:95;

  /* ---------------------------------------------------------------- layout */
  --shell-max:1440px;                 /* content cap; gutters absorb the rest */
  --shell-gutter:clamp(16px, 3vw, 40px);
  --rail-width:320px;
  --col-gap:clamp(16px, 2vw, 32px);
  --reader-shell-max:1040px;
  --reader-prose-max:960px;           /* a wide shell must not widen the measure */
  --reader-header-height:56px;        /* fallback; measured at runtime, see syncReaderHeaderHeight() */
}

@media (prefers-color-scheme: dark){
  :root{
    --bg:#15181a;
    --surface:#1e2225;
    --surface-sunk:#262b2f;
    --ink:#f0f4f5;
    --muted:#9aa4a9;
    --faint:#8a9297;
    --line:#2c3236;
    --line-strong:#3e474c;

    /* Cyan is lifted for dark surfaces, which makes it light enough to carry ink
       rather than white — so --on-brand flips to near-black. */
    --brand:#22b8d9;
    --brand-fill:#22b8d9;
    --brand-deep:#5ecfe8;
    --on-brand:#06222b;

    --progress-prepare:#60a5fa;
    --progress-voices:#a78bfa;
    --progress-transcribe:#22b8d9;
    --progress-retry:#f2b84b;

    --zest:#ffcf33; --zest-deep:#ffd24d; --zest-edge:#8a6d1a;

    --success:#2fc46c; --success-deep:#7ddfa4;
    --danger:#e16e65;
    --notice-bg:#272215; --notice-line:#453b22; --notice-ink:#d9bc72;

    --shadow-sm:0 1px 2px rgba(0,0,0,.35);
    --shadow-lift:0 2px 12px rgba(0,0,0,.45);
    /* A raised marketing card has to be dimmed on a dark page or it reads as a grey halo. */
    --shadow-flat:0 1px 2px rgba(0,0,0,.35);
    --shadow-card:0 12px 32px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.3);
    --shadow-card-hover:0 18px 44px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.3);

    color-scheme:dark;
  }
}

/* Derived values are declared HERE and never mixed at a call site. Custom properties
   substitute lazily, so one declaration re-resolves against whichever theme is active;
   a hand-mixed copy at a call site keeps its light-mode value on a dark surface. */
:root{
  --brand-tint:color-mix(in srgb, var(--brand) 13%, var(--surface));
  --brand-line:color-mix(in srgb, var(--brand) 40%, transparent);
  --brand-wash:color-mix(in srgb, var(--brand) 7%, var(--surface));
  --progress-current:var(--progress-prepare);
  --zest-tint:color-mix(in srgb, var(--zest) 16%, var(--surface));
  --success-tint:color-mix(in srgb, var(--success) 12%, var(--surface));
  --danger-tint:color-mix(in srgb, var(--danger) 10%, var(--surface));
  --danger-pulse:color-mix(in srgb, var(--danger) 14%, transparent);
  --icon:var(--muted);
  --header-bg:color-mix(in srgb, var(--bg) 90%, transparent);
  --focus-ring:0 0 0 3px color-mix(in srgb, var(--brand) 35%, transparent);
  --mark:color-mix(in srgb, var(--zest) 45%, transparent);

  /* Interaction states. A state is a token for the same reason a palette colour is:
     a hand-mixed copy at a call site keeps its light-mode value on a dark surface.
     Mixed against --ink rather than a constant, so the same declaration darkens on a
     light theme and LIFTS on a dark one, where a darker press reads as a recess.
     --surface-pressed must stay distinct from --surface-sunk: inside .seg and .btnrow
     the track IS --surface-sunk, so a press painted with it is invisible on its own
     container. */
  --surface-hover:color-mix(in srgb, var(--ink) 6%, var(--surface));
  --surface-pressed:color-mix(in srgb, var(--ink) 12%, var(--surface));
  --ink-fill-hover:color-mix(in srgb, var(--ink) 88%, var(--bg));
  --brand-fill-hover:var(--brand-deep);
  /* Selection, from the trio the rest of the app already uses (.setnavitem, speaker
     assign). The segmented control used to raise a --surface pill instead, which
     inverts on dark. */
  --selected-bg:var(--brand-tint);
  --selected-line:var(--brand-line);
  --selected-ink:var(--brand-deep);
  --disabled-opacity:.5;
  /* A scrim darkens. It must NOT derive from --ink: --ink is near-white in dark mode,
     so a mix over it veiled the page in white and made the background LIGHTER behind
     an open dialog. Anchored to a dark constant in both themes instead. */
  --scrim:rgba(10,12,15,.45);
}
@media (prefers-color-scheme: dark){
  :root{
    /* Only the tints restate: a 13% wash that separates on white disappears on a dark
       card, and the border has to carry more of the distinction there. */
    --brand-tint:color-mix(in srgb, var(--brand) 20%, var(--surface));
    --brand-line:color-mix(in srgb, var(--brand) 50%, transparent);
    --zest-tint:color-mix(in srgb, var(--zest) 13%, var(--surface));
    /* A dark page needs a heavier veil to read as "behind" at all. */
    --scrim:rgba(0,0,0,.62);
  }
}

/* A 320px phone at 200% text scale has ~160 effective CSS pixels, where the reader
   header wraps to several rows. Fallback only — syncReaderHeaderHeight() sets the
   measured height as an inline style on <html>, which beats this. */
@media (max-width:260px){
  :root{--reader-header-height:112px;}
}
