/* ==========================================================================
   Wisdom Vault — "The Private Athenaeum"
   00-tokens: design tokens, reset, base typography
   ========================================================================== */

:root {
  --serif: "Iowan Old Style", "Palatino Nova", Palatino, "Book Antiqua",
    "URW Palladio L", P052, Charter, Georgia, serif;
  --sans: "Avenir Next", Avenir, Seravek, "Segoe UI", "Helvetica Neue",
    Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Daylight reading room */
  --paper: #f4efe3;
  --paper-2: #ede6d4;
  --paper-3: #e5dcc6;
  --ink: #26221a;
  /* ink-soft/ink-faint deepened one step 2026-07 (sol56 V1 #2): faint text
     — row subs, captions, labels, empty-state copy — was reading ~2.9:1
     against --paper, well under small-text AA. Same hue, darker lightness:
     ink-soft now ~5.9:1 (was 5.2), ink-faint ~4.5:1 (was 2.9). --line(-soft)
     nudged the same one step for border legibility; parchment hue held. */
  --ink-soft: #625b4c;
  --ink-faint: #676152; /* rebased 2026-07-21 (AIL r2 adjudication #2 —
    the card-metadata token: domain labels like "Influence", .row-sub,
    .spine-no render it at 11-13px on --paper-2/--paper-3, the cream
    CARD surfaces, not just --paper): was #736c5b, 4.19:1 on --paper-2
    and 3.82:1 on --paper-3 (both sub-AA despite 4.55 on --paper itself)
    -> 4.95/4.51/5.37 on paper-2/paper-3/paper respectively. Same hue,
    darker only. */
  --line: rgba(38, 34, 26, 0.21);
  --line-soft: rgba(38, 34, 26, 0.12);
  --accent: #8c3b2a;
  --accent-soft: rgba(140, 59, 42, 0.1);
  --gilt: #a67c37;
  --gilt-soft: rgba(166, 124, 55, 0.16);
  --ok: #4a6741;
  --shadow: 0 1px 2px rgba(38, 34, 26, 0.06), 0 8px 28px rgba(38, 34, 26, 0.08);
  --shadow-lift: 0 2px 4px rgba(38, 34, 26, 0.08), 0 14px 44px rgba(38, 34, 26, 0.14);
  --grain-alpha: 0.05;

  /* ---- skin axis (orthogonal to theme) --------------------------------
     Typography/density/ornament defaults — this is the "athenaeum" skin's
     contract. [data-skin="..."] overrides in 05-skins.css redefine these;
     [data-skin="athenaeum"] is intentionally absent from that file because
     it is identical to the defaults declared right here (single source of
     truth — see 05-skins.css header comment). */
  --font-body: var(--serif);
  --font-display: var(--serif);
  --body-size: 17px;
  --body-leading: 1.62;
  --heading-leading: 1.25;
  --content-width: 720px;
  --row-pad: 15px 6px;
  --rule-bg: linear-gradient(90deg, var(--gilt), transparent 70%);
  --dropcap-size: 3.1em;
  --dropcap-lh: 0.82;
  --dropcap-float: left;
  --dropcap-pad: 6px 8px 0 0;
  --dropcap-color: var(--accent);
  --dropcap-weight: 600;
}

/* ---- theme packs -------------------------------------------------------
   Eighteen selectable token sets, swapped via data-theme on :root.
   :root above is Athenaeum Light; [data-theme="dark"] is Athenaeum Dark;
   sepia / slate / contrast / forest / midnight / manuscript (settings label:
   Vellum) / wine / fjord / phosphor / iris / quartz / ocean / ember / moss /
   plum / carbon follow.
   Every pack redefines the full contract: paper(×3) / ink(×3) / line(×2) /
   accent / gilt / ok / shadows. Body ink is checked to ≥7:1 contrast
   against paper (WCAG AAA) for every pack. Dark packs must ALSO be added
   to the dark-chip selector list in 35-map.css (atlas chips flip to a
   66%-lightness variant there — see the comment on that list). */

[data-theme="dark"] {
  /* Athenaeum Dark — lamplight (ink-soft/ink-faint/line lightened one
     step 2026-07, same rationale as the light pack above: ink-faint was
     ~4.0:1 against --paper, now ~4.6:1; ink-soft ~7.0:1 -> ~7.9:1) */
  --paper: #171310;
  --paper-2: #201a15;
  --paper-3: #292219;
  --ink: #e9e1cd;
  --ink-soft: #b2a994;
  --ink-faint: #948973; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #897f69, 3.97:1 on --paper-3 (worst real card surface) -> 4.55:1.
    Same hue, lighter only. */
  --line: rgba(233, 225, 205, 0.21);
  --line-soft: rgba(233, 225, 205, 0.12);
  --accent: #d29a4d;
  --accent-soft: rgba(210, 154, 77, 0.13);
  --gilt: #c9974b;
  --gilt-soft: rgba(201, 151, 75, 0.16);
  --ok: #8aa07f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.45);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.55);
  --grain-alpha: 0.04;
}

[data-theme="sepia"] {
  /* Sepia — warm long-reading, aged golden paper (deepened 2026-07 so it
     no longer twins with the paler Vellum/manuscript parchment;
     ink/paper contrast 9.9:1). --ink-faint deepened one more step
     2026-07-21 (AIL r1 code-lane #3): was #93805d, 2.91:1 against
     --paper — well under small-text AA (row subs render it at 12px).
     Same hue/saturation, darker lightness only: #716248, 4.50:1.
     AIL r2 (fable F9): that rebase hit --paper only (zero margin); row-
     subs actually render inside .card/.card.today-card, whose ground is
     --paper-2 (4.04:1) or --paper-3 (3.58:1), both sub-AA. Deepened one
     more step: #60533d — 5.10/4.53/5.69 on paper/paper-2/paper-3. */
  --paper: #f0dfb8;
  --paper-2: #e9d3a2;
  --paper-3: #dfc78e;
  --ink: #3c2f1c;
  --ink-soft: #6e5b3e;
  --ink-faint: #60533d;
  --line: rgba(60, 47, 28, 0.2);
  --line-soft: rgba(60, 47, 28, 0.1);
  --accent: #94481d;
  --accent-soft: rgba(148, 72, 29, 0.12);
  --gilt: #8a6423;
  --gilt-soft: rgba(138, 100, 35, 0.18);
  --ok: #5b6e3d;
  --shadow: 0 1px 2px rgba(60, 47, 28, 0.07), 0 8px 28px rgba(60, 47, 28, 0.1);
  --shadow-lift: 0 2px 4px rgba(60, 47, 28, 0.09), 0 14px 44px rgba(60, 47, 28, 0.16);
  --grain-alpha: 0.07;
}

[data-theme="slate"] {
  /* Slate — cool neutral, blue-grey study */
  --paper: #1b1f24;
  --paper-2: #22272e;
  --paper-3: #2b323b;
  --ink: #dde3ea;
  --ink-soft: #9aa5b1;
  --ink-faint: #919ba6; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #707c8a, 3.05:1 on --paper-3 (worst real card surface) -> 4.59:1.
    Same hue, lighter only. */
  --line: rgba(221, 227, 234, 0.16);
  --line-soft: rgba(221, 227, 234, 0.08);
  --accent: #8ab4d8;
  --accent-soft: rgba(138, 180, 216, 0.13);
  --gilt: #c0aa72;
  --gilt-soft: rgba(192, 170, 114, 0.16);
  --ok: #8fbf9f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.42);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.52);
  --grain-alpha: 0.035;
}

[data-theme="contrast"] {
  /* High Contrast — WCAG-AAA-oriented, print-sharp */
  --paper: #ffffff;
  --paper-2: #ffffff;
  --paper-3: #ededed;
  --ink: #000000;
  --ink-soft: #1f1f1f;
  --ink-faint: #3d3d3d;
  --line: rgba(0, 0, 0, 0.6);
  --line-soft: rgba(0, 0, 0, 0.28);
  --accent: #8b0000;
  --accent-soft: rgba(139, 0, 0, 0.09);
  --gilt: #5c4400;
  --gilt-soft: rgba(92, 68, 0, 0.13);
  --ok: #0a5c2e;
  --shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
  --shadow-lift: 0 0 0 2px rgba(0, 0, 0, 0.55);
  --grain-alpha: 0;
}

[data-theme="forest"] {
  /* Forest — deep green reading lodge */
  --paper: #131a14;
  --paper-2: #1a231b;
  --paper-3: #233024;
  --ink: #e2e8dc;
  --ink-soft: #a3b09a;
  --ink-faint: #8a9983; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #75856e, 3.51:1 on --paper-3 (worst real card surface) -> 4.58:1.
    Same hue, lighter only. */
  --line: rgba(226, 232, 220, 0.16);
  --line-soft: rgba(226, 232, 220, 0.08);
  --accent: #a8c686;
  --accent-soft: rgba(168, 198, 134, 0.13);
  --gilt: #c9b458;
  --gilt-soft: rgba(201, 180, 88, 0.15);
  --ok: #8aa07f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.42);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.52);
  --grain-alpha: 0.05;
}

[data-theme="midnight"] {
  /* Midnight — deep blue-black, cool ink (ink/paper contrast 14.7:1) */
  --paper: #0b0e15;
  --paper-2: #10141c;
  --paper-3: #161b26;
  --ink: #d9e1ee;
  --ink-soft: #94a1b8;
  --ink-faint: #79839d; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #66718c, 3.53:1 on --paper-3 (worst real card surface) -> 4.55:1.
    Same hue, lighter only. */
  --line: rgba(217, 225, 238, 0.16);
  --line-soft: rgba(217, 225, 238, 0.08);
  --accent: #6f9bd8;
  --accent-soft: rgba(111, 155, 216, 0.13);
  --gilt: #c3934a;
  --gilt-soft: rgba(195, 147, 74, 0.16);
  --ok: #82ab86;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.45);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.55);
  --grain-alpha: 0.035;
}

[data-theme="manuscript"] {
  /* Manuscript — warm parchment light, brown ink (ink/paper contrast 13.2:1) */
  --paper: #f4ead2;
  --paper-2: #ecdfc0;
  --paper-3: #e1d0a8;
  --ink: #2e2013;
  --ink-soft: #5c4a34;
  --ink-faint: #68573f; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #8a7454, 2.93:1 on --paper-3 (worst real card surface) -> 4.56:1.
    Same hue, darker only. */
  --line: rgba(46, 32, 19, 0.18);
  --line-soft: rgba(46, 32, 19, 0.09);
  --accent: #7a3b12;
  --accent-soft: rgba(122, 59, 18, 0.11);
  --gilt: #8f6a24;
  --gilt-soft: rgba(143, 106, 36, 0.16);
  --ok: #4f6b34;
  --shadow: 0 1px 2px rgba(46, 32, 19, 0.07), 0 8px 28px rgba(46, 32, 19, 0.1);
  --shadow-lift: 0 2px 4px rgba(46, 32, 19, 0.09), 0 14px 44px rgba(46, 32, 19, 0.16);
  --grain-alpha: 0.08;
}

[data-theme="wine"] {
  /* Wine — dark claret; red-leaning paper + rosé accent (pulled off pink
     2026-07 so it separates from Plum's orchid aubergine;
     ink/paper contrast 13.7:1) */
  --paper: #211013;
  --paper-2: #2a1418;
  --paper-3: #351a1f;
  --ink: #ecdbde;
  --ink-soft: #af8f96;
  --ink-faint: #9b838b; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #806870, 3.13:1 on --paper-3 (worst real card surface) -> 4.56:1.
    Same hue, lighter only. */
  --line: rgba(236, 219, 222, 0.16);
  --line-soft: rgba(236, 219, 222, 0.08);
  --accent: #d5787c;
  --accent-soft: rgba(213, 120, 124, 0.13);
  --gilt: #caa15a;
  --gilt-soft: rgba(202, 161, 90, 0.16);
  --ok: #8aab86;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.42);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.52);
  --grain-alpha: 0.045;
}

[data-theme="fjord"] {
  /* Fjord — cool nordic gray-blue, light (ink/paper contrast 13.6:1) */
  --paper: #e7edf1;
  --paper-2: #dde5eb;
  --paper-3: #cfdae3;
  --ink: #16232c;
  --ink-soft: #47606f;
  --ink-faint: #506069; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #718793, 2.65:1 on --paper-3 (worst real card surface) -> 4.60:1.
    Same hue, darker only. */
  --line: rgba(22, 35, 44, 0.18);
  --line-soft: rgba(22, 35, 44, 0.09);
  --accent: #2f6690;
  --accent-soft: rgba(47, 102, 144, 0.1);
  --gilt: #83693a;
  --gilt-soft: rgba(131, 105, 58, 0.15);
  --ok: #3e6b4c;
  --shadow: 0 1px 2px rgba(22, 35, 44, 0.06), 0 8px 28px rgba(22, 35, 44, 0.08);
  --shadow-lift: 0 2px 4px rgba(22, 35, 44, 0.08), 0 14px 44px rgba(22, 35, 44, 0.14);
  --grain-alpha: 0.04;
}

[data-theme="phosphor"] {
  /* Phosphor — near-black, soft restrained green ink (ink/paper contrast 12.0:1) */
  --paper: #0a0d0a;
  --paper-2: #0f130f;
  --paper-3: #151a15;
  --ink: #a9d6ac;
  --ink-soft: #7ba181;
  --ink-faint: #688974; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #567160, 3.30:1 on --paper-3 (worst real card surface) -> 4.55:1.
    Same hue, lighter only. */
  --line: rgba(169, 214, 172, 0.14);
  --line-soft: rgba(169, 214, 172, 0.07);
  --accent: #6fae7a;
  --accent-soft: rgba(111, 174, 122, 0.12);
  --gilt: #b99a55;
  --gilt-soft: rgba(185, 154, 85, 0.15);
  --ok: #7bbf83;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 8px 28px rgba(0, 0, 0, 0.5);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.55), 0 14px 44px rgba(0, 0, 0, 0.6);
  --grain-alpha: 0.03;
}

[data-theme="iris"] {
  /* Iris — dusk violet, dark (ink/paper contrast 13.9:1) */
  --paper: #150f1e;
  --paper-2: #1c1526;
  --paper-3: #241b30;
  --ink: #e3daf0;
  --ink-soft: #a293ba;
  --ink-faint: #8c82a1; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #75698c, 3.26:1 on --paper-3 (worst real card surface) -> 4.57:1.
    Same hue, lighter only. */
  --line: rgba(227, 218, 240, 0.16);
  --line-soft: rgba(227, 218, 240, 0.08);
  --accent: #a480d9;
  --accent-soft: rgba(164, 128, 217, 0.14);
  --gilt: #c9a24d;
  --gilt-soft: rgba(201, 162, 77, 0.16);
  --ok: #8ab08a;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.42);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.52);
  --grain-alpha: 0.045;
}

[data-theme="quartz"] {
  /* Quartz — soft rose-grey, light (ink/paper contrast 13.0:1) */
  --paper: #f0e9ea;
  --paper-2: #e8dfe1;
  --paper-3: #ddd1d3;
  --ink: #2a2128;
  --ink-soft: #5e4f57;
  --ink-faint: #64585d; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #8a7a80, 2.73:1 on --paper-3 (worst real card surface) -> 4.57:1.
    Same hue, darker only. */
  --line: rgba(42, 33, 40, 0.16);
  --line-soft: rgba(42, 33, 40, 0.08);
  --accent: #a8506b;
  --accent-soft: rgba(168, 80, 107, 0.1);
  --gilt: #9c7a4a;
  --gilt-soft: rgba(156, 122, 74, 0.15);
  --ok: #5c7a52;
  --shadow: 0 1px 2px rgba(42, 33, 40, 0.06), 0 8px 28px rgba(42, 33, 40, 0.08);
  --shadow-lift: 0 2px 4px rgba(42, 33, 40, 0.08), 0 14px 44px rgba(42, 33, 40, 0.14);
  --grain-alpha: 0.05;
}

[data-theme="ocean"] {
  /* Ocean — deep sea-teal, dark (papers pulled tealward 2026-07 so the
     pack reads "sea", not a twin of Midnight's blue-black;
     ink/paper contrast 14.0:1) */
  --paper: #081a21;
  --paper-2: #0d222b;
  --paper-3: #132c36;
  --ink: #d4e8ec;
  --ink-soft: #93aebd;
  --ink-faint: #7c94a0; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #6a8492, 3.70:1 on --paper-3 (worst real card surface) -> 4.58:1.
    Same hue, lighter only. */
  --line: rgba(212, 232, 236, 0.16);
  --line-soft: rgba(212, 232, 236, 0.08);
  --accent: #4fa3c9;
  --accent-soft: rgba(79, 163, 201, 0.13);
  --gilt: #c9a24d;
  --gilt-soft: rgba(201, 162, 77, 0.16);
  --ok: #7fb08a;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.45);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.55);
  --grain-alpha: 0.035;
}

[data-theme="ember"] {
  /* Ember — coal-red charcoal, hot amber accents, dark (papers deepened
     redward + accent heated 2026-07 so it separates from Athenaeum Dark's
     lamplight; ink/paper contrast 13.0:1) */
  --paper: #211410;
  --paper-2: #2a1a14;
  --paper-3: #35221a;
  --ink: #ecd9b8;
  --ink-soft: #b8a281;
  --ink-faint: #9d8b6f; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #8c7a5f, 3.63:1 on --paper-3 (worst real card surface) -> 4.56:1.
    Same hue, lighter only. */
  --line: rgba(236, 217, 184, 0.16);
  --line-soft: rgba(236, 217, 184, 0.08);
  --accent: #e68a33;
  --accent-soft: rgba(230, 138, 51, 0.14);
  --gilt: #d9a544;
  --gilt-soft: rgba(217, 165, 68, 0.17);
  --ok: #8fae6c;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.42), 0 8px 28px rgba(0, 0, 0, 0.46);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.52), 0 14px 44px rgba(0, 0, 0, 0.56);
  --grain-alpha: 0.045;
}

[data-theme="moss"] {
  /* Moss — sage, light (ink/paper contrast 12.7:1) */
  --paper: #eaeee0;
  --paper-2: #e1e6d3;
  --paper-3: #d4dcc2;
  --ink: #1f2a1a;
  --ink-soft: #4c5a41;
  --ink-faint: #57614e; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #78876c, 2.70:1 on --paper-3 (worst real card surface) -> 4.59:1.
    Same hue, darker only. */
  --line: rgba(31, 42, 26, 0.18);
  --line-soft: rgba(31, 42, 26, 0.09);
  --accent: #5c7a3e;
  --accent-soft: rgba(92, 122, 62, 0.1);
  --gilt: #8a7327;
  --gilt-soft: rgba(138, 115, 39, 0.15);
  --ok: #4a7a4f;
  --shadow: 0 1px 2px rgba(31, 42, 26, 0.06), 0 8px 28px rgba(31, 42, 26, 0.09);
  --shadow-lift: 0 2px 4px rgba(31, 42, 26, 0.08), 0 14px 44px rgba(31, 42, 26, 0.15);
  --grain-alpha: 0.055;
}

[data-theme="plum"] {
  /* Plum — rich aubergine, dark (ink/paper contrast 14.2:1) */
  --paper: #1a0e18;
  --paper-2: #22131f;
  --paper-3: #2c1a28;
  --ink: #ecdbe8;
  --ink-soft: #af93a9;
  --ink-faint: #968291; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #7f6a7a, 3.30:1 on --paper-3 (worst real card surface) -> 4.59:1.
    Same hue, lighter only. */
  --line: rgba(236, 219, 232, 0.16);
  --line-soft: rgba(236, 219, 232, 0.08);
  --accent: #c168a0;
  --accent-soft: rgba(193, 104, 160, 0.13);
  --gilt: #c9974f;
  --gilt-soft: rgba(201, 151, 79, 0.16);
  --ok: #8aab86;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.44);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 44px rgba(0, 0, 0, 0.54);
  --grain-alpha: 0.045;
}

[data-theme="carbon"] {
  /* Carbon — neutral graphite with brass fittings, dark (gilt retuned from
     pure grey #9a9a9a 2026-07: grey sat 1 step from --ink-soft, so the
     load-bearing ◆ / tier chips lost their signal; a desaturated brass
     keeps the pack near-monochrome but the marker alive;
     ink/paper contrast 15.0:1) */
  --paper: #121212;
  --paper-2: #191919;
  --paper-3: #212121;
  --ink: #e6e6e6;
  --ink-soft: #a3a3a3;
  --ink-faint: #898989; /* rebased 2026-07-21 (AIL r3 lane G, finding #1):
    was #737373, 3.40:1 on --paper-3 (worst real card surface) -> 4.60:1.
    Same hue (neutral), lighter only. */
  --line: rgba(230, 230, 230, 0.14);
  --line-soft: rgba(230, 230, 230, 0.07);
  --accent: #6e9bc4;
  --accent-soft: rgba(110, 155, 196, 0.12);
  --gilt: #a5936c;
  --gilt-soft: rgba(165, 147, 108, 0.15);
  --ok: #7fae7f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 8px 28px rgba(0, 0, 0, 0.5);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.55), 0 14px 44px rgba(0, 0, 0, 0.6);
  --grain-alpha: 0.025;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { height: 100%; }

body {
  min-height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: var(--body-leading);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* paper grain — pure data-URI turbulence, no network */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: var(--grain-alpha);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

input, select {
  font: inherit;
  color: inherit;
}

::selection { background: var(--gilt-soft); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--heading-leading);
}

/* small-caps utility label — the app's chrome voice.
   Also carried by real h2/h3 section headings, so it must neutralise the
   browser's heading margins and size rather than inherit them. */
.label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
  line-height: 1.45;
}

/* Keyboard users land here before the 16-item rail; visible only on focus. */
.skip-link {
  position: fixed;
  left: 12px;
  /* a percentage here resolves against a zero-height containing block and
     leaves the link visible — offset by a concrete distance instead */
  top: -80px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: 8px;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent);
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { top: 12px; }

.fleuron { color: var(--gilt); }

/* focus */
:focus-visible {
  outline: 2px solid var(--gilt);
  outline-offset: 2px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Wisdom Vault — "The Private Athenaeum"
   05-skins: the SKIN axis — typography / density / ornament.

   Orthogonal to the theme (data-theme) axis in 00-tokens.css: skins never
   set color tokens (--paper/--ink/--accent/--gilt/etc.), only the
   typography/density/ornament custom properties declared at :root in
   00-tokens.css (--font-body, --font-display, --body-size, --body-leading,
   --heading-leading, --content-width, --row-pad, --rule-bg, --dropcap-*).
   Swapped via data-skin on :root, mirroring the data-theme pattern.

   [data-skin="athenaeum"] has no block here on purpose — its values ARE
   the :root defaults in 00-tokens.css (the current, pre-skin-axis look).
   That is the pixel-parity contract: adding this file must not move a
   single computed style for the default skin, because there is nothing
   here to override it with. modernist / manuscript / compact below are
   the only three skins that actually redefine anything.
   ========================================================================== */

[data-skin="modernist"] {
  /* Modernist — self-contained OS system-sans stack, no external fonts.
     No drop caps, tighter headings, a flat rule instead of the gilt
     fade. */
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --heading-leading: 1.15;
  --rule-bg: var(--line);
  --dropcap-size: 1em;
  --dropcap-lh: inherit;
  --dropcap-float: none;
  --dropcap-pad: 0;
  --dropcap-color: inherit;
  --dropcap-weight: inherit;
}

[data-skin="manuscript"] {
  /* Manuscript — the slow-reading skin: wider line-height, larger body
     size, a narrower content column (more surrounding whitespace reads
     as "generous margins"), drop caps kept on and slightly larger to
     match the bigger body type. Font stack unchanged (still --serif). */
  --body-size: 18.5px;
  --body-leading: 1.85;
  --content-width: 640px;
  --dropcap-size: 3.4em;
}

[data-skin="compact"] {
  /* Compact — the power-scanner skin: smaller body, tighter line-height,
     tighter headings, denser list rows, no drop caps. Leading sits at
     1.48, not the original 1.42: at 15px over the full 720px measure the
     tighter value read as a wall of text on desktop (fine at 375px);
     1.48 keeps the density while restoring the scan rhythm. */
  --body-size: 15px;
  --body-leading: 1.48;
  --heading-leading: 1.15;
  --row-pad: 8px 6px;
  --dropcap-size: 1em;
  --dropcap-lh: inherit;
  --dropcap-float: none;
  --dropcap-pad: 0;
  --dropcap-color: inherit;
  --dropcap-weight: inherit;
}

/* ==========================================================================
   Drift — 05-nocturne: the night wardrobe.

   One identity family, five members — "the same collection at night."
   The Athenaeum is a daylit reading room; Drift is one luminous card in
   a dark hall, and each scheme is a different hall: Nocturne (indigo
   night, candle gold — the original and default), Emberfall (coal black,
   fire glowing from below), Fathom (abyssal blue-green, bioluminescent
   teal), Violet Hour (dusk indigo-violet, first-star silver), Moonless
   (pure graphite, cold silver, no warmth at all).

   Implemented as token-value blocks over the SAME custom-property
   contract the shared sheets define (00-tokens.css), scoped to
   html[data-drift="<scheme>"]. Every structural rule — the aura, the
   vignette, the lit card, the hushed small-caps dealer voice, the ghost
   chrome, the night transitions — is written ONCE against the family
   selector html[data-drift]:not([data-drift="match"]) and colored purely
   by each scheme's tokens, so the five stay siblings by construction.
   When the reader flips "Match my Athenaeum theme" in the overlay,
   00-core.js sets data-drift="match" + the shared data-theme/data-skin
   attributes instead, and every rule here goes dormant.

   Quality bars kept: system font stacks only; body ink >= 7:1 on paper
   AND on the card gradient in EVERY scheme (verified >= 14:1 — table in
   PR); 375px-first; the global prefers-reduced-motion rule in
   00-tokens.css (!important) kills the night transitions too.
   ========================================================================== */

/* ---------------- scheme token blocks (palette only) ----------------
   Primitives per scheme: paper/ink scale, accent + gilt, ok, the aura
   and vignette washes (+ their positions — Emberfall glows from BELOW),
   the card gradient, and the claim ink. Everything derived (lines,
   soft washes, shadows) is computed from these in the family block. */

html[data-drift="native"] {
  /* Nocturne — indigo-black night, moon-ivory ink, candle-gold accents.
     Deliberately NOT a twin of any Athenaeum pack: darker paper than
     midnight/iris, warmer ink than slate, composition of its own.
     --ink-faint lightened 2026-07-21 (AIL r1 code-lane #5): was
     #767090, 3.92:1 against the lit card (--night-card-top #16131f,
     the worst-case surface — the active scheme name/keyboard legend
     render there). Same hue/sat, lighter only: #807b98, 4.54:1. */
  --paper: #0b0b12;
  --paper-2: #14121d;
  --paper-3: #1e1b2c;
  --ink: #e9e6db;
  --ink-soft: #a9a3bd;
  --ink-faint: #807b98;
  --accent: #e0b968;
  --gilt: #c2a566;
  --ok: #8fb996;
  --night-aura: #191531;
  --night-aura-at: 50% -12%;
  --night-vignette: rgba(0, 0, 0, 0.55);
  --night-vignette-at: 50% 115%;
  --night-card-top: #16131f;
  --night-card-bottom: #120f1a;
  --night-claim: #f1ede1;
}

html[data-drift="ember"] {
  /* Emberfall — coal black, warm amber aura rising from BELOW the card
     (the one scheme whose light comes from under), fire-gold accents.
     --ink-faint already clears 4.5:1 against --night-card-top (4.51:1,
     AIL r1 code-lane #5 audit 2026-07-21) — left unchanged. */
  --paper: #100c0a;
  --paper-2: #191210;
  --paper-3: #241a15;
  --ink: #f0e4d4;
  --ink-soft: #c0a88f;
  --ink-faint: #8d7b6a;
  --accent: #e8934a;
  --gilt: #c98a45;
  --ok: #a4b087;
  --night-aura: #2d1b10;
  --night-aura-at: 50% 112%;
  --night-vignette: rgba(0, 0, 0, 0.5);
  --night-vignette-at: 50% -15%;
  --night-card-top: #1b1310;
  --night-card-bottom: #150e0b;
  --night-claim: #f7ecdc;
}

html[data-drift="fathom"] {
  /* Fathom — abyssal blue-green, sea-glass ink, one bioluminescent
     teal accent drifting in the dark. --ink-faint lightened 2026-07-21
     (AIL r1 code-lane #5): was #628278, 4.13:1 against --night-card-top
     (the worst-case surface). Same hue/sat, lighter only: #67897e,
     4.53:1. */
  --paper: #06110f;
  --paper-2: #0c1d19;
  --paper-3: #12251f;
  --ink: #dcebe4;
  --ink-soft: #93b3a8;
  --ink-faint: #67897e;
  --accent: #4fd8c4;
  --gilt: #58b3a2;
  --ok: #7fc4a5;
  --night-aura: #0c2b26;
  --night-aura-at: 50% -12%;
  --night-vignette: rgba(0, 0, 0, 0.55);
  --night-vignette-at: 50% 115%;
  --night-card-top: #0c1d19;
  --night-card-bottom: #091613;
  --night-claim: #e8f4ed;
}

html[data-drift="violet"] {
  /* Violet Hour — dusk indigo-violet, the sky just after the sun,
     first-star silver as the only bright thing in it. --ink-faint
     lightened 2026-07-21 (AIL r1 code-lane #5): was #746c99, 3.74:1
     against --night-card-top (the worst-case surface — the widest
     fail of the five schemes). Same hue/sat, lighter only: #817aa2,
     4.51:1. */
  --paper: #0e0c1a;
  --paper-2: #171329;
  --paper-3: #201a38;
  --ink: #e6e4ee;
  --ink-soft: #a49dc2;
  --ink-faint: #817aa2;
  --accent: #c9d0ec;
  --gilt: #9d92c8;
  --ok: #8fb996;
  --night-aura: #221c44;
  --night-aura-at: 50% -12%;
  --night-vignette: rgba(0, 0, 0, 0.55);
  --night-vignette-at: 50% 115%;
  --night-card-top: #171329;
  --night-card-bottom: #110e20;
  --night-claim: #efedf6;
}

html[data-drift="moonless"] {
  /* Moonless — pure graphite and cold silver, no color warmth at all;
     the aura barely registers, the dark simply pools. --ink-faint
     lightened 2026-07-21 (AIL r1 code-lane #5): was #737b86, 4.23:1
     against --night-card-top (the worst-case surface). Same hue/sat,
     lighter only: #77808b, 4.53:1. */
  --paper: #0c0d0f;
  --paper-2: #141619;
  --paper-3: #1c1f23;
  --ink: #e2e5e9;
  --ink-soft: #a2a9b3;
  --ink-faint: #77808b;
  --accent: #b7bfca;
  --gilt: #8b939f;
  --ok: #9aa89e;
  --night-aura: #17191d;
  --night-aura-at: 50% -12%;
  --night-vignette: rgba(0, 0, 0, 0.6);
  --night-vignette-at: 50% 115%;
  --night-card-top: #141619;
  --night-card-bottom: #101214;
  --night-claim: #eceef1;
}

/* ---------------- the family (structure, shared by all five) ----------
   Note the specificity: this selector (0,2,1) outranks the scheme blocks
   (0,1,1), so derived tokens live ONLY here and primitives ONLY above. */

html[data-drift]:not([data-drift="match"]) {
  /* derived washes — computed from each scheme's own primitives */
  --line: color-mix(in srgb, var(--ink) 15%, transparent);
  --line-soft: color-mix(in srgb, var(--ink) 7%, transparent);
  --accent-soft: color-mix(in srgb, var(--accent) 13%, transparent);
  --gilt-soft: color-mix(in srgb, var(--gilt) 15%, transparent);
  --shadow: 0 2px 6px rgba(0, 0, 0, 0.5), 0 18px 60px rgba(0, 0, 0, 0.55);
  --shadow-lift: 0 4px 10px rgba(0, 0, 0, 0.6), 0 30px 90px rgba(0, 0, 0, 0.65);
  --grain-alpha: 0.03;

  /* night type — slow night reading: slightly larger serif body with
     looser leading, a narrower measure, drop cap in the scheme accent */
  --font-body: var(--serif);
  --font-display: var(--serif);
  --body-size: 17.5px;
  --body-leading: 1.72;
  --heading-leading: 1.18;
  --content-width: 700px;
  --dropcap-size: 3.2em;
  --dropcap-color: var(--accent);
  --dropcap-weight: 500;
}

/* deep atmospheric background — not flat: a faint aura on one side of
   the card, a vignette pooling on the other, the grain barely-there */
html[data-drift]:not([data-drift="match"]) body {
  background:
    radial-gradient(130% 90% at var(--night-aura-at),
      var(--night-aura) 0%, transparent 58%),
    radial-gradient(120% 70% at var(--night-vignette-at),
      var(--night-vignette) 0%, transparent 55%),
    var(--paper);
}

/* the card is the only lit object in the room */
html[data-drift]:not([data-drift="match"]) .flow-card {
  background: linear-gradient(180deg,
    var(--night-card-top) 0%, var(--night-card-bottom) 100%);
  border-color: color-mix(in srgb, var(--ink) 8%, transparent);
  border-radius: 16px;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 5%, transparent),
    0 24px 80px rgba(0, 0, 0, 0.55);
}

/* luminous restrained type: the claim at display scale */
html[data-drift]:not([data-drift="match"]) .flow-claim {
  font-size: clamp(24px, 5.4vw, 38px);
  font-weight: 500;
  color: var(--night-claim);
}

/* the dealer's voice: hushed small caps in the scheme's quiet metal */
html[data-drift]:not([data-drift="match"]) .deal-note {
  font-style: normal;
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  color: var(--gilt);
}

html[data-drift]:not([data-drift="match"]) .flow-src {
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: 0.14em;
}

/* chrome dissolves into the dark: ghost buttons, no filled discs */
html[data-drift]:not([data-drift="match"]) .rail-btn,
html[data-drift]:not([data-drift="match"]) .flow-menu,
html[data-drift]:not([data-drift="match"]) .scope-chip {
  background: transparent;
  border-color: color-mix(in srgb, var(--ink) 13%, transparent);
}
html[data-drift]:not([data-drift="match"]) .rail-btn:hover:not(:disabled),
html[data-drift]:not([data-drift="match"]) .flow-menu:hover,
html[data-drift]:not([data-drift="match"]) .scope-chip:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* transitions with presence — the next card rises out of the dark;
   killed wholesale by the reduced-motion rule in 00-tokens.css */
@keyframes nocturneIn {
  from { opacity: 0; transform: translateY(2.4%) scale(0.985); }
  to { opacity: 1; transform: none; }
}
@keyframes nocturneBack {
  from { opacity: 0; transform: translateY(-2.4%) scale(0.985); }
  to { opacity: 1; transform: none; }
}
html[data-drift]:not([data-drift="match"]) .flow-card.deal-in {
  animation: nocturneIn 0.5s cubic-bezier(0.22, 0.9, 0.26, 1);
}
html[data-drift]:not([data-drift="match"]) .flow-card.deal-back {
  animation: nocturneBack 0.5s cubic-bezier(0.22, 0.9, 0.26, 1);
}

/* ==========================================================================
   Drift — 10-flow: the dealer shell.

   flow.css is built as 00-tokens.css + 05-skins.css + this file (see
   write_flow_shell() in build_webapp.py), so every color comes from the
   shared theme packs (ink/paper >= 7:1 by that contract), typography and
   ornament follow the shared skins (--font-display / --dropcap-* /
   --content-width), and the global prefers-reduced-motion kill switch in
   00-tokens.css already covers every animation declared here.
   ========================================================================== */

html, body {
  height: 100%;
  overflow: hidden; /* the page never scrolls — the card body does */
}

#stage {
  position: relative;
  z-index: 1; /* above the paper grain */
  height: 100%;
}

/* ---------------- boot / error ---------------- */

.boot-state {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--ink-soft);
  text-align: center;
  padding: 24px;
}

.boot-fleuron { font-size: 28px; color: var(--gilt); }

/* ---------------- stage layout ---------------- */

.flow-stage {
  height: 100dvh;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  max-width: calc(var(--content-width) + 48px);
  margin: 0 auto;
  /* +10px of breathing room below the rail on top of the safe-area
     inset itself (was a bare 10px with no margin beyond the notch) */
  padding: 10px 12px calc(20px + env(safe-area-inset-bottom, 0px));
}

/* ---------------- top line: dealer note + menu ---------------- */

.flow-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 6px 8px;
  min-height: 34px;
}

.deal-note {
  font-family: var(--sans);
  font-size: 12px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.flow-menu {
  position: relative; /* anchors the coarse-pointer hit-slop below */
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  color: var(--gilt);
  font-size: 16px;
  line-height: 1;
  border: 1px solid var(--line); /* one step up from --line-soft */
}
.flow-menu:hover { background: var(--gilt-soft); }

/* the deal-scope chip: one quiet word on the card chrome; tapping it
   opens the overlay's full dial (20-view.js data-act="scope") */
.scope-chip {
  position: relative; /* anchors the hit-slop below */
  flex: none;
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft); /* one step up from --ink-faint */
  background: transparent;
  border: 1px solid var(--line); /* one step up from --line-soft */
  border-radius: 12px;
  padding: 4px 11px;
  line-height: 1;
}
.scope-chip:hover { color: var(--gilt); border-color: var(--gilt); }
/* AIL r2 (item 6): the drawn chip is ~58x20 — well under the 44x44
   touch-target floor on height. An invisible ::after centered on the
   chip expands the CLICKABLE area to 44x44 (unconditional, not gated
   behind pointer:coarse — see the .rail-btn/.flow-menu note below for
   why). Since the chip's own drawn width already exceeds 44px, only
   the height actually grows; horizontally the real button box remains
   the authority, so neighbouring controls are unaffected. */
.scope-chip::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

/* ---------------- the card ---------------- */

.flow-card {
  position: relative; /* anchors .card-fade to the card's own bottom edge */
  flex: 1;
  min-height: 0; /* lets the body scroll instead of growing the card */
  display: flex;
  flex-direction: column;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
}

.flow-card-head {
  padding: 22px 24px 14px;
  border-bottom: 1px solid var(--line-soft);
}

.flow-claim {
  font-family: var(--font-display);
  font-size: clamp(21px, 4.6vw, 30px);
  line-height: var(--heading-leading);
  text-wrap: balance;
}

.flow-src {
  margin-top: 10px;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink-soft); /* one deliberate step up from --ink-faint */
}

.flow-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain; /* no rubber-band chaining to the page */
  padding: 20px 24px 28px;
}

/* ---------------- overflow contract ----------------
   a card whose prose runs past the visible body gets a bottom fade + a
   "scroll to continue" cue — cards that fit show neither (20-view.js
   toggles .has-overflow only when the body is actually scrollable, the
   same bodyScrollable() check the read-mark contract uses). The cue's
   job ends at the reader's first deliberate scroll: the same listener
   that flips Deck.sawEnd for the read-mark contract also drops this
   class, so an ambiguous cutoff reads as a deliberate reading mechanic
   instead of an accident. */
.card-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 56px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 8px;
  background: linear-gradient(to bottom, transparent, var(--paper-2) 75%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.flow-card.has-overflow .card-fade { opacity: 1; }

.card-fade-cue {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* atom-body content styles — same voice as the Athenaeum reader
   (static/css/30-views.css), tokens shared so skins behave identically */
.atom-body { font-size: var(--body-size); }
.atom-body p { margin: 0 0 1.25em; }
.atom-body p:last-child { margin-bottom: 0; }

.atom-body > p:first-of-type::first-letter {
  font-size: var(--dropcap-size);
  line-height: var(--dropcap-lh);
  float: var(--dropcap-float);
  padding: var(--dropcap-pad);
  color: var(--dropcap-color);
  font-weight: var(--dropcap-weight);
}

.atom-body h2, .atom-body h3, .atom-body h4 {
  margin: 1.7em 0 0.7em;
  font-size: 1.1em;
}
.atom-body h2::before {
  content: "\2766\00a0\00a0";
  color: var(--gilt);
  font-size: 0.8em;
}
.atom-body h2.sec-limits { color: var(--accent); }
.atom-body ul, .atom-body ol { margin: 0 0 1.25em 1.4em; }
.atom-body li { margin-bottom: 0.45em; }
.atom-body blockquote {
  margin: 0 0 1.25em;
  padding: 4px 0 4px 20px;
  border-left: 2px solid var(--gilt);
  color: var(--ink-soft);
  font-style: italic;
}
.atom-body code {
  font-family: var(--mono);
  font-size: 0.86em;
  background: var(--paper-3);
  padding: 1px 5px;
  border-radius: 4px;
}
.atom-body hr { border: none; text-align: center; margin: 2em 0; }
.atom-body hr::after { content: "\2766"; color: var(--gilt); font-size: 14px; }

.wl { border-bottom: 1px dotted var(--gilt); }
.wl:hover {
  text-decoration: none;
  border-bottom-style: solid;
  background: var(--gilt-soft);
}
.wl-dead { color: var(--ink-soft); border-bottom: 1px dashed var(--line); }
.wl-img { font-family: var(--sans); font-size: 12px; color: var(--ink-faint); }

/* ---------------- the rail ---------------- */

.flow-rail {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 10px 4px 2px;
}

.rail-btn {
  position: relative; /* anchors the coarse-pointer hit-slop below */
  width: 42px;
  height: 42px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--paper-2);
  color: var(--ink-soft);
  font-size: 17px;
  line-height: 1;
  transition: background 0.15s ease, color 0.15s ease;
}
.rail-btn:hover:not(:disabled) { background: var(--gilt-soft); color: var(--ink); }
.rail-btn:disabled { opacity: 0.35; cursor: default; }
.rail-btn.star.on, .rail-btn.star:hover:not(:disabled) { color: var(--gilt); }

/* hit-slop (AIL r1 code-lane #7, unconditional as of r2 item 6):
   .flow-menu (34x34) and .rail-btn (42x42) are both under the 44x44
   touch-target floor. An invisible ::after centered on each expands the
   CLICKABLE area to 44x44 without growing the drawn circle.
   AIL r2: this was previously gated behind `@media (pointer: coarse)`,
   which left the TRUE (query-independent) target at 34/42px — exactly
   the residue r2's sol/fable review flagged ("rings are currently
   ~42px"). Unconditional now, so the region is a real 44x44 regardless
   of how pointer type is detected; geometry re-checked (10px .flow-rail
   gap, 12px .flow-top gap) and no adjacent slop overlaps. */
.flow-menu::after,
.rail-btn::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.flow-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 5px;
  min-width: 60px;
  max-width: 40vw;
  padding: 0 6px;
}

.flow-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  /* brighter than --line alone (a very faint per-theme rgba): tie the
     unlit pip to --ink-faint's solid color instead, at reduced opacity,
     so it reads as "unlit" without approaching the background tone */
  background: color-mix(in srgb, var(--ink-faint) 65%, transparent);
}
.flow-dot.past { background: var(--gilt); }
.flow-dot.current {
  background: var(--accent);
  transform: scale(1.35);
}

/* ---------------- receding chrome ----------------
   #stage.quiet is armed by 20-view.js after idle: the top line and rail
   sink to near-nothing so only the card remains; any hover/focus (or any
   input, which unsets the class) brings them back. */

.flow-top, .flow-rail { transition: opacity 0.6s ease; }
#stage.quiet .flow-top, #stage.quiet .flow-rail { opacity: 0.12; }
#stage.quiet .flow-top:hover, #stage.quiet .flow-rail:hover,
#stage.quiet .flow-top:focus-within, #stage.quiet .flow-rail:focus-within {
  opacity: 1;
}

/* ---------------- deal transitions (reduced-motion safe via tokens) ----
   each dealt card LANDS — translateY + fade with a small spring overshoot
   in the easing, so it reads as a card dropped into place (the dealer
   metaphor) rather than a plain cross-fade. Forward deals settle from
   just below, retreat settles from just above, keeping the same
   direction sense the old translateX version carried. The shared
   00-tokens.css prefers-reduced-motion rule (!important) collapses both
   to ~0.01ms — an instant swap — without any extra guard here. */

@keyframes dealIn {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes dealBack {
  from { opacity: 0; transform: translateY(-14px); }
  to { opacity: 1; transform: translateY(0); }
}
.flow-card.deal-in { animation: dealIn 0.18s cubic-bezier(0.34, 1.56, 0.64, 1); }
.flow-card.deal-back { animation: dealBack 0.18s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* ---------------- star/skip micro-feedback ----------------
   a ~200ms pulse under the reader's thumb (20-view.js pulseRail) — the
   toast (00-core.js toast()) carries the words, this is the beat. Same
   reduced-motion coverage as the deal transitions above: the shared
   kill switch collapses the duration, animationend still fires so the
   class still clears. */
@keyframes railPulse {
  0% { transform: scale(1); }
  45% { transform: scale(1.22); }
  100% { transform: scale(1); }
}
.rail-btn.pulse { animation: railPulse 0.2s ease; }

/* ---------------- shared button voice ---------------- */

.flow-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 18px;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  text-decoration: none;
}
.flow-btn:hover { background: var(--gilt-soft); text-decoration: none; }
.flow-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}
.flow-btn.primary:hover { opacity: 0.92; }
.flow-btn.on { border-color: var(--gilt); background: var(--gilt-soft); }

.flow-deal-error {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 8px 4px 0;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 13px;
}

.flow-deal-error .flow-btn { min-height: 44px; }

/* ---------------- summary card ---------------- */

.summary-body { display: flex; flex-direction: column; gap: 28px; }

.summary-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin-top: 6px;
}

.summary-stat {
  padding: 16px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: var(--paper);
  text-align: center;
}

.summary-num {
  font-family: var(--font-display);
  font-size: 30px;
  margin-bottom: 4px;
}

.summary-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

/* ---------------- overlay ---------------- */

.flow-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* +10px below the panel on top of the safe-area inset, so the menu's
     final action (End session / the key legend) never sits flush with
     a device's home-indicator/notch */
  padding: 8vh 16px calc(26px + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--paper) 55%, transparent);
  backdrop-filter: blur(2px);
}

.overlay-panel {
  width: min(440px, 100%);
  max-height: 84vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow-lift);
  padding: 18px 20px 22px;
}

.overlay-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

#flow-find {
  width: 100%;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
}

.overlay-results { display: flex; flex-direction: column; gap: 2px; }

.overlay-result {
  text-align: left;
  padding: 9px 10px;
  border-radius: 8px;
  font-size: 15px;
  color: var(--ink);
}
.overlay-result:hover { background: var(--gilt-soft); }

.overlay-empty { color: var(--ink-faint); font-size: 14px; }

.overlay-len { display: flex; gap: 10px; }

.overlay-scope { display: flex; flex-wrap: wrap; gap: 8px; }
.overlay-scope .flow-btn { font-size: 13px; padding: 8px 14px; }

/* the night wardrobe: five little skies, one light in each — the paper
   as the disc, the accent as a single star (quiet, no dropdown) */
.overlay-schemes { display: flex; gap: 14px; padding: 2px 0; }

.scheme-swatch {
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  background:
    radial-gradient(circle at 33% 30%,
      var(--sw-accent) 0 3.5px, transparent 4.5px),
    var(--sw-paper);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.scheme-swatch:hover { transform: scale(1.12); }
.scheme-swatch.on {
  border-color: var(--sw-accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--sw-accent) 45%, transparent);
}
.overlay-schemes.matched .scheme-swatch { opacity: 0.4; }

.scheme-name {
  margin-top: -6px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.overlay-end { align-self: flex-start; color: var(--accent); }

/* the keyboard vocabulary (arrows deal, c stars, x skips, Esc closes
   this menu) has no other in-UI surface — the rail buttons carry it via
   aria-label only. One quiet line, not a callout. */
.overlay-keys {
  margin-top: -4px;
  font-family: var(--sans);
  font-size: 11.5px;
  color: var(--ink-faint);
}

/* ---------------- focus-visible ----------------
   the shared static/00-tokens.css :focus-visible rule (concatenated into
   flow.css by write_flow_shell()) sets border-radius:2px on whatever is
   focused — correct for square controls, but it flattens the rail's
   round buttons, the scope chip's pill, and the night-scheme swatches.
   Give Drift's own interactive shapes a ring that respects their actual
   geometry instead of overriding it. */

.rail-btn:focus-visible,
.flow-menu:focus-visible,
.scheme-swatch:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 50%;
}

.scope-chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 12px;
}

.flow-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 22px;
}

.overlay-result:focus-visible,
#flow-find:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 8px;
}

/* ---------------- toast (same voice as the Athenaeum) ---------------- */

.toast {
  position: fixed;
  bottom: 74px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 13px;
  padding: 10px 22px;
  border-radius: 22px;
  box-shadow: var(--shadow-lift);
  opacity: 0;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 60;
  pointer-events: none;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------- first-run coach ---------------- */
.flow-coach {
  flex: none;
  display: flex;
  justify-content: center;
  padding: 0 4px 8px;
  opacity: 0;
  animation: coachIn 0.3s ease 0.2s forwards;
}

@keyframes coachIn { to { opacity: 1; } }

.flow-coach-panel {
  width: min(560px, 100%);
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow-lift);
  padding: 10px 12px 10px 16px;
}

.flow-coach-copy { min-width: 0; }

.flow-coach-title {
  font-family: var(--font-display);
  font-size: 16px;
  margin-bottom: 3px;
}

.flow-coach-keys {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink-soft);
}

.flow-coach-mobile { display: none; }
.flow-coach-start { min-height: 44px; flex: none; }

/* ---------------- breakpoints ---------------- */

/* small phones: the card takes the whole stage, tighter frame */
@media (max-width: 480px) {
  /* +10px safe-area buffer below the rail, same delta as the base rule */
  .flow-stage { padding: 6px 8px calc(18px + env(safe-area-inset-bottom, 0px)); }
  .flow-card-head { padding: 18px 18px 12px; }
  .flow-body { padding: 16px 18px 24px; }
  /* AIL r1 code-lane #7: rail-btn no longer shrinks to 40x40 here — that
     was the flagged ergonomic regression ("shrinks primary controls
     below its own baseline"). The rail recovers the narrower viewport's
     space via the tighter 6px gap below instead of shrinking the
     42x42 controls themselves. */
  .flow-rail { gap: 6px; }
  .flow-coach-panel { padding-left: 14px; }
  .flow-coach-desktop { display: none; }
  .flow-coach-mobile { display: block; }
}

/* desktop: a comfortable measure, breathing room around the card */
@media (min-width: 900px) {
  .flow-stage { padding: 24px 24px 20px; }
  .flow-card-head { padding: 30px 40px 18px; }
  .flow-body { padding: 26px 40px 36px; }
}

/* ==========================================================================
   Drift — 15-material: the Material row (Paper / Glass / Alloy).

   Spec: docs/superpowers/ail/2026-07-12-webapp/SPEC-materials-docket.md
   §A. Materials are skin PACKS layered over the ACTIVE theme (any
   data-theme, any of the five data-drift night schemes, or the "match
   Athenaeum" state) via a shared --mat-* token contract computed from
   each pack's OWN custom properties (--paper/--paper-2/--paper-3/--ink/
   --accent/--gilt) — never a literal theme-specific hex. Paper (the
   default; no [data-mat="paper"] block exists below) stays byte-for-byte
   untouched, same pixel-parity contract as [data-skin="athenaeum"] in
   static/css/05-skins.css: the absence of a rule IS the default look.

   Concat order (write_flow_shell() in build_webapp.py): 00-tokens.css,
   05-skins.css, then this directory's *.css sorted by filename —
   05-nocturne.css, 10-flow.css, then this file — so these rules layer
   over both the base flow chrome and the night-scheme tokens.

   Specificity note: 05-nocturne.css re-styles .flow-card / .rail-btn /
   .flow-menu / .scope-chip directly at html[data-drift]:not([data-drift=
   "match"]) <selector> — specificity (0,3,1) for the class selectors,
   (0,2,2) for its `body` rule. A plain html[data-mat="x"] <selector> is
   only (0,2,1)/(0,1,2) and would silently lose to a night scheme. Every
   rule below that would otherwise lose is duplicated with a [data-drift]-
   qualified selector at equal-or-greater specificity; source order (this
   file loads after 05-nocturne.css) then wins the tie, so material
   composes with all five night schemes instead of being overridden by
   them. .overlay-panel and .flow-coach-panel aren't touched by
   05-nocturne.css at all, so the plain selector already wins there — the
   duplication is harmless redundancy for those two, kept for one shared
   rule instead of splitting the selector list in two.
   ========================================================================== */

/* ---------------- primitives: --mat-* per pack -------------------------
   Panel surface, edge/bevel, elevation, and the fixed backdrop wash —
   every value is a color-mix()/gradient built from the ACTIVE theme's
   own tokens, so all eighteen theme packs x five night schemes x the
   match-Athenaeum state each produce a coherent glass or alloy look
   with zero hardcoded theme hexes. white/black below are universal
   specular-highlight primitives (a bevel or glass-edge reflection isn't
   tinted by the surface it sits on), not theme-specific hexes — the
   rule this file's header note is protecting against. */

html[data-mat="glass"] {
  /* panel bg: theme paper-2 at 0.66 alpha (spec's 0.55-0.7 band, kept
     toward the top of that band for the contrast promise below) */
  --mat-surface: color-mix(in srgb, var(--paper-2) 66%, transparent);
  --mat-blur: blur(18px) saturate(1.4);
  /* a denser wash, NOT part of the spec's 4 named tokens — an extra
     scrim stacked behind body text only (see the .flow-card-head /
     .flow-body rule below), satisfying hard rule 1's contrast clause
     regardless of what the blur lets show through at the card's edges */
  --mat-scrim: color-mix(in srgb, var(--paper-2) 88%, transparent);
  /* 1px inner light border, brighter along the top edge */
  --mat-edge: inset 0 1px 0 color-mix(in srgb, white 55%, transparent),
    inset 0 0 0 1px color-mix(in srgb, white 14%, transparent);
  /* airy, large, faint — light seems to pass through rather than sit on it */
  --mat-elev: 0 2px 8px color-mix(in srgb, var(--ink) 8%, transparent),
    0 26px 70px color-mix(in srgb, var(--ink) 14%, transparent);
  /* something to refract: a faint two-color mesh in the theme's own
     accent/gilt, fixed behind the whole app (applied via body::after
     below, never via `background` on body itself) */
  --mat-backdrop: radial-gradient(60% 45% at 18% 12%,
      color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 70%),
    radial-gradient(50% 40% at 86% 82%,
      color-mix(in srgb, var(--gilt) 14%, transparent) 0%, transparent 72%);
}

html[data-mat="alloy"] {
  /* subtle vertical metal gradient built from the panel hue itself —
     lighter top-left highlight, darker bottom-right ("light: #f2f2f4 ->
     #d8d8dc feel, dark equivalents" per spec), computed by mixing the
     theme's OWN paper-2/paper-3 toward white/black rather than a literal
     hex, so it re-derives correctly per theme automatically */
  --mat-surface: linear-gradient(165deg,
    color-mix(in srgb, var(--paper-2) 100%, white 15%) 0%,
    var(--paper-2) 42%,
    color-mix(in srgb, var(--paper-3) 100%, black 10%) 100%);
  --mat-blur: none; /* opaque — no refraction, no scrim needed */
  --mat-scrim: transparent;
  /* bevel pair: light top/left, dark bottom/right */
  --mat-edge: inset 1px 1px 0 color-mix(in srgb, white 45%, transparent),
    inset -1px -1px 0 color-mix(in srgb, black 40%, transparent);
  /* tighter, harder than glass — a machined edge, not an airy one */
  --mat-elev: 0 1px 2px color-mix(in srgb, black 30%, transparent),
    0 10px 26px color-mix(in srgb, black 24%, transparent);
  /* faint brushed texture, fixed behind the whole app */
  --mat-backdrop: repeating-linear-gradient(115deg,
    color-mix(in srgb, var(--ink) 5%, transparent) 0px,
    color-mix(in srgb, var(--ink) 5%, transparent) 1px,
    transparent 1px, transparent 5px);
  /* static sheen primitive, reused by the ::before rule below — a single
     diagonal highlight, not a --mat-* contract token, just this pack's
     own implementation of the spec's "sheen sweep" requirement */
  --mat-sheen: linear-gradient(115deg,
    transparent 32%,
    color-mix(in srgb, white 22%, transparent) 48%,
    transparent 64%);
}

/* ---------------- fixed backdrop -----------------------------------------
   An independent ::after layer behind the whole app — never touches
   `background` on body itself, so it never has to fight 05-nocturne.css's
   own body-background rule for specificity, and it composes with every
   theme / night scheme / match state automatically. Sits above the paper
   grain (body::before, 00-tokens.css) and below #stage (z-index:1,
   10-flow.css) by DOM/generated-content order — no explicit z-index
   needed for either relationship to hold. */
html[data-mat="glass"] body::after,
html[data-mat="alloy"] body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image: var(--mat-backdrop);
}

/* ---------------- the family: surface + edge + elevation ----------------
   Mirrors 05-nocturne.css's own "primitives per pack, structure written
   once against the family selector" architecture. One rule for both
   packs and all six material-bearing surfaces — the card, the three rail
   chrome pieces 05-nocturne.css itself re-styles, and the two overlay
   panels it never touches. */
html[data-mat]:not([data-mat="paper"])
    :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel),
html[data-drift][data-mat]:not([data-mat="paper"])
    :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel) {
  background: var(--mat-surface);
  box-shadow: var(--mat-edge), var(--mat-elev);
}

/* glass only: the actual refraction. backdrop-filter clips to each
   element's own border-radius inherently — no extra overflow:hidden
   needed for this half of the contract. -webkit- prefix for Safari/iOS,
   where Drift is most likely to run as an installed PWA. */
html[data-mat="glass"]
    :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel),
html[data-drift][data-mat="glass"]
    :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel) {
  backdrop-filter: var(--mat-blur);
  -webkit-backdrop-filter: var(--mat-blur);
}

/* glass only: the contrast scrim (hard rule 1) — a denser wash stacked
   over --mat-surface specifically behind the card's two text-bearing
   regions, so the claim + reading body always sit at the theme's own
   promised contrast no matter what the blur lets through at the card's
   edges. alloy's --mat-scrim is `transparent`, so this rule is a
   structural no-op for alloy — left unscoped-by-material on purpose
   rather than duplicated. */
html[data-mat] .flow-card-head,
html[data-drift][data-mat] .flow-card-head,
html[data-mat] .flow-body,
html[data-drift][data-mat] .flow-body {
  background: var(--mat-scrim);
}

/* alloy only: position/overflow for the sheen clip below. .flow-card
   already carries position:relative + overflow:hidden (10-flow.css, for
   .card-fade) — redeclaring here is harmless. The rail chrome pieces
   don't have either by default, so they need it for the sheen to clip to
   their own circle/pill shape instead of spilling past it. Neither
   property clips this element's own :focus-visible outline (outline
   paints outside the border box regardless of the element's own overflow
   value) or the .card-fade overflow cue (a later DOM sibling inside
   .flow-card, so it always paints above this ::before) — both stay
   visible on both materials as required. */
html[data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip),
html[data-drift][data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip) {
  position: relative;
  overflow: hidden;
}
html[data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip)::before,
html[data-drift][data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip)::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--mat-sheen);
}

/* ---------------- hard rule 1: reduced transparency + no-backdrop-filter
   fallback -> solid panel. Drift has no literal --panel token (its
   "panel" surfaces just use --paper-2 directly, see .flow-card /
   .overlay-panel / .flow-coach-panel in 10-flow.css) — solid var(--paper-2)
   IS the equivalent fallback here. Alloy never uses backdrop-filter (it's
   already fully opaque), so only glass needs this. */
@media (prefers-reduced-transparency: reduce) {
  html[data-mat="glass"]
      :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel),
  html[data-drift][data-mat="glass"]
      :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel) {
    background: var(--paper-2);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  html[data-mat="glass"] .flow-card-head,
  html[data-drift][data-mat="glass"] .flow-card-head,
  html[data-mat="glass"] .flow-body,
  html[data-drift][data-mat="glass"] .flow-body {
    background: transparent; /* the card itself is already solid now */
  }
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html[data-mat="glass"]
      :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel),
  html[data-drift][data-mat="glass"]
      :is(.flow-card, .rail-btn, .flow-menu, .scope-chip, .overlay-panel, .flow-coach-panel) {
    background: var(--paper-2);
  }
  html[data-mat="glass"] .flow-card-head,
  html[data-drift][data-mat="glass"] .flow-card-head,
  html[data-mat="glass"] .flow-body,
  html[data-drift][data-mat="glass"] .flow-body {
    background: transparent;
  }
}

/* ---------------- hard rule 2: no sheen/transition additions under
   reduced motion. The sheen above is already fully static (no
   @keyframes, no transition property declared anywhere in this file),
   so this block is a defensive no-op that makes the compliance
   grep-able/explicit rather than implicit. */
@media (prefers-reduced-motion: reduce) {
  html[data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip)::before,
  html[data-drift][data-mat="alloy"] :is(.flow-card, .rail-btn, .flow-menu, .scope-chip)::before {
    transition: none;
    animation: none;
  }
}

/* ---------------- the Material row's own chips ---------------------------
   Reuses .flow-btn wholesale (20-view.js clones the overlay-scope/
   overlay-len chip pattern exactly: .flow-btn + .on + aria-pressed) —
   .overlay-material only needs the same flex-wrap row .overlay-scope
   already has, nothing else. */
.overlay-material { display: flex; flex-wrap: wrap; gap: 8px; }
.overlay-material .flow-btn { font-size: 13px; padding: 8px 14px; }
