/* ============================================================================
   THEMES
   One definition drives the whole interface — every page, both overlays, focus
   mode, the waveform, the seek strip and the progress charts. Nothing outside
   these blocks may name a color.
   Three rules hold the system together:
     1. WARM IS THE BRAND. It is the default, and it is what the installed app
        icons, the therapist report and the client guide are built from. Those
        three never follow the client's choice — a report has to look the same
        on the therapist's machine as it did on the client's.
     2. NAMES ARE BY JOB, NOT BY COLOR. --accent is gold in Warm and green in
        Meadow; a variable called --gold that is green is a trap for whoever
        reads this next.
     3. --accent-deep IS THE EMPHASIS TEXT COLOR, so it is DARKER than
        --accent on a light theme and BRIGHTER on a dark one. Reversing that on
        the dark themes is the whole reason they stay readable.
   Sources: Warm = design-2.html, Meadow = design-alt-3.html, Dusk =
   design-3.html, Night Studio = design-alt-2.html (project root).
   ========================================================================= */

:root, html[data-theme="warm"] {
  /* Rebuilt from design-2.html by opening it side by side with the app. The
     things that were wrong the first time, all of them from reading hex values
     instead of looking: headings and card titles are the mockup's INK, not
     gold — gold is an accent there, never the text color; the interactive
     accent is TERRACOTTA, not gold; cards carry a 2px ochre top edge on a
     4px corner; the tagline and the intro line are italic serif; and the lion
     is the small ring-seal, not the big bright mark. */
  /* surfaces */
  --bg: #F7F0E1;            /* paper  */
  --panel: #FDF8EC;         /* paper2 */
  --panel2: #F1E7D0;        /* band   */
  --hover-fill: #E6D8B6;
  --video-bg: #33291A;
  --video-ink: #FBF3E7;
  --video-ink-soft: #CDBB98;
  --video-edge: #8A7A5C;
  /* lines */
  --line: #E3D7BA;          /* hair */
  --line-strong: #C9B78C;   /* rule */
  /* text */
  --ink: #33291A;
  --ink-soft: #6E6046;      /* soft */
  --ink-faint: #7C6E53;
  --heading: #33291A;       /* headings are ink in this theme, not gold */
  --nav-ink: #6E6046;       /* idle nav and steps */
  --btn-ink: #33291A;       /* control labels */
  --tagline: #B04E28;
  --tagline-style: italic;
  --sub-style: italic;
  /* accent — terracotta leads, ochre and gold are ornament */
  --accent: #B04E28;        /* terra */
  --accent-deep: #9B4321;   /* interactive emphasis: active nav, links */
  --accent-soft: #6E6046;
  /* --accent-fill is the FILL BEHIND --on-accent TEXT (primary button, active
     nav pill, active step, active shelf tab, pressed popover option, pressed
     emotion chip, Finish). It is a separate name from --accent because a fill
     that carries a label has to satisfy 4.5:1 with --on-accent, while --accent
     itself also paints the seek handle and the progress bar, where no text sits
     on it and the theme's own hue matters more. In every theme but Lantern the
     two are the same color. */
  --accent-fill: #B04E28;
  --accent-fill-hover: #8F3E1F;
  --accent-pale: #F4E0D4;   /* terra-tint */
  --on-accent: #FBF3E7;
  /* functional */
  --danger: #9B4321;
  --danger-border: #D9B49B;
  --danger-bg: #F4E0D4;
  /* markings keep the mockup's gold */
  --mark: #F0E1B8;          /* gold-tint */
  --mark-selected: #E0C46E;
  --mark-edge: #A67916;
  --mark-edge-selected: #7C560B;
  --wave: #6E5B2A;
  --duck: #2B2210;          /* the playhead is ink in the mockup */
  /* seek strip */
  --seek-track: #F1E7D0;
  --seek-fill: #96690F;   /* was #A67916: 3.18:1 as painted, the tightest of the five */
  --seek-handle: #B04E28;
  /* progress charts — a sequential ramp, least emphasis to most. Every step is
     at least 2:1 off its neighbour and every color at least 3:1 off --panel,
     because three bars of one gold were indistinguishable at bar width. */
  --chart-expected: #B8862B;
  --chart-peak: #8A4F1E;
  --chart-end: #4A2410;
  --chart-blend: #8A7238;
  --chart-axis: #C9B78C;
  --chart-grid: #E3D7BA;
  --chart-text: #6E6046;
  /* card chrome */
  --card-top: #96690F;      /* ochre */
  --card-top-w: 2px;
  --card-title-size: 20px;
  --card-text-size: 13px;
  --orn-display: block;
  /* display treatment carried BEYOND home — see the note on --h2-size below */
  --h2-size: 20px;
  --panel-top: #96690F;
  --panel-top-w: 2px;
  /* THE LION. Geometry is frozen — see the note above the <symbol> in
     index.html. A theme may vary the ink, the accent and the STROKE WEIGHT,
     and nothing else.
     Why weight is per-theme at all: identical paths at identical weight do NOT
     render alike on different grounds. A stroke's antialiased skirt reads as
     ink once it is a fixed perceptual step off the ground, so the higher the
     mark's contrast, the wider that skirt reads and the heavier the same
     stroke looks. Warm 4.28:1 and Lantern 3.15:1 render the muzzle correctly
     at 4.5. Meadow (7.4:1), Dusk (8.2:1) and Night (8.6:1) all bloom the nose
     and the mouth into one bar at that weight and take 3.6 instead.
     Measured, at 1x, from the rendered bitmap — tools/contrast-checks/
     muzzle-render.mjs. Warm is the reference every other theme is held to. */
  --logo-ink: #96690F;
  --logo-accent: #B04E28;
  --logo-stroke: 4.5;
  /* how thick the SELECTED marking's outline is, in device pixels */
  --mark-edge-w-sel: 1;
  /* depth */
  --scrim: rgba(51, 41, 26, 0.5);
  --scrim-dim: rgba(51, 41, 26, 0.3);
  --shadow: rgba(51, 41, 26, 0.18);
  --shadow-strong: rgba(51, 41, 26, 0.3);
  --on-accent-veil: rgba(251, 243, 231, 0.12);
  /* type + shape */
  --font-display: Charter, "Bitstream Charter", "Iowan Old Style", "Palatino Linotype", Cambria, Georgia, serif;
  --font-body: "Segoe UI", system-ui, -apple-system, sans-serif;
  --radius: 6px;
  --radius-card: 4px;
}

/* PRINT FORCES WARM, AND IT IS DONE BY SCOPE RATHER THAN BY RESTATEMENT.
   The four non-Warm themes below are each wrapped in @media screen, so on paper
   none of them applies and the :root block above — which IS Warm — governs on
   its own. Nothing is restated, so nothing can drift.
   This replaces a dead override in help.html, which failed twice over: it
   restated Warm inside `@media print { :root { ... } }`, and `:root` is
   specificity (0,1,0) against `html[data-theme="night"]` at (0,1,1) — a media
   query contributes nothing to specificity, so the block never won and the
   client guide printed pale ink on white paper (1.25:1 in Night, 1.21:1 in
   Dusk: functionally blank). Its `body { background: #FFFDF7 }` DID win, being
   element-vs-element, which is why the page looked like Warm paper with the
   wrong ink on it. The values it restated had also gone stale against Warm.
   NOTE: this makes the whole app print as Warm, not just help.html. That is
   the intended rule — the printed guide and the therapist's report are Warm
   documents whatever the client chose on screen. */

/* LANTERN — the studio's original cream-and-gold, kept as an option at
   Amitai's request. This is the app as it looked before the theme system, with
   the serif display face: gold headings, gold controls, rounder cards. It is
   NOT Warm and is not the default. */
@media screen {
  html[data-theme="lantern"] {
    /* THE GROUND IS DELIBERATELY DEEPER THAN WARM'S (2026-08-07). It used to be
       #F7F0DF against Warm's #F7F0E1 — a two-point difference nobody could see,
       so switching between the two themes looked like nothing had happened
       anywhere except home. Lantern now sits on a real straw-gold ground while
       Warm keeps its near-white paper. Everything below that moved, moved
       BECAUSE of this: text that was measured against the old paler ground had
       to come down with it. */
    --bg: #F2E6C6;
    --panel: #FCF4DE;
    --panel2: #E9DBB4;
    --hover-fill: #DCC793;
    --video-bg: #4A3F28;
    --video-ink: #FFF8E8;
    --video-ink-soft: #D7C7A0;
    --video-edge: #A99777;
    --line: #E0D0A6;
    --line-strong: #C4A970;
    --ink: #4A3F28;
    --ink-soft: #6B5C3B;
    --ink-faint: #7A6A46;
    --heading: #7E5B12;       /* was #8A6516: 4.28:1 on the deeper ground */
    --nav-ink: #7E5B12;
    --btn-ink: #7E5B12;
    --tagline: #74602C;       /* was #8A7238 — 4.07:1 on --bg, a hair under AA */
    --tagline-style: normal;
    --sub-style: normal;
    --accent: #C89B2E;
    --accent-deep: #7E5B12;
    --accent-soft: #74602C;   /* was #8A7238 — 4.39:1 as the popover label */
    --accent-pale: #EBCE8F;
    --on-accent: #FFF8E8;
    /* Lantern is the ONE theme where the label-bearing fill is not --accent. Its
       gold (#C89B2E) is the look this theme exists to preserve, but cream on it
       is 2.42:1 — unreadable. The gold stays wherever no text sits on it (seek
       handle, progress bar, chip edges); only the fill under a label goes dark
       enough to carry one. */
    --accent-fill: #8A6210;
    --accent-fill-hover: #6E4E0C;
    --danger: #934C29;        /* was #A3542D — 4.26:1 on its own tint */
    --danger-border: #D9B49B;
    --danger-bg: #F4E0D4;
    /* These four moved BECAUSE THE GROUND MOVED, not as a restyle. The waveform
       band (--panel2) went from #F2E9D2 to #E9DBB4, and every marking color was
       measured against the old, paler band: left alone, the unselected wash fell
       from 1.26:1 to 1.11:1 against its own background and the marking outline
       from 3.16:1 to 2.84:1, i.e. below the 3:1 floor. What is preserved here is
       the RELATIONSHIPS Amitai approved, not the hex values. */
    --mark: #DFC079;
    --mark-selected: #CFA84E;
    --mark-edge: #9E7113;
    --mark-edge-selected: #6E4A0C;
    --wave: #63511C;          /* one step down with the deeper --panel2, so the
                                 trace holds the 5.5:1 it was approved at */
    --duck: #2E2410;
    --seek-track: #E9DBB4;
    --seek-fill: #8A6210;     /* was #B78A24: 2.29:1 on the deeper track */
    --seek-handle: #7E5B12;
    --chart-expected: #B07E1A;
    --chart-peak: #7E5109;
    --chart-end: #452C05;
    --chart-blend: #8A7238;
    --chart-axis: #C4A970;
    --chart-grid: #E0D0A6;
    --chart-text: #6B5C3B;
    --card-top: transparent;
    --card-top-w: 1px;
    --card-title-size: 17px;
    --card-text-size: 14px;
    --orn-display: none;
    --h2-size: 17px;
    --panel-top: #E0D0A6;
    --panel-top-w: 1px;
    --logo-ink: #A67916;
    --logo-accent: #7E5109;
    --logo-stroke: 4.5;      /* 3.15:1 mark — the lightest-reading of the five */
    --mark-edge-w-sel: 1;
    --scrim: rgba(74, 63, 40, 0.5);
    --scrim-dim: rgba(74, 63, 40, 0.3);
    --shadow: rgba(74, 63, 40, 0.18);
    --shadow-strong: rgba(74, 63, 40, 0.3);
    --on-accent-veil: rgba(255, 248, 232, 0.1);
    --font-display: Charter, "Bitstream Charter", "Iowan Old Style", "Palatino Linotype", Cambria, Georgia, serif;
    --font-body: "Segoe UI", system-ui, -apple-system, sans-serif;
    --radius: 14px;
    --radius-card: 14px;
  }

}

/* MEADOW — light, ivory and forest green. The markings keep the gold heritage
   so a marked moment still reads the same way it does in Warm. */
@media screen {
  html[data-theme="meadow"] {
    --heading: #23412F;
    --nav-ink: #23412F;
    --btn-ink: #23412F;
    --tagline: #7A5912;
    --tagline-style: normal;
    --sub-style: normal;
    --card-top: transparent;
    --card-top-w: 1px;
    --card-title-size: 17px;
    --card-text-size: 14px;
    --orn-display: none;
    --radius-card: 4px;
    --h2-size: 17px;
    --panel-top: #D6CCAF;
    --panel-top-w: 1px;
    --logo-ink: #2E5340;      /* forest strokes, ochre ears and nose — the
                                 treatment design-alt-3 gives the lion */
    --logo-accent: #7A5912;
    /* 3.6, not 4.5: dark-on-light like Warm, but at 7.4:1 against its ivory the
       mark reads much heavier than Warm's 4.28:1, and the muzzle fused. */
    --logo-stroke: 3.6;
    --mark-edge-w-sel: 1;
    --bg: #F3EDDD;
    --panel: #FAF6EA;
    --panel2: #ECE4CE;
    --hover-fill: #CBDAC6;
    --video-bg: #1A211C;
    --video-ink: #F2F5EC;
    --video-ink-soft: #BFCDBB;
    --video-edge: #7E9384;
    --line: #D6CCAF;
    --line-strong: #7E9384;
    --ink: #26332B;
    --ink-soft: #55604F;
    --ink-faint: #5E6858;
    --accent: #2E5340;
    --accent-deep: #23412F;
    --accent-soft: #55705E;
    --accent-fill: #2E5340;
    --accent-fill-hover: #3E6B52;
    --accent-pale: #DFE8DC;
    --on-accent: #F2F5EC;
    --danger: #8B3F22;
    --danger-border: #C6A18E;
    --danger-bg: #F0DCCF;
    --mark: #EFE3C0;
    --mark-selected: #E2C87E;
    --mark-edge: #7A5912;
    --mark-edge-selected: #5E4510;
    --wave: #4E5A46;
    --duck: #6B2E12;
    --seek-track: #ECE4CE;
    /* was #C6D6C2 — a pale green on cream at 1.2:1, so how far you had scrubbed
       was close to invisible. #5E7A64 is 3.73:1 on the same track. */
    --seek-fill: #5E7A64;
    --seek-handle: #2E5340;
    --chart-expected: #75925F;
    --chart-peak: #2E5340;
    --chart-end: #0E2114;
    --chart-blend: #7A5912;
    --chart-axis: #A9B79F;
    --chart-grid: #D6CCAF;
    --chart-text: #55604F;
    --scrim: rgba(28, 38, 30, 0.55);
    --scrim-dim: rgba(28, 38, 30, 0.32);
    --shadow: rgba(28, 38, 30, 0.16);
    --shadow-strong: rgba(28, 38, 30, 0.3);
    --on-accent-veil: rgba(242, 245, 236, 0.12);
    --font-display: Constantia, Cambria, Georgia, serif;
    --font-body: Corbel, Candara, "Segoe UI", system-ui, sans-serif;
    --radius: 4px;
  }

}

/* DUSK — warm dark. Brown-charcoal surfaces, the gold carried through, soft
   rings instead of hard borders. Evening practice with headphones. */
@media screen {
  html[data-theme="dusk"] {
    --heading: #EEC96A;
    --nav-ink: #C4B79C;
    --btn-ink: #EEC96A;
    --tagline: #BFA265;
    --tagline-style: normal;
    --sub-style: normal;
    --card-top: transparent;
    --card-top-w: 1px;
    --card-title-size: 17px;
    --card-text-size: 14px;
    --orn-display: none;
    --radius-card: 14px;
    --h2-size: 17px;
    --panel-top: rgba(217, 174, 69, 0.16);
    --panel-top-w: 1px;
    /* Light-on-dark at 8.2:1, the heaviest-reading mark of the five, so the
       stroke comes down a fifth. Nothing is moved — the geometry is frozen. */
    --logo-ink: #D9AE45;
    --logo-accent: #A6812C;
    --logo-stroke: 3.6;
    --mark-edge-w-sel: 2;
    --bg: #201B15;
    --panel: #2A231A;
    --panel2: #332B1F;
    --hover-fill: #3A3122;
    --video-bg: #14100B;
    --video-ink: #EFE7D4;
    --video-ink-soft: #C4B79C;
    --video-edge: #8A7A5C;
    --line: rgba(217, 174, 69, 0.16);
    --line-strong: rgba(217, 174, 69, 0.34);
    --ink: #EFE7D4;
    --ink-soft: #C4B79C;
    --ink-faint: #AFA286;
    --accent: #D9AE45;
    --accent-deep: #EEC96A;   /* brighter, not darker: this is emphasis TEXT */
    --accent-soft: #BFA265;
    --accent-fill: #D9AE45;
    --accent-fill-hover: #EEC96A;
    --accent-pale: #4A3C1E;
    --on-accent: #241B0B;
    --danger: #E4A183;
    --danger-border: #8A5236;
    --danger-bg: #3A2418;
    /* SELECTED MARKINGS ON A DARK GROUND (2026-08-07). The wash alone cannot
       carry this state. The trace is stroked OVER the wash, so brightening the
       wash lifts the background under the trace and swallows it — at 0.38 the
       trace measured 2.77:1, under the 3:1 floor — and Dusk's trace has almost
       no headroom left above 0.34. So the separation is bought three ways at
       once: the UNSELECTED wash steps back, the OUTLINES separate properly
       (they were 1.31:1 apart, near-identical golds), and the selected outline
       is drawn at double weight. */
    --mark: rgba(217, 174, 69, 0.12);
    --mark-selected: rgba(217, 174, 69, 0.32);
    --mark-edge: #927939;   /* was #8A7236: 3.01:1 on --panel2, 0.01 over the floor */
    --mark-edge-selected: #EEC96A;
    --wave: #BFA97E;
    --duck: #FFF3DC;
    --seek-track: #241E15;
    --seek-fill: #8A7236;
    --seek-handle: #EEC96A;
    /* On a dark ground the ramp runs the other way round in LIGHTNESS while
       keeping the same EMPHASIS order: expected is the quietest, end the
       loudest. "Expected = lightest" on a dark theme would make the least
       important series shout and bury the most important one in the background. */
    --chart-expected: #8A7236;
    --chart-peak: #D9AE45;
    --chart-end: #FFEFC6;
    --chart-blend: #C4B79C;
    --chart-axis: #5A4C33;
    --chart-grid: #3A3122;
    --chart-text: #C4B79C;
    --scrim: rgba(12, 9, 6, 0.72);
    --scrim-dim: rgba(12, 9, 6, 0.45);
    --shadow: rgba(0, 0, 0, 0.5);
    --shadow-strong: rgba(0, 0, 0, 0.7);
    --on-accent-veil: rgba(239, 231, 212, 0.1);
    --font-display: "Segoe UI", system-ui, -apple-system, sans-serif;
    --font-body: "Segoe UI", system-ui, -apple-system, sans-serif;
    --radius: 14px;
  }

}

/* NIGHT STUDIO — a recording room after dark. Blue-black surfaces, warm
   parchment text, one gold that carries the marked moments. */
@media screen {
  html[data-theme="night"] {
    --heading: #F0C25C;
    --nav-ink: #AFB4BF;
    --btn-ink: #F0C25C;
    --tagline: #9AA0AC;
    --tagline-style: normal;
    --sub-style: normal;
    --card-top: transparent;
    --card-top-w: 1px;
    --card-title-size: 17px;
    --card-text-size: 14px;
    --orn-display: none;
    --radius-card: 8px;
    --h2-size: 17px;
    --panel-top: #2A3040;
    --panel-top-w: 1px;
    /* 8.6:1 against its blue-black — see the note in the Dusk block */
    --logo-ink: #D9A93F;
    --logo-accent: #A6802C;
    --logo-stroke: 3.6;
    --mark-edge-w-sel: 2;
    --bg: #101319;
    --panel: #171B24;
    --panel2: #1E2330;
    --hover-fill: #232838;
    --video-bg: #05060A;
    --video-ink: #E9E4D4;
    --video-ink-soft: #AFB4BF;
    --video-edge: #5A6478;
    --line: #2A3040;
    --line-strong: #3A4156;
    --ink: #E9E4D4;
    --ink-soft: #AFB4BF;
    --ink-faint: #9AA0AC;
    --accent: #D9A93F;
    --accent-deep: #F0C25C;   /* brighter, not darker: this is emphasis TEXT */
    --accent-soft: #BE9235;
    --accent-fill: #D9A93F;
    --accent-fill-hover: #F0C25C;
    --accent-pale: #2A2416;
    --on-accent: #1A1406;
    --danger: #E09A79;
    --danger-border: #7A4B36;
    --danger-bg: #2E1E16;
    /* see the Dusk block for why the wash alone cannot carry this state. Night's
       trace is brighter than Dusk's, so it has room for the selected wash to go
       up as well as for the unselected one to step back. */
    --mark: rgba(217, 169, 63, 0.15);
    --mark-selected: rgba(217, 169, 63, 0.44);
    --mark-edge: #8A6E2B;
    --mark-edge-selected: #F0C25C;
    --wave: #AEB8CE;
    --duck: #E8A54F;
    --seek-track: #0B0D12;
    --seek-fill: #8A6E2B;
    --seek-handle: #F0C25C;
    /* emphasis order, not lightness order — see the note in the Dusk block */
    --chart-expected: #7E6A34;
    --chart-peak: #D9A93F;
    --chart-end: #FBF0D2;
    --chart-blend: #AFB4BF;
    --chart-axis: #3A4156;
    --chart-grid: #232838;
    --chart-text: #AFB4BF;
    --scrim: rgba(4, 5, 8, 0.74);
    --scrim-dim: rgba(4, 5, 8, 0.45);
    --shadow: rgba(0, 0, 0, 0.6);
    --shadow-strong: rgba(0, 0, 0, 0.8);
    --on-accent-veil: rgba(233, 228, 212, 0.1);
    --font-display: Bahnschrift, "Segoe UI Semibold", "Segoe UI", system-ui, sans-serif;
    --font-body: "Segoe UI", system-ui, -apple-system, sans-serif;
    --radius: 8px;
  }

}

:root {
  /* Text weight. Measured on Windows 11 (Segoe UI) and matched to the faces
     that actually exist there: 400 Regular, 500/600 both Semibold, 700 Bold,
     800/900 both Black. So the default is one real step up from plain (body
     Semibold, headings Bold), and the bold setting is another real step
     (body Bold, headings Black) — no request lands on a weight the font
     cannot render. On iOS the SF stack has every step natively. */
  --w-body: 500;
  --w-strong: 700;
}
html.weight-bold { --w-body: 700; --w-strong: 900; }

/* Text size. `zoom` scales layout as well as glyphs, so nothing has to be
   re-measured — but viewport units are NOT scaled by it, so anything sized in
   vh/vw is written as a percentage of the fixed containing block instead. */
html.text-larger { zoom: 1.15; }
html.text-largest { zoom: 1.3; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  font-weight: var(--w-body);
  /* no min-height: 100vh — body's background already paints the whole canvas,
     and under `zoom` a viewport-unit height would force a permanent scrollbar */
}

button { font: inherit; cursor: pointer; }

/* ---------- header ---------- */
header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
/* .logo, not svg.logo. index.html's mark is an inline <svg> and help.html's is
   still an <img> — narrowing this rule to `svg.logo` during the theme batch
   left help.html's mark UNSIZED, and assets/logo.svg carries a viewBox with no
   width/height, so it has a ratio but no intrinsic size and grew to fill the
   flex line: 393x393px inside a 432px-tall header, and 133px of sideways
   scroll at 375px. Matching on the class covers both element types. */
header .logo { width: 52px; height: 52px; flex: none; }
.brand-name {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: var(--w-strong);
  letter-spacing: 0.10em;
  color: var(--heading);
}
.brand-tag {
  font-family: var(--font-display);
  font-style: var(--tagline-style, normal);
  font-size: 11px;
  letter-spacing: 0.3em;
  color: var(--tagline, var(--accent-soft));
}
/* The pills and the Aa button share one wrapping group, so on a phone they
   fill a row and wrap together instead of Aa landing on a line of its own.
   Wrapping is essential: five controls at the largest text size are wider
   than a phone screen, and a nav that cannot wrap scrolls the whole page. */
.header-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
nav { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
/* Help is an <a> because it really does go to another page, while the rest
   switch views in place — but it has to read as the same pill in the row. */
nav button, nav a {
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--nav-ink);
  padding: 7px 18px;
  border-radius: 999px;
  font-size: 14px;
  line-height: 1.5;
  text-decoration: none;
  white-space: nowrap;
}
nav a { display: inline-flex; align-items: center; }
nav button:hover, nav a:hover { background: var(--panel); }
nav button.active { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }

main { max-width: 1120px; margin: 0 auto; padding: 18px 22px 60px; }
.view.hidden { display: none; }

/* ---------- stepper ---------- */
.stepper { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.step {
  flex: 1 1 150px;
  text-align: center;
  font-size: 13px;
  padding: 8px 6px;
  border-radius: 8px;
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--nav-ink);
}
.step:hover { background: var(--panel2); }
.step.active { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }
.step.active:hover { background: var(--accent-fill-hover); }
.step.done { background: var(--accent-pale); border-color: var(--accent-pale); }
.step.done:hover { background: var(--accent-pale); filter: brightness(0.97); }

/* ---------- panels ---------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-top: var(--panel-top-w, 1px) solid var(--panel-top, var(--line));
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 14px;
}
/* A THEME HAS TO BE VISIBLE OUTSIDE HOME. Before this, Warm's display
   treatment lived entirely on the home screen: its 20px serif card titles, its
   italic serif intro, its ornament and its ochre card edge. Every other view
   fell back to a shared 17px Charter heading, so Warm and Lantern were
   character-for-character identical in the studio, the library, My sounds and
   My progress — only the ink color differed. These three hooks carry each
   theme's display voice into every panel on every view:
     --h2-size    how loud a panel heading is (Warm 20px, the rest 17px)
     --panel-top  the top edge of a panel — Warm's 2px ochre rule, which is its
                  card signature, now runs across the whole app
     --sub-style  the standfirst under a heading, italic serif in Warm
   A theme that wants none of this simply repeats the shared values. */
.panel h2 {
  font-family: var(--font-display);
  margin: 0 0 10px; font-size: var(--h2-size, 17px);
  font-weight: var(--w-strong); color: var(--heading);
}
/* the line directly under a heading is a standfirst, not a label — it is the
   one piece of body copy a theme may set in its display face. Deliberately NOT
   `.hint` in general: half the hints in this app are control labels sitting
   beside a select or a slider, and italic serif on those reads as a mistake. */
h2 + .hint {
  font-family: var(--font-display);
  font-style: var(--sub-style, normal);
  font-size: 14px;
}
.hint { font-size: 13px; color: var(--ink-soft); }

/* ---------- dropzone ---------- */
.dropzone {
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);
  padding: 46px 20px;
  text-align: center;
  color: var(--nav-ink);
  background: var(--panel);
  cursor: pointer;
  transition: background 0.15s;
}
.dropzone:hover, .dropzone.drag { background: var(--panel2); }
.dropzone .big { font-family: var(--font-display); font-size: 17px; font-weight: var(--w-strong); }

/* ---------- studio layout ---------- */
.studio-grid { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 860px) { .studio-grid { grid-template-columns: 1fr; } }

video#player {
  width: 100%;
  max-height: 340px;
  border-radius: 10px;
  background: var(--video-bg);
  display: block;
}
video#player.audio-only { max-height: 90px; }

.transport { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.time { font-family: Consolas, monospace; font-size: 13px; color: var(--nav-ink); }

/* Reserved space for status text that appears and clears. Without it, every
   autosave ("Saving…" → "All changes saved") reflowed the row and the UI
   jumped about a second after every edit. The hidden ::after block holds each
   element at the width of the longest string it can ever show — the \A puts
   every candidate on its own zero-height line, so the widest one wins whatever
   the font metrics are. */
/* Height matters as much as width: in a wrapping flex row the reserved-width
   span can sit on its own line, and an empty span is 0px tall — text appearing
   would grow that line and push everything below it down. The zero-width space
   in ::before keeps a line box open at the font's exact line height, so the
   element is always precisely one text line tall, empty or not. */
/* The group may wrap and the indicator may shrink on narrow phones — its
   reserved width plus the two buttons is wider than a ~380px viewport, and
   without this the group overflowed the panel. The width stays constant per
   viewport across all indicator states, so nothing ever jumps. */
.transport-status {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 100%;
}
#save-indicator { display: inline-block; white-space: nowrap; text-align: right; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
#save-indicator::before { content: "\200B"; }
#save-indicator::after {
  content: "Couldn't save on this device\A Not saving — reload the page\A All changes saved";
  white-space: pre; display: block; height: 0; visibility: hidden;
}
#practice-left { display: inline-block; white-space: nowrap; }
#practice-left::before { content: "\200B"; }
#practice-left::after {
  content: "10:00.0 left";
  display: block; height: 0; visibility: hidden;
}
/* block-level, on its own row — reserving one line of height stops the
   appear/clear jump; width changes can't move anything */
#export-status { min-height: 1.4em; }

.btn {
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--btn-ink);
  padding: 8px 16px;
  border-radius: 9px;
  font-size: 14px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.btn:hover { background: var(--panel2); }
.btn.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); font-weight: var(--w-strong); }
.btn.primary:hover { background: var(--accent-fill-hover); }
.btn.small { padding: 5px 11px; font-size: 13px; }
.btn.danger { color: var(--danger); border-color: var(--danger-border); }
.btn:disabled { opacity: 0.45; cursor: default; }
.btn.toggled { background: var(--accent-pale); border-color: var(--accent); }

/* ---------- waveform ---------- */
.wave-wrap { position: relative; margin-top: 12px; }
canvas#wave {
  width: 100%;
  height: 110px;
  display: block;
  background: var(--panel2);
  border-radius: 8px;
  cursor: crosshair;
  touch-action: none;
}
.wave-help { font-size: 12px; color: var(--ink-soft); margin-top: 5px; }

/* ---------- seek strip (scrubbing only — never marks or selects) ---------- */
/* THESE FOUR RULES NOW READ THE --seek-* TOKENS, WHICH IS THE WHOLE POINT OF
   THEM. Until 2026-08-07 they did not: the strip painted from --panel2, the
   fill from --accent-pale and the handle from --accent, while the three
   per-theme --seek-* values sat unread. Two separate "fixes" were written into
   STATUS.md, measured by the sweep and one of them hand-tested and passed —
   and not one pixel had changed, because the sweep was scoring --seek-fill on
   --seek-track, a pair nothing painted. As painted the scrub fill was
   1.01-1.30:1 in every theme; in Lantern nothing on the control cleared 3:1.
   See S0.3.3b: a token no rule reads is a failure, not a pass. */
.seek-strip {
  position: relative;
  height: 14px;
  margin-top: 6px;
  background: var(--seek-track);
  border-radius: 7px;
  cursor: pointer;
  touch-action: none;
}
/* HOVER MUST NOT REPAINT THE GROUND THE FILL SITS ON. The old rule used --well,
   which is the same color as --accent-pale in Meadow, so Meadow's fill went to
   exactly 1.00:1 the moment a mouse touched the strip — and a mouse is the only
   way it is scrubbed on a desktop. Recoloring the track to a --seek-track-hover
   had the same shape of problem from the other side: measured AS PAINTED it
   dropped Warm to 2.82:1 and Dusk to 2.97:1, because any step the track takes is
   a step toward or away from the fill sitting on it.
   So the track is constant and hover uses a channel of its own: a ring in the
   theme's own handle color, which is >=3:1 on that track by construction. */
.seek-strip:hover { box-shadow: 0 0 0 2px var(--seek-handle); }
.seek-strip:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.seek-fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 0;
  background: var(--seek-fill);
  border-radius: 7px 0 0 7px;
  pointer-events: none;
}
.seek-handle {
  position: absolute;
  left: 0; top: 50%;
  width: 14px; height: 14px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--seek-handle);
  /* the ring is a GAP IN THE TRACK, so it is the track's color — not --panel,
     which is a different surface and in Lantern left a gold dot ringed in
     straw gold at 1.25:1 */
  border: 2px solid var(--seek-track);
  box-sizing: border-box;
  pointer-events: none;
}

/* ---------- detection controls ---------- */
.detect-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
select, input[type="text"] {
  font: inherit;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: 7px 10px;
  /* A <select>'s intrinsic width is its LONGEST OPTION, and it will happily
     exceed its container: #lib-category, whose options include "Body Part
     Sounds", measured 356px inside a 320px screen and was the sole reason the
     Library view scrolled sideways on a phone in every theme. min-width lets
     it shrink inside a flex row; max-width stops it anywhere else. The label
     is truncated by the control itself, and the full text is still in the
     dropdown when it is opened. */
  max-width: 100%;
  min-width: 0;
}
input[type="range"] { accent-color: var(--accent); }
/* THE ONLY FAINT-TEXT SURFACE IN THE APP, and until now nothing painted it —
   the UA did, from its own light-mode default, which measured 3.37:1 on Dusk
   and 3.74:1 on Night. --ink-faint was declared in all five themes and read by
   nothing; this is the role it was named for. */
::placeholder { color: var(--ink-faint); opacity: 1; }

/* ---------- region chips ---------- */
.chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.chip {
  font-size: 12px;
  background: var(--panel2);
  color: var(--accent-deep);
  border: 1px solid transparent;
  padding: 4px 11px;
  border-radius: 999px;
  cursor: pointer;
}
/* SELECTION IS NOT ALLOWED TO BE COLOR ALONE (SC 1.4.1), and color was doing
   all of it: in Lantern the selected fill was 1.11:1 against the unselected one
   and the border 1.86:1, so which marking was selected — and which sound was
   armed before pressing "Use for all markings" — simply was not perceivable.
   Three cues now: a tick, a doubled edge, and the fill.
   The tick's space is RESERVED ON EVERY ITEM and only made visible on the
   selected one, so selecting never reflows the row. The extra edge weight is an
   inset shadow rather than a wider border for the same reason. */
.chip::before, .sound-item::before {
  content: "\2713";
  font-weight: var(--w-strong);
  visibility: hidden;
  flex: none;
}
/* the chip is not a flex container, so its tick needs its own gap — and the
   margin is reserved on the unselected chip too, since visibility:hidden still
   occupies the box */
.chip::before { margin-right: 0.35em; }
.chip.selected::before, .sound-item.selected::before { visibility: visible; }
.chip.selected, .sound-item.selected {
  box-shadow: inset 0 0 0 1px var(--accent-deep);
}
.chip.selected { border-color: var(--accent-deep); background: var(--accent-pale); }
.chip .no-sound { color: var(--danger); }

/* ---------- sound picker ---------- */
.shelf-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.shelf-tabs button {
  font-size: 12px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--accent-deep);
  padding: 4px 12px;
  border-radius: 999px;
}
.shelf-tabs button.active { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }

.sound-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; max-height: 300px; overflow-y: auto; }
@media (max-width: 500px) { .sound-list { grid-template-columns: 1fr; } }
.sound-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  background: var(--panel2);
  color: var(--ink);
  padding: 7px 9px;
  border-radius: 8px;
  border: 1px solid transparent;
}
.sound-item:hover { border-color: var(--line-strong); }
.sound-item.selected { border-color: var(--accent-deep); background: var(--accent-pale); }
.sound-item .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.icon-btn {
  border: none;
  background: transparent;
  color: var(--accent-deep);
  padding: 3px;
  border-radius: 6px;
  display: inline-flex;
  font-size: 15px;
}
.icon-btn:hover { background: var(--panel); }

/* ---------- sliders panel ---------- */
.slider-row { margin-top: 8px; }
.slider-row label { display: flex; justify-content: space-between; font-size: 13px; color: var(--ink-soft); margin-bottom: 4px; }
.slider-row input { width: 100%; }
.slider-ends { display: flex; justify-content: space-between; font-size: 11px; color: var(--accent-soft); }

/* ---------- library ---------- */
.lib-toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
.lib-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
}
.lib-card .title { font-size: 13px; font-weight: var(--w-strong); color: var(--ink); margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lib-card .meta { font-size: 11px; color: var(--ink-soft); margin-bottom: 8px; }
.lib-card .actions { display: flex; gap: 5px; flex-wrap: wrap; }
.lib-card video { width: 100%; border-radius: 7px; background: var(--video-bg); margin-bottom: 6px; }

/* ---------- record ---------- */
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); display: inline-block; animation: pulse 1.2s infinite; }
@keyframes pulse { 50% { opacity: 0.3; } }

/* ---------- toast & progress ---------- */
#toast {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent-deep);
  color: var(--on-accent);
  padding: 10px 22px;
  border-radius: 999px;
  font-size: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s;
  max-width: 90%;   /* % of the viewport, so it stays correct under zoom */
  text-align: center;
  z-index: 50;
}
#toast.show { opacity: 1; }

.progress-wrap { margin-top: 10px; }
.progress-bar { height: 8px; background: var(--panel2); border-radius: 999px; overflow: hidden; }
.progress-bar > div { height: 100%; width: 0%; background: var(--accent); transition: width 0.2s; }

footer {
  text-align: center;
  font-size: 12px;
  color: var(--accent-soft);
  padding: 18px;
  border-top: 1px solid var(--line);
}
footer a { color: var(--accent-deep); }

.hidden { display: none !important; }

/* ---------- my progress ---------- */
.chart-wrap { margin-top: 4px; }
.chart-wrap canvas { width: 100%; display: block; }
.chart-legend {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 13px;
  color: var(--ink-soft);
}
.chart-legend .lg { display: inline-flex; align-items: center; gap: 7px; }
.chart-legend .sw { width: 14px; height: 12px; border-radius: 3px; flex: none; }
/* A LEGEND HAS TO BE PAINTED IN THE CHART'S OWN COLORS. These four were
   accent tokens, not chart tokens, so the key beside the picture named one set
   of colors while the bars were drawn in another — which is worse than no key
   at all once the three series are deliberately far apart. */
.sw-expected { background: var(--chart-expected); }
.sw-peak { background: var(--chart-peak); }
.sw-end { background: var(--chart-end); }
.sw-blend { background: transparent; border-bottom: 2px dashed var(--chart-blend); border-radius: 0; height: 8px; }
/* --chart-blend, not --accent-soft: this key names the mark drawSessionBars
   draws for a skipped answer, and that mark is stroked in C.blend. It was an
   accent token, so the swatch showed a color that appears nowhere on the
   chart — 180 degrees off in Night. */
.sw-skip { background: repeating-linear-gradient(90deg, var(--chart-blend) 0 3px, transparent 3px 6px); height: 3px; }

/* a wide table scrolls inside its own box rather than pushing the page sideways */
.table-scroll { overflow-x: auto; }
/* a minimum width keeps the columns readable on a phone — the table scrolls
   inside its own box rather than crushing words into single letters */
.progress-table { border-collapse: collapse; width: 100%; min-width: 620px; font-size: 13px; }
.progress-table th, .progress-table td {
  border-bottom: 1px solid var(--line);
  padding: 8px 10px;
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
}
.progress-table th {
  color: var(--accent-deep);
  font-weight: var(--w-strong);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.progress-table td.num { font-family: Consolas, monospace; }
/* only the numbers hold a single line; the wordy columns wrap so the table
   fits its panel instead of pushing the last column out of sight */
.progress-table td:nth-child(1) { white-space: normal; min-width: 96px; }
.progress-table td:nth-child(2) { white-space: normal; max-width: 160px; }
.progress-table td.feelings { white-space: normal; min-width: 150px; color: var(--ink-soft); }
.progress-table tr.unfinished td { color: var(--ink-soft); }
.progress-table .tag {
  display: inline-block;
  font-size: 11px;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--panel2);
  color: var(--accent-deep);
  margin-left: 6px;
}
.note-row td { white-space: normal; color: var(--ink); background: var(--panel2); }
.btn.note-btn { padding: 2px 10px; font-size: 12px; border-radius: 999px; }

/* ---------- share with my therapist ---------- */
.share-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 12px; }
/* The label changes to "Building your report…" while it works. Reserve the
   wider of the two strings on the inner span — .btn is a flex container, so a
   sizer placed on the button itself would sit BESIDE the label, not under it. */
#share-btn-label { display: inline-block; text-align: center; }
#share-btn-label::after {
  content: "Share with my therapist\A Building your report…";
  white-space: pre; display: block; height: 0; visibility: hidden;
}
/* one reserved line, same reason as #export-status: appearing text must not
   push the panel around */
#share-status { min-height: 1.4em; margin: 8px 0 0; }
.share-next {
  margin-top: 12px;
  padding: 14px 16px;
  background: var(--panel2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* a link styled as a button still carries the browser's underline */
a.btn { text-decoration: none; }
.share-next .share-line { margin: 0 0 12px; }
.share-next .hint { margin: 10px 0 0; }
#share-file-name { font-family: Consolas, monospace; word-break: break-all; }

/* ---------- text size and weight control ---------- */
/* The popover is absolutely positioned, so opening and closing it can never
   move anything on the page. */
.textprefs { position: relative; }
#btn-textprefs {
  font-size: 15px;
  font-weight: var(--w-strong);
  letter-spacing: 0.03em;
  padding: 6px 13px;
  border-radius: 999px;
}
.textprefs-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 70;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 14px 16px;
  box-shadow: 0 10px 30px var(--shadow);
  min-width: 210px;
}
.tp-label {
  margin: 0 0 7px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
.tp-row { display: flex; gap: 6px; flex-wrap: wrap; }
.tp-row + .tp-label { margin-top: 14px; }
.tp-row .btn[aria-pressed="true"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
  font-weight: var(--w-strong);
}
/* the theme choices stack, so the swatch and the name stay on one line each */
.tp-themes { flex-direction: column; align-items: stretch; }
.tp-themes .btn { display: flex; align-items: center; gap: 9px; text-align: left; }
.tp-swatch {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  flex: none;
  border: 1px solid var(--line-strong);
}
/* Each swatch shows its OWN theme's surface and accent, not the active one —
   the point is to preview the choice. These four pairs are the only place
   outside the theme blocks that names a color, and they are quoting them. */
.tp-swatch[data-theme="warm"]    { background: #F7F0E1; box-shadow: inset 0 0 0 5px #B04E28; }
.tp-swatch[data-theme="lantern"] { background: #F2E6C6; box-shadow: inset 0 0 0 5px #C89B2E; }
.tp-swatch[data-theme="meadow"]  { background: #F3EDDD; box-shadow: inset 0 0 0 5px #2E5340; }
.tp-swatch[data-theme="dusk"]    { background: #201B15; box-shadow: inset 0 0 0 5px #D9AE45; }
.tp-swatch[data-theme="night"]   { background: #101319; box-shadow: inset 0 0 0 5px #D9A93F; }

/* ---------- home ---------- */
/* the brand is the way home from anywhere — it has to feel like a control */
.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 12px;
  padding: 4px 12px 4px 4px;
  margin-left: -4px;
  text-align: left;
  cursor: pointer;
  text-decoration: none;   /* it is an <a> on the help page */
  min-width: 0;            /* so the long name can wrap instead of overflowing */
}
.brand-text { min-width: 0; }
.brand:hover { background: var(--panel); border-color: var(--line); }
.brand:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.brand-text, .brand-name, .brand-tag { display: block; }

.home-hero { text-align: center; padding: 30px 10px 4px; }
.home-logo { width: var(--home-logo-size, 96px); height: var(--home-logo-size, 96px); flex: none; }
/* the ornament under the greeting: present only in the theme that has one */
.home-orn {
  display: var(--orn-display, none);
  color: var(--card-top, var(--accent));
  font-size: 15px;
  letter-spacing: 0.6em;
  margin: 12px 0 10px;
}
.home-greeting {
  font-family: var(--font-display);
  margin: 14px 0 4px;
  font-size: 28px;
  font-weight: var(--w-strong);
  letter-spacing: -0.005em;
  color: var(--heading);
}
.home-sub {
  font-family: var(--font-display);
  font-style: var(--sub-style, normal);
  margin: 0 auto; max-width: 560px; color: var(--ink-soft); font-size: 15px;
}

/* two across, so the cards sit as a balanced block rather than leaving one
   stranded on a row of its own. (Phase B adds a fifth card, My progress —
   widen to three columns then.) */
.home-cards {
  display: grid;
  /* min() so a card can never be wider than the space it has: at the largest
     text size on a small phone a fixed 272px minimum overflowed the page */
  grid-template-columns: repeat(auto-fit, minmax(min(272px, 100%), 1fr));
  gap: 14px;
  max-width: 900px;
  margin: 32px auto 0;
}
.home-about { max-width: 900px; }
.home-card {
  /* FLEX-COLUMN, NOT BLOCK, and that is the whole fix for the titles sitting at
     different heights. Four of the five cards are <button>s, and a button
     vertically CENTERS its own content — a UA behavior that `display: block`
     does not switch off. The grid stretches every card to the tallest in the
     row, so the short cards ("My sounds") floated their title down the middle
     while the long one started at the top, and the fifth card (an <a>, which
     has no such behavior) sat differently again. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  font: inherit;
  text-align: left;
  text-decoration: none;
  background: var(--panel);
  border: 1px solid var(--line);
  border-top: var(--card-top-w, 1px) solid var(--card-top, var(--line));
  border-radius: var(--radius-card, var(--radius));
  padding: 20px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.15s;
}
.home-card:hover {
  background: var(--panel2);
  border-color: var(--line-strong);
  transform: translateY(-2px);
}
.home-card:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.home-card-title {
  font-family: var(--font-display);
  display: block;
  font-size: var(--card-title-size, 17px);
  font-weight: var(--w-strong);
  color: var(--heading);
  margin-bottom: 6px;
}
.home-card-text { display: block; font-size: var(--card-text-size, 14px); line-height: 1.6; color: var(--ink-soft); }

.home-about {
  max-width: 900px;
  margin: 26px auto 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2px 20px;
}
.home-about summary {
  cursor: pointer;
  padding: 15px 0;
  font-weight: var(--w-strong);
  color: var(--accent-deep);
  list-style: none;
}
.home-about summary::-webkit-details-marker { display: none; }
.home-about summary::before { content: "▸  "; color: var(--accent-soft); }
.home-about[open] summary::before { content: "▾  "; }
.home-about-body { padding-bottom: 18px; }
.home-about-body p { font-size: 15px; line-height: 1.65; margin: 0 0 12px; }
.home-about-body p:last-child { margin-bottom: 0; }
.home-about-body a { color: var(--accent-deep); }
@media (prefers-reduced-motion: reduce) { .home-card:hover { transform: none; } }

/* ---------- reprocessing sessions ---------- */

.session-cta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.session-cta .hint { flex: 1 1 240px; min-width: 0; }

/* check-in overlays: calm, full-screen, in the app's own style */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: var(--bg);
  overflow-y: auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 24px 16px 40px;
}
.overlay-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  width: 100%;
  max-width: 620px;
  margin: auto;
}
.overlay-card.narrow { max-width: 460px; }
.overlay-card.narrow p { margin: 0 0 4px; font-size: 15px; }
.overlay-head { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.overlay-logo { width: 44px; height: 44px; flex: none; }
.overlay-head h2 { margin: 0; font-size: 19px; font-weight: var(--w-strong); color: var(--heading); }
.overlay-head .hint { margin: 3px 0 0; }
.overlay-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 22px;
}
/* a quiet way out — never as loud as the action it sits beside */
.btn.link {
  border-color: transparent;
  background: transparent;
  color: var(--ink-soft);
  text-decoration: underline;
  padding: 8px 6px;
}
.btn.link:hover { background: transparent; color: var(--accent-deep); }
.scale-note { margin: 6px 0 0; }

.emo-block { margin-top: 22px; }
.emo-q { display: block; margin: 0 0 8px; font-size: 15px; font-weight: var(--w-strong); color: var(--accent-deep); }
.emo-q .hint { font-weight: var(--w-body); }
.pick-chips { display: flex; gap: 7px; flex-wrap: wrap; }
.pick-chip {
  font: inherit;
  font-size: 14px;
  background: var(--panel2);
  color: var(--accent-deep);
  border: 1px solid var(--line-strong);
  padding: 7px 14px;
  border-radius: 999px;
  cursor: pointer;
}
.pick-chip:hover { background: var(--hover-fill); }
.pick-chip[aria-pressed="true"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
  font-weight: var(--w-strong);
}
.emo-search {
  font: inherit;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 9px 16px;
  width: 100%;
  margin: 10px 0 0;
}
.emo-search:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }
#emo-pre-results:not(:empty), #emo-post-results:not(:empty) { margin-top: 10px; }
#ses-note {
  font: inherit;
  color: var(--ink);
  background: var(--panel2);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  padding: 10px 12px;
  width: 100%;
  resize: vertical;
}

/* ---------- focus mode ---------- */
/* The scrim and its bar live here; the <video> itself is promoted by the rule
   at the bottom and is never moved in the DOM, so the mix graph survives. */
#focus-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--video-bg);
  display: flex;
  flex-direction: column;
}
:root { --focus-bar-h: 64px; --focus-hint-h: 40px; }
.focus-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 18px;
  height: var(--focus-bar-h); /* the player's top inset is measured from it */
  flex: none;
}
.focus-title {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--video-ink-soft);
  white-space: nowrap;
}
.focus-time {
  margin-left: auto;
  font-family: Consolas, monospace;
  font-size: 15px;
  color: var(--video-ink);
  display: inline-block;
  white-space: nowrap;
  text-align: right;
  /* Finish is the way out of a session and must never be pushed off the edge.
     A width media query cannot protect it on its own: under the largest text
     setting `zoom` shrinks the layout viewport this fixed overlay fills, but
     the query still sees the unscaled device width. So the clock — the only
     thing here that is merely informative — is allowed to shrink to nothing,
     while the two buttons never shrink at all. */
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
}
/* reserved width, same technique as #save-indicator: the label changes every
   second and must never move the Finish button */
.focus-time::before { content: "\200B"; }
.focus-time::after {
  content: "00:00.0 elapsed\A 00:00.0 left";
  white-space: pre; display: block; height: 0; visibility: hidden;
}
/* Pause and Finish are the only two controls on this screen and Finish is the
   way out. They LOOK small and quiet (34px) but the finger target stays 44px
   via the ::after below — shrinking the visible button must not shrink what
   a thumb has to hit on an iPad. */
.focus-bar .btn {
  background: transparent;
  border-color: var(--video-edge);
  color: var(--video-ink);
  font-size: 13px;
  padding: 6px 15px;
  min-height: 34px;
  border-radius: 9px;
  position: relative;
}
.focus-bar .btn::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;   /* the invisible hit area, centered on the visible button */
}
.focus-bar .btn { flex: none; } /* see .focus-time — the clock yields, not these */
/* A genuinely narrow bar (a small phone, or any phone at the largest text
   setting). The play button drops its word and keeps its icon — the button
   carries an aria-label, so a screen reader loses nothing — and the clock
   shrinks a step and reserves a shorter string, which app.js writes to match. */
#focus-overlay.tight .focus-bar { gap: 10px; padding: 0 12px; }
#focus-overlay.tight #focus-play-label { display: none; }
#focus-overlay.tight .focus-time { font-size: 13px; }
#focus-overlay.tight .focus-time::after { content: "00:00 left"; }
.focus-bar .btn.focus-finish {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
  font-weight: var(--w-strong);
}
.focus-bar .btn.focus-finish:hover { background: var(--accent-fill); }
/* "Play" and "Pause" are different widths — reserve the wider one so the bar
   never shifts when playback state changes */
#focus-play-label { display: inline-block; min-width: 3.4em; text-align: left; }
.save-problem {
  color: var(--danger);
  font-size: 14px;
  margin: 16px 0 0;
}
/* One soft check on an untouched answer, asked as a small card OVER the
   check-in rather than a line of text beside it — text below the buttons was
   easy to miss entirely. The screen behind stays visible but steps back, so
   the question reads as a gentle aside, not another whole screen. */
.overlay.dim {
  background: var(--scrim);
  z-index: 85;
  align-items: center;
}
.overlay.dim .overlay-card {
  max-width: 420px;
  padding: 24px 26px;
  box-shadow: 0 14px 44px var(--shadow-strong);
}
.overlay.dim .overlay-card p { font-size: 16px; line-height: 1.5; }
.overlay.dim .overlay-actions { margin-top: 20px; }
.focus-bar .btn:hover { background: var(--on-accent-veil); }
.focus-hint {
  position: absolute;
  left: 0; right: 0; bottom: 12px;
  margin: 0;
  text-align: center;
  font-size: 12px;
  color: var(--video-ink-soft);
}

/* After a few still seconds the chrome gets out of the way and leaves just the
   picture. It never hides while the video is paused — hiding the controls of a
   stopped video would strand the client. */
.focus-bar, .focus-hint {
  transition: opacity 0.45s ease, transform 0.45s ease;
}
#focus-overlay.chrome-hidden .focus-bar {
  opacity: 0;
  transform: translateY(-100%);
  pointer-events: none;
}
#focus-overlay.chrome-hidden .focus-hint {
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .focus-bar, .focus-hint { transition: opacity 0.2s ease; }
  #focus-overlay.chrome-hidden .focus-bar { transform: none; }
}
@media (max-width: 520px) { .focus-title { display: none; } }

body.focus-mode { overflow: hidden; }
/* <video> is a REPLACED element: with width/height auto it takes its INTRINSIC
   size and ignores left/right, which pinned the picture to the left at partial
   height with a dead field beside it. Explicit width and height fill the area,
   and object-fit:contain centers the picture — a portrait clip gets calm dark
   sides instead of being stretched. */
body.focus-mode #player {
  position: fixed;
  left: 0;
  top: var(--focus-bar-h);
  width: 100%;
  height: calc(100% - var(--focus-bar-h) - var(--focus-hint-h));
  max-width: none;
  max-height: none;
  margin: 0;
  border-radius: 0;
  background: var(--video-bg);
  object-fit: contain;
  /* taps fall through to the scrim, which owns tap-to-play/pause; the studio
     behind stays inert for the keyboard */
  pointer-events: none;
  z-index: 95;
}
/* the toast must stay readable above every overlay */
#toast { z-index: 120; }
