/* app.css — base layout, reset, shared components */

/* Saira Condensed — for timers / big numbers ONLY (weights 500/600/700) */
@import url("https://fonts.googleapis.com/css2?family=Saira+Condensed:wght@500;600;700&display=swap");

/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

/* The `hidden` attribute ALWAYS hides. The browser's own `[hidden] { display:
 * none }` is a UA-stylesheet rule, so ANY author rule that sets `display` on
 * the same element silently overrode it — `.usage-upgrade { display: inline-
 * flex }` kept the Settings "Upgrade to Pro" pill visible for EVERY account
 * (fix/settings-unentitled-display, 2026-09-09), and `.authcheck` /
 * `.switch-row` did the same to the Face ID controls on devices without
 * biometrics. `!important` so no future `display:` rule can recur it.
 * Hiding that must be driven by a CLASS keeps its own modifier (`.tabbar.is-
 * hidden`, `.is-tip-hidden`); this rule only enforces the attribute. */
[hidden] {
  display: none !important;
}

/* ---------- Base ---------- */
html {
  -webkit-text-size-adjust: 100%;
}

/* Lock horizontal scroll + side rubber-band (device-only artifacts). Any
 * element wider than the viewport (e.g. the absolutely-positioned ambient
 * glows) is clipped here rather than pushing the page sideways.
 * MUST be `clip`, not `hidden`: overflow-x hidden computes overflow-y to auto,
 * silently turning body into a scroll container — position:sticky then sticks
 * to body's (never-moving) scrollport instead of the real document scroller,
 * which is why the sticky top bars never pinned on device. `clip` clips the
 * same sideways overflow WITHOUT creating a scroll container. (`hidden` kept
 * as the fallback line for browsers without clip support.) */
html,
body {
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior-x: none;
}

body {
  font-family: -apple-system, system-ui, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.46;
  text-wrap: pretty;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  /* Smooth color transitions on theme change (<=200ms) */
  transition: background-color 200ms ease, color 200ms ease;
}

@media (max-width: 380px) {
  body {
    font-size: 15px;
  }
}

/* Tappable controls: suppress iOS double-tap-to-zoom and the tap delay on the
 * controls themselves (play/skip, task checks, switches, pills, menu items —
 * anywhere rapid repeated taps are expected). `manipulation` still allows
 * panning AND pinch-zoom, so this never disables zoom as an accessibility
 * feature — it only stops a quick second tap from being eaten by the zoom
 * gesture recognizer. The scrubber keeps its stricter touch-action: none. */
button,
[role="button"],
[role="checkbox"],
[role="switch"],
[role="slider"],
[role="menuitem"],
.pill,
.memo-card,
.transcript-row,
.legal-row {
  touch-action: manipulation;
}

/* ---------- Typography utility ---------- */
/* .display — Saira Condensed. Use for timers / big numbers ONLY.
 * NOTE (v111): Saira Condensed has NO tabular figures — the tabular-nums
 * below is a measured no-op in this face (kept for the fallback fonts).
 * COUNTING numbers in .display must use digitSpans() (ui.js) + .dgt slots
 * instead, or they reflow sideways as digits change. */
.display {
  font-family: "Saira Condensed", -apple-system, system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
}

/* Fixed-width digit slot for counting numbers in the .display face (see
 * note above): 0.49em = Saira Condensed's widest digit ("8"), so every
 * digit fits and an in-place update never shifts its neighbors. Colons and
 * other separators keep natural width. Counting numbers ONLY — tabular
 * spacing reads worse for static text. */
.dgt {
  display: inline-block;
  width: 0.49em;
  text-align: center;
}

/* ---------- Mount point ---------- */
#app {
  display: block;
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  /* iOS safe areas */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
    env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* ---------- Shared primitives ---------- */
a {
  color: var(--pink-text);
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-list);
  box-shadow: var(--shadow-card);
  transition: background-color 200ms ease, border-color 200ms ease;
}

/* Placeholder view styling for scaffold verification */
.view {
  padding: 24px 18px;
}

.view h1 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.view pre {
  margin-top: 12px;
  padding: 12px 14px;
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: var(--r-list);
  color: var(--text-secondary);
  font-size: 13px;
  overflow-x: auto;
}

/* ============================================================
 * Chips (shared) — pill labels used on the first-run state
 * ============================================================ */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 13px;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 500;
  background: var(--card);
  color: var(--text-secondary);
  border: 1px solid var(--line);
}

.chip svg {
  width: 13px;
  height: 13px;
}

.chip--pink {
  background: var(--pink-soft);
  color: var(--pink-text);
  border-color: transparent;
}

/* ============================================================
 * Home — first-run / empty state (#/home)
 * ============================================================ */
.home {
  position: relative; /* anchors the .ptr indicator, same as .memos/.tasks */
  display: flex;
  flex-direction: column;
  /* NO FORCED SCROLL: #app adds the safe-area insets as padding, so a plain
   * 100dvh here guaranteed ~(top+bottom inset) of dead scroll on device —
   * the empty state bounced with nothing below the fold (same mechanism the
   * v105 Schedule fix documents; same clamp family as .sched/.rec). The
   * pull-to-refresh gesture doesn't need the overflow: it's transform-driven
   * and preventDefaults the top rubber-band. */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  padding: 22px 20px 28px;
}

.home__center {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Optical balance: nudge the cluster down so the empty space reads even
   * top/bottom (offsets the title above + the orb halo's upward overhang). */
  padding-top: 26px;
}

/* --- Mic orb (the record trigger) --- */
.orb {
  position: relative;
  width: 96px;
  height: 96px;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.orb__halo {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--pink-soft);
  pointer-events: none;
}

.orb__halo--outer {
  width: 140px;
  height: 140px;
  opacity: 0.45;
  z-index: 0;
}

.orb__halo--mid {
  width: 112px;
  height: 112px;
  opacity: 0.75;
  z-index: 1;
}

.orb__core {
  position: relative;
  z-index: 2;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: var(--pink);
  box-shadow: var(--shadow-pink);
  display: grid;
  place-items: center;
}

.orb__core svg {
  width: 34px;
  height: 34px;
  color: #fff;
}

.home__hint {
  /* Clears the orb halo's ~22px overhang so the hint has breathing room
   * while staying grouped with the orb. */
  margin-top: 32px;
  font-size: 13px;
  color: var(--text-tertiary);
}

.home__h1 {
  margin: 26px auto 12px;
  max-width: 240px;
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.4px;
  color: var(--text);
}

.home__sub {
  margin: 0 auto 20px;
  max-width: 270px;
  font-size: 15.5px;
  line-height: 1.46;
  color: var(--text-secondary);
}

.home__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 300px;
}

/* --- Orb → Recording transition (<=250ms move + crossfade) --- */
.orb--travel {
  transition: transform 250ms ease;
  z-index: 50;
}

.home--leaving .wordmark-row,
.home--leaving .home__hint,
.home--leaving .home__h1,
.home--leaving .home__sub,
.home--leaving .home__chips {
  transition: opacity 200ms ease;
  opacity: 0;
}

.home__overlay {
  position: fixed;
  inset: 0;
  background: var(--bg); /* the CURRENT theme's bg — the Recording screen it fades into follows the theme too (v107; was element-forced dark) */
  opacity: 0;
  transition: opacity 250ms ease;
  z-index: 40;
  pointer-events: none;
}

.home__overlay.is-on {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .orb--travel,
  .home__overlay {
    transition: none;
  }
}

/* ============================================================
 * Recording — immersive capture mode (#/recording). Theme-following
 * since v107 (was always-dark); every color is a token, plus one
 * per-theme shadow split on the Finish button below.
 * ============================================================ */
.rec {
  position: relative;
  /* NO SCROLL: clamp to the viewport minus #app's safe-area padding (with
   * min-height:100dvh the section exceeded the padded viewport and the body
   * scrolled). Interior regions compress instead. #app already provides the
   * safe-area bottom inset, so the 36px is pure breathing room for the
   * controls row + "Finish" label. */
  height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  padding: 18px 20px 36px;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}

/* Clean top boundary where the ambient glow meets the screen edge — the same
 * hairline language as the other screens' menus, not a hard black-to-pink jump. */
.rec::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
  z-index: 2;
}

.rec__top {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* Left cell — holds the wordmark. Equal flex with the right cell keeps the
 * centered RECORDING pill truly centered on any width. */
.rec__brand {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
}

.rec__pill {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.3px;
}

.rec__pill .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pink);
  /* Soft ring ~3px beyond the dot (a shadow spread — no extra element). It
   * BREATHES on a calm 2s cycle while recording; the dot itself stays solid.
   * This replaces the old broad header glow, which was tuned for dark and
   * smudged on light. Static under prefers-reduced-motion (animation: none
   * below leaves this base ring showing). */
  box-shadow: 0 0 0 3px var(--pink-line);
  animation: recRing 2s ease-in-out infinite;
}

/* Right-side balancer (equal flex with the left wordmark) so the centered
 * RECORDING pill stays centered now that the people-count is gone. */
.rec__topspacer {
  flex: 1 1 0;
}

.rec__center {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0; /* may compress (no-scroll screen) — never pushes controls off */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Top-stacked (v107, was centered): the transcript REGION below absorbs the
   * remaining height, so timer/waveform/limit-pill hold still when live
   * transcription eventually fills that region. */
  justify-content: flex-start;
  text-align: center;
}

/* Centered friendly states (mic denied / saving / error) keep the old
 * composition. */
.rec__center--mid {
  justify-content: center;
}

.rec__mtitle {
  margin-top: 3vh; /* breathing room now the column is top-stacked */
  font-size: 19px;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}

.rec__msub {
  margin-top: 4px;
  font-size: 13.5px;
  color: var(--text-tertiary);
}

.rec__timer {
  margin: 12px 0 6px;
  /* 36px (v119 — was 92): the meter is the screen's hero now; the timer
   * stays the largest TEXT and fully legible. The .dgt digit slots are
   * em-based (0.49em), so they scale with this size and the timer still
   * never jitters as digits change. */
  font-size: 36px;
  font-weight: 600;
  line-height: 1.04;
  color: var(--text);
}

/* Live waveform — pink bars, animated via transform: scaleY */
/* Baseline-and-reflection meter (v118; enlarged v119 — the meter is the
 * screen's hero, fed by the space the 92px timer gave up): main bars grow
 * UP from a thin always-visible hairline; a shorter (≈65% height) and
 * dimmer (0.32 opacity) copy grows DOWN below it. The asymmetry is the
 * point — an equal mirror reads as a symmetric shape; this reads as a
 * reflection on a surface. Footprint: 64 + 1 + 42 = 107px. */
.rec__wave {
  height: 107px;
  margin: 8px 0 24px;
}

.rec__wave-main,
.rec__wave-refl {
  display: flex;
  gap: 5px; /* widened from 4px to suit the taller bars (count/width kept) */
}

.rec__wave-main {
  align-items: flex-end; /* bars sit ON the baseline and grow upward */
  height: 64px;
}

.rec__wave-refl {
  align-items: flex-start; /* reflection hangs FROM the baseline, downward */
  height: 42px; /* ≈65% of the main bars — same scaleY, shorter element */
  opacity: 0.32;
}

/* The baseline: a 1px NEUTRAL hairline (v119 — the v118 2px pink line at
 * 0.34 competed with the pink bars it separates). --line is the app's
 * standard hairline and suits both themes here: visible but recessed, a
 * reference line rather than an element. Extends 6px past the bars on each
 * side so it reads as a surface, not an underline. Always visible,
 * including before the first sample and in silence; dims with the whole
 * component in the paused/interrupted states. */
.rec__wave-line {
  width: calc(100% + 12px);
  height: 1px;
  margin-left: -6px;
  /* Light theme: --line (7% black) was too faint on device (v120) — the
   * charcoal --text-secondary (#6B6B73) is clearly visible and, at 1px,
   * still recessed behind the pink bars. Dark keeps --line (9% white),
   * device-confirmed. */
  background: var(--text-secondary);
}

[data-theme="dark"] .rec__wave-line {
  background: var(--line);
}

.rec__wave span {
  display: block;
  width: 4px;
  border-radius: 2px;
  background: var(--pink);
  /* Flat inactive minimum — the honest default (silence, paused,
   * interrupted, metering unavailable). JS scales every bar in place with
   * the current REAL mic level ~33×/s through a fixed center-peaked shape;
   * both rows receive the identical transform (the reflection's shorter
   * height does the 65%). NO smoothing since v117 and NO CSS transition
   * (v111 — a transition fought the attack and blunted every peak). No
   * self-running animation (v108 — the old keyframes were decorative). */
  transform: scaleY(0.08);
}

.rec__wave-main span {
  height: 64px;
  transform-origin: bottom;
}

.rec__wave-refl span {
  height: 42px;
  transform-origin: top;
}

/* Flexible region (v107): absorbs all remaining height above the transport
 * row. Live transcription — the feature this region was reserved for — was
 * CUT from the roadmap (v145). It now holds the STATUS card at its top
 * (listening / interruption explanations) and the v144 mic guidance at its
 * bottom, still without moving the timer, waveform, or limit pill. */
.rec__tregion {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
}

/* --- Time-limit pill (v107) — centered above the transcript region --- */

/* The wrap IS the pill surface; it holds the main (open-sheet) button and,
 * when set, the one-tap × clear. Unset: quiet card pill. Set: the language-
 * picker selected treatment (--pink-soft / --pink-text). */
.rec__limitwrap {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  margin-bottom: 18px;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--text-secondary);
}

.rec__limitwrap.is-set {
  background: var(--pink-soft);
  border-color: transparent;
  color: var(--pink-text);
}

.rec__limit {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 14px;
  border: 0;
  background: none;
  border-radius: var(--r-pill);
  color: inherit;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.rec__limit svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
}

/* One-tap remove. Padding-left trimmed so the × reads as part of the pill;
 * its own 36px column keeps the tap target honest. */
.rec__limitwrap.is-set .rec__limit {
  padding-right: 4px;
}

.rec__limitclear {
  display: grid;
  place-items: center;
  width: 32px;
  height: 36px;
  padding: 0 6px 0 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.rec__limitclear svg {
  width: 13px;
  height: 13px;
}

/* Picker-sheet caption under the title (the sheet itself reuses the
 * langsheet list/opt/check treatment wholesale). */
.limitsheet__cap {
  margin: 10px 4px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* An option at or below what's already recorded would stop instantly —
 * rendered but not choosable. (The language picker never disables options,
 * so this rule is limit-sheet-only in practice.) */
.langsheet__opt:disabled {
  opacity: 0.4;
  cursor: default;
}

.rec__transcript {
  width: 100%;
  max-width: 360px;
  padding: 14px 16px;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-list);
  box-shadow: var(--shadow-card);
}

.rec__eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--pink-text);
}

.rec__line {
  margin-top: 7px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.rec__line strong {
  color: var(--text);
  font-weight: 700;
}

/* --- Mic guidance (v144): bottom of the flexible transcript region — below
 * the limit pill, above the transport row. The permanent one-liner renders
 * every session; the one-time tip card (first recording) stacks above it.
 * Static elements only: nothing here animates, so reduced motion needs no
 * special case, and every color is a theme token. --- */
/* The placeholder card yields while the one-time tip is up (measured: all
 * three together clip the region on a 14 Pro and the one-liner loses).
 * Hidden, never removed — live-transcription scaffolding stays intact. */
.rec__transcript.is-tip-hidden {
  display: none;
}

.rec__guide {
  flex: 0 0 auto;
  margin-top: auto; /* pin to the region's bottom, just above the controls */
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding-top: 12px;
}

.rec__micline {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  text-align: center;
  color: var(--text-tertiary);
}

.rec__tip {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  background: var(--pink-soft);
  border: 1px solid var(--pink-line);
  border-radius: var(--r-summary);
  text-align: left;
}

.rec__tip > svg {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  color: var(--pink-text);
}

.rec__tipbody {
  min-width: 0;
}

.rec__tiptitle {
  margin: 0;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
}

.rec__tiptext {
  margin: 4px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.rec__tipgotit {
  margin: 6px 0 0;
  padding: 2px 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--pink-text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Controls row */
.rec__controls {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  margin-top: 18px;
}

.rec__ctrl {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* Invisible stand-in with the ghost buttons' exact footprint — keeps the
 * three-slot transport row (and with it the centered Finish button) intact
 * where a control was removed (v120: the dead flag button). */
.rec__ghostspacer {
  display: block;
  width: 58px;
  height: 58px;
}

.rec__ghost {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: var(--card); /* card + hairline like the app's other circular buttons (v107, was transparent-on-dark) */
  border: 1px solid var(--line);
  color: var(--text);
  display: grid;
  place-items: center;
  cursor: pointer;
}

.rec__ghost svg {
  width: 22px;
  height: 22px;
}

/* Pink Finish button — fully circular, consistent with the tab-bar record
 * button. LIGHT gets the full --shadow-pink lift (a pink circle on a light
 * page needs it); DARK keeps the original tight halo — the big bloom reads
 * as a smear on near-black. */
.rec__finish {
  width: 78px;
  height: 78px;
  border: 0;
  border-radius: 50%;
  background: var(--pink);
  box-shadow: var(--shadow-pink);
  display: grid;
  place-items: center;
  cursor: pointer;
}

[data-theme="dark"] .rec__finish {
  box-shadow: 0 2px 10px rgba(255, 46, 116, 0.45);
}

.rec__finish .stop {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background: #fff;
}

.rec__label {
  font-size: 13px;
  color: var(--text-secondary);
}

@keyframes recRing {
  0%,
  100% {
    box-shadow: 0 0 0 3px rgba(255, 46, 116, 0.14);
  }
  50% {
    box-shadow: 0 0 0 3px rgba(255, 46, 116, 0.42);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* JS skips level sampling entirely here — calm static bars, static ring. */
  .rec__wave span {
    transform: scaleY(0.35);
  }
  .rec__pill .dot {
    animation: none;
  }
}

/* ============================================================
 * SHARED COMPONENTS
 * Reused across home / summary / transcript / settings.
 * ============================================================ */

/* --- Avatars (colorIdx → --avatar-palette, set inline by ui.js) --- */
.avatar {
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.2px;
  flex: 0 0 auto;
  flex-shrink: 0; /* never let a flex row squish the circle into an oval */
}

.avatar--ring {
  box-shadow: 0 0 0 2px var(--card);
}

/* "+N" overflow chip — neutral, not a palette color */
.avatar--more {
  background: var(--card-2);
  color: var(--text-secondary);
}

.avatar-stack {
  display: inline-flex;
  align-items: center;
}

.avatar-stack > .avatar {
  margin-left: -8px;
}

.avatar-stack > .avatar:first-child {
  margin-left: 0;
}

/* --- Pink eyebrow (TL;DR, The gist labels) --- */
.eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--pink-text);
}

/* --- Chip variants (base .chip lives in the first-run section) --- */
.chip--count {
  white-space: nowrap; /* never break "3 actions" across lines */
  font-weight: 700;
}

/* --- Top bar + pills (summary / transcript / settings) --- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 4px;
  margin-bottom: 14px;
}

.topbar__right {
  display: flex;
  gap: 8px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 38px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.pill svg {
  width: 17px;
  height: 17px;
}

.pill.is-on {
  background: var(--pink-soft);
  border-color: transparent;
  color: var(--pink-text);
}

/* --- Round icon button (settings gear on the memo list) --- */
.iconbtn {
  display: inline-grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
}

.iconbtn svg {
  width: 20px;
  height: 20px;
}

/* --- Due chip (action items) --- */
.due-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}

.due-chip svg {
  width: 13px;
  height: 13px;
}

/* Tabular numerals helper (timestamps) — NOT Saira; that's .display only. */
.tnum {
  font-variant-numeric: tabular-nums;
}

/* ============================================================
 * Memo list (#/home, populated)
 * ============================================================ */
/* ============================================================
 * App wordmark "/reScribe" — shared top-left brand slot
 * (Memos, Settings, Recording). See ui.js wordmark().
 * ============================================================ */
.wordmark {
  display: inline-flex;
  align-items: baseline;
  font-size: 22.5px;
  font-weight: 700;
  letter-spacing: -0.3px;
  line-height: 1;
  text-decoration: none;
}

.wordmark__slash {
  color: var(--text-tertiary);
}

.wordmark__name {
  color: var(--pink-text);
}

/* Consistent top-left slot so the wordmark sits identically across screens. */
.wordmark-row {
  display: flex;
  align-items: center;
  min-height: 38px;
  margin-bottom: 12px;
}

/* Page-locator title, right-aligned opposite the wordmark on the four
 * top-level tab pages — and opposite the back pill on the legal pages
 * (.topbar__title shares the exact treatment; same quiet locator, different
 * left neighbor). Quiet by design (15.5px/700, --text-secondary) so the
 * pink wordmark stays the loudest thing in the row; a span never exceeds the
 * row's 38px strut, so header height is identical on every tab. */
.wordmark-row__title,
.topbar__title {
  margin-left: auto;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.2px;
  color: var(--text-secondary);
}

/* Zero-width strut reserving the import button's 38px height in EVERY
 * wordmark row. Settings (and the Memos loading/error states) have no 38px
 * control, and on the sticky bars the border-box min-height above is already
 * consumed by the safe-area padding — without the strut those rows collapse
 * to wordmark height (~23px) and the bar height jumps between screens. */
.wordmark-row::before {
  content: "";
  width: 0;
  height: 38px;
}

/* Sticky headers (Memos list + memo detail top bar): full-bleed boxes that
 * extend up over the body's safe-area padding (negative top margin, matching
 * top padding) so the stuck bar covers the notch/status-bar region and content
 * scrolls under it — never over it. Translucent blur + hairline for
 * separation; solid --bg fallback where color-mix is unavailable. */
.memos .wordmark-row,
.settings .wordmark-row,
.tasks .wordmark-row,
.schedule .wordmark-row,
.summary .topbar,
.transcript .topbar,
.ask .topbar,
.legal .topbar,
.paste .topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  margin-left: -20px;
  margin-right: -20px;
  padding-left: 20px;
  padding-right: 20px;
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
/* Top offsets preserve each screen's at-rest content position exactly
 * (body safe-area padding + the view's 14px top padding [+ the topbar's
 * original 4px inner padding]). */
.memos .wordmark-row,
.settings .wordmark-row,
.tasks .wordmark-row,
.schedule .wordmark-row {
  margin-top: calc(-1 * (env(safe-area-inset-top, 0px) + 14px));
  padding-top: calc(env(safe-area-inset-top, 0px) + 14px);
  padding-bottom: 10px;
}
.summary .topbar,
.transcript .topbar,
.ask .topbar,
.legal .topbar,
.paste .topbar {
  margin-top: calc(-1 * (env(safe-area-inset-top, 0px) + 14px));
  padding-top: calc(env(safe-area-inset-top, 0px) + 18px);
  padding-bottom: 10px;
}

/* (The header import button moved into the dashboard as .dash__import when
 * the page-locator title took the header's right slot.) */

.memos {
  position: relative;
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  /* Top/left match .settings so the shared wordmark sits identically. */
  padding: 14px 20px 40px;
}

/* Pull-to-refresh indicator (Chrome-style): a pink circular-arrow glyph in a
 * soft round disc with a small "Reload" label below, hanging ABOVE the
 * section's top edge and revealed as the whole section translates down with
 * the pull (JS drives the section transform + disc opacity/rotation); the disc
 * spins in the revealed gap while the list re-fetches. top centers the ~58px
 * disc+label stack in the 64px PTR_HOLD gap. */
.ptr {
  position: absolute;
  top: -61px;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  pointer-events: none;
  opacity: 0;
  z-index: 30;
}
.ptr__disc {
  display: grid;
  place-items: center;
  width: 41px;
  height: 41px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
  color: var(--pink);
}
.ptr__disc svg {
  width: 23px;
  height: 23px;
}
.ptr__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2px;
  line-height: 1;
  color: var(--pink-text);
}
.ptr.is-refreshing .ptr__disc {
  animation: mp-spin 0.8s linear infinite;
}

.search {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 42px;
  margin-top: 16px;
  padding: 0 13px;
  border-radius: 7px;
  background: var(--card);
  border: 1px solid var(--line);
}

.search__icon {
  display: grid;
  place-items: center;
  color: var(--text-tertiary);
}

.search__icon svg {
  width: 18px;
  height: 18px;
}

.search__input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 15px;
  font-family: inherit;
  outline: none;
}

.search__input::placeholder {
  color: var(--text-tertiary);
}

.memo-section {
  margin: 24px 0 10px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.memo-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.memo-empty {
  margin-top: 28px;
  text-align: center;
  font-size: 15px;
  color: var(--text-secondary);
}

/* --- Loading / error / notice states (Firestore-backed views) --- */
.memo-loading {
  margin-top: 28px;
  text-align: center;
  font-size: 15px;
  color: var(--text-tertiary);
}

.memo-error {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
}

.memo-error__text {
  font-size: 15px;
  color: var(--text-secondary);
  max-width: 320px;
}

.memo-retry {
  height: 42px;
  padding: 0 22px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

/* Inline save-failure notice on the summary view (hidden until shown). */
.summary__notice {
  display: none;
}

.summary__notice.is-on {
  display: block;
  margin-bottom: 10px;
  padding: 10px 14px;
  border-radius: var(--r-list);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 13.5px;
  font-weight: 600;
  text-align: center;
}

/* --- Overflow (⋯) menu (summary topbar) --- */
.sum-more {
  position: relative;
  display: inline-flex;
}

.sum-more__btn {
  width: 38px;
  height: 38px;
}

.sum-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  min-width: 170px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: 7px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.18);
}

/* Reprocess with no audio left: visibly inert (tap explains why instead of
 * failing) — same row, muted. Double class outranks the base rule below. */
.sum-menu__item.sum-menu__item--disabled {
  color: var(--text-tertiary);
  cursor: default;
}

.sum-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.sum-menu__item svg {
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
}

.sum-menu__item:hover {
  background: var(--line);
}

.sum-menu__item--danger,
.sum-menu__item--danger svg {
  color: var(--red-text);
}

/* --- Editable title (rename) --- */
.summary__title-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  text-align: left;
  cursor: text;
}

.summary__title-pencil {
  display: inline-flex;
  flex: none;
  color: var(--text-tertiary);
}

.summary__title-pencil svg {
  width: 18px;
  height: 18px;
}

.summary__title-btn:hover .summary__title-pencil {
  color: var(--text-secondary);
}

/* --- Confirm dialog (destructive actions) --- */
.confirm {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
}

.confirm__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.confirm__box {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 340px;
  padding: 22px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
}

.confirm__title {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

.confirm__body {
  margin-top: 8px;
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.confirm__error {
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: var(--r-list);
  background: var(--red-soft);
  color: var(--red-text);
  font-size: 13px;
  font-weight: 600;
}

.confirm__actions {
  margin-top: 18px;
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

.confirm__btn {
  height: 42px;
  padding: 0 18px;
  border: 0;
  border-radius: var(--r-pill);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.confirm__btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.confirm__btn--cancel {
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
}

.confirm__btn--danger {
  background: var(--red);
  color: #fff;
}

/* ============================================================
 * Share-as-PDF section picker (bottom sheet — see js/share.js)
 * ============================================================ */

.sharesheet {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.sharesheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.sharesheet__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 520px;
  padding: 20px 20px calc(16px + env(safe-area-inset-bottom, 0px));
  background: var(--card);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-memo) var(--r-memo) 0 0;
  box-shadow: 0 -20px 50px rgba(0, 0, 0, 0.35);
  animation: sharesheet-up 240ms cubic-bezier(0.2, 0.9, 0.3, 1);
}

@keyframes sharesheet-up {
  from {
    transform: translateY(28px);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sharesheet__panel {
    animation: none;
  }
}

.sharesheet__title {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

.sharesheet__sub {
  margin-top: 3px;
  font-size: 13.5px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sharesheet__rows {
  margin-top: 14px;
  display: grid;
  gap: 8px;
}

.sharerow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--r-list);
  background: var(--card-2);
  border: 1px solid var(--line);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The real checkbox is invisible but present (a11y / native toggling); the
 * .sharerow__box paints its state. */
.sharerow__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.sharerow__box {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  border: 1.5px solid var(--text-tertiary);
  color: transparent;
}

.sharerow__box svg {
  width: 14px;
  height: 14px;
}

.sharerow__input:checked ~ .sharerow__box {
  background: var(--pink);
  border-color: var(--pink);
  color: #fff;
}

.sharerow__label {
  flex: 1;
  font-size: 15px;
  font-weight: 600;
}

.sharerow__hint {
  font-size: 12px;
  color: var(--text-tertiary);
}

.sharesheet__error {
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: var(--r-list);
  background: var(--red-soft);
  color: var(--red-text);
  font-size: 13px;
  font-weight: 600;
}

.sharesheet__actions {
  margin-top: 16px;
  display: flex;
  gap: 10px;
}

.sharesheet__btn {
  flex: 1;
  height: 46px;
  border: 0;
  border-radius: var(--r-pill);
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
}

.sharesheet__btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.sharesheet__btn--cancel {
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
}

.sharesheet__btn--primary {
  background: var(--pink);
  color: #fff;
}

/* Copy-as-Markdown: a full-width secondary output above the Cancel/Create
 * row (shares the cancel button treatment). */
.sharesheet__btn--md {
  width: 100%;
  margin-top: 16px;
}

.sharesheet__btn--md + .sharesheet__actions {
  margin-top: 10px;
}

/* Save-audio rides the --md treatment: Copy-as-Markdown and Save-audio are
 * the two "take this away with me" actions and read as a tight pair (10px
 * apart), while the terminal Cancel/Create row keeps a fuller 16px gap below
 * them so it stays its own zone — no hairline needed. */
.sharesheet__btn--md + .sharesheet__btn--md {
  margin-top: 10px;
}

.sharesheet__btn--audio + .sharesheet__actions {
  margin-top: 16px;
}

/* The audio action makes the share picker taller than the shell's other
 * riders; scoped scroll guard (added by share.js only) so a six-section memo
 * still fits the shortest screens. */
.sharesheet__panel--scroll {
  max-height: calc(100vh - 24px);
  overflow-y: auto;
}

/* --- Settings language picker (rides the .sharesheet shell) --- */

.langsheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.langsheet__close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--card-2);
  color: var(--text-secondary);
  cursor: pointer;
}

.langsheet__close svg {
  width: 16px;
  height: 16px;
}

.langsheet__list {
  margin-top: 14px;
  max-height: min(48vh, 420px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: grid;
  gap: 4px;
}

.langsheet__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border: 0;
  border-radius: var(--r-list);
  background: none;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.langsheet__opt.is-selected {
  background: var(--pink-soft);
  color: var(--pink-text);
}

.langsheet__check {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--pink);
}

.langsheet__check svg {
  width: 100%;
  height: 100%;
}

/* --- Transcript "Who said this?" speaker picker (rides the same shell +
 * option treatment as the language picker — one selected style) --- */

.spksheet__who {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.spksheet__titles {
  min-width: 0;
}

.spksheet__sub {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--text-secondary);
}

/* "Add a speaker" row: dashed pink circle + pink label, same option row as
 * the speakers above it. */
.spksheet__addcircle {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1.5px dashed var(--pink-text);
  background: transparent;
  color: var(--pink-text);
}

.spksheet__addcircle svg {
  width: 14px;
  height: 14px;
}

.spksheet__addlabel {
  color: var(--pink-text);
  font-weight: 500;
}

.spksheet__hint {
  margin: 12px 4px 0;
  font-size: 12px;
  color: var(--text-tertiary);
}

/* Naming state (state 2). The back control mirrors .langsheet__close. */
.spksheet__back {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--card-2);
  color: var(--text-secondary);
  cursor: pointer;
}

.spksheet__back svg {
  width: 16px;
  height: 16px;
}

.spksheet__form {
  margin-top: 14px;
  display: grid;
  gap: 12px;
}

.spksheet__preview {
  display: flex;
  justify-content: center;
}

.spksheet__help {
  margin: -4px 4px 0;
}

.spksheet__err {
  margin: -4px 4px 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--red-text);
}

.spksheet__actions {
  display: flex;
  gap: 10px;
}

.spksheet__btn {
  flex: 1;
  height: 46px;
  border-radius: var(--r-pill);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.spksheet__btn--cancel {
  border: 1px solid var(--line);
  background: none;
  color: var(--text);
}

.spksheet__btn--add {
  border: 0;
  background: var(--pink);
  color: #fff;
}

/* --- Memo card --- */
.memo-card {
  display: block;
  padding: 16px;
  border-radius: var(--r-memo);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  color: inherit;
  text-decoration: none;
}

/* Dark theme: swap the light shadow for a hairline border. */
[data-theme="dark"] .memo-card {
  box-shadow: none;
  border-color: var(--line);
}

.memo-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.memo-card__title {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.2px;
}

.memo-card__meta {
  margin-top: 2px;
  font-size: 13px;
  color: var(--text-tertiary);
}

/* Condensed scan line (AI subtitle; legacy docs fall back to tldr): up to two
 * lines, then ellipsis at the end of the second. */
.memo-card__sub {
  margin-top: 10px;
  font-size: 14.5px;
  line-height: 1.4;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.memo-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
}

/* Task-progress on a memo card: small track + d/t label. Pink while partial,
 * green once every action item is done. Never shown on unread cards. */
.memo-progress {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.memo-progress__track {
  display: inline-block;
  width: 52px;
  height: 5px;
  border-radius: var(--r-pill);
  background: var(--line);
  overflow: hidden;
}

.memo-progress__fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--pink);
  transition: width 220ms ease;
}

.memo-progress__label {
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

.memo-progress.is-complete .memo-progress__fill {
  background: var(--green);
}

.memo-progress.is-complete .memo-progress__label {
  color: var(--green);
}

/* ============================================================
 * Home dashboard — editorial header above the memo list.
 * Calm and flat: no widget boxes, no card-in-card; hairline rules
 * separate the zones. Hidden entirely while a search/filter is active.
 * ============================================================ */

.memos.is-filtering .dash {
  display: none;
}

.dash__date {
  margin: 4px 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-tertiary);
}

.dash__headline {
  margin: 0 0 8px;
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.7px;
  line-height: 1.18;
  color: var(--text);
}

.dash__num {
  color: var(--pink);
}

.dash__sub {
  margin: 0 0 16px;
  font-size: 13.5px;
  color: var(--text-tertiary);
}

/* Two side-by-side actions in the old single-button slot: the split
 * Import|Paste card (secondary — card surface, hairline, pink icon/label:
 * clearly tappable but it never competes with the primary) and Schedule
 * (primary pink, label shortened from "Schedule a meeting" to fit
 * half-width on a 320pt SE). */
.dash__actions {
  display: flex;
  gap: 10px;
}

/* The split Import|Paste card (paste-to-memo entry): TWO half-width buttons
 * in the EXACT card footprint the old full-width Import button had. The row
 * is 72px tall (Schedule grows to match, purely visual symmetry) so each
 * half clears Apple's 44pt tap minimum with room to spare — the 52px row's
 * top/bottom halves measured 24.5/25.5pt, which is why the earlier vertical
 * split was stopped. */
.dash__split,
.dash__schedule {
  flex: 1 1 0;
  min-width: 0;
  height: 72px;
  border-radius: 12px;
}

.dash__split {
  display: flex;
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: var(--shadow-card);
  overflow: hidden; /* the halves' square corners clip to the card radius */
}

/* Each half is its own full-height button: two independent hit areas that
 * TILE the card completely — no dead zone between them, and the hairline is
 * the right half's border-left, so it lives inside that half's hit area
 * rather than between two targets. */
.dash__split-half {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--pink-text);
  font-family: inherit;
  white-space: nowrap;
  cursor: pointer;
}

.dash__split-half + .dash__split-half {
  border-left: 1px solid var(--line);
}

.dash__split-half:disabled {
  opacity: 0.55; /* the old .dash__import:disabled treatment, per half */
}

.dash__split-half svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-bottom: 2px;
}

.dash__split-label {
  font-size: 13.5px;
  font-weight: 700;
}

/* The quiet second line — the same explicit --text-secondary subtitle tier
 * as the Appearance theme subtitles (.segmented__sub, feature/about-page)
 * and the schedule sheet's sub-label; 12.5px here, the card being roomier
 * than a segmented-control segment. Explicit color so it stays quiet under
 * the halves' --pink-text. */
.dash__split-sub {
  font-size: 12.5px;
  color: var(--text-secondary);
}

.dash__schedule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--shadow-card); /* v108 — the pink bloom smudged on light */
}

.dash__schedule svg {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
}

.dash__rule {
  border: none;
  border-top: 1px solid var(--line);
  margin: 20px 0 16px;
}

.dash__eyebrow {
  margin: 0 0 4px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 1.2px;
  color: var(--text-tertiary);
}

.dash__eyebrow--memos {
  margin-bottom: 12px;
}

/* ============================================================
 * Tasks tab (#/tasks) — open action items GROUPED BY MEMO: a title+date
 * header row over a card of task rows per memo, a compact sort pill, and
 * a collapsed COMPLETED section at the bottom.
 * ============================================================ */

.tasks {
  position: relative; /* anchors the .ptr indicator, same as .memos */
  /* NO FORCED SCROLL: insets subtracted for the same reason as .home/.sched —
   * plain 100dvh inside #app's inset padding = dead scroll on the "all caught
   * up" empty state (and any list shorter than the viewport). */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  padding: 14px 20px 40px;
}

/* (The big "Tasks" heading moved into the sticky header as the page-locator
 * title; the subline now leads the content directly under the header.) */
.tasks__count {
  margin: 14px 0 12px;
  font-size: 13.5px;
  color: var(--text-tertiary);
}

.tasks__em {
  font-weight: 700;
  color: var(--pink-text);
}

/* Sort pill (reuses .filter-chip) + tagmenu-style popover beneath it. */
.tasks__sort {
  position: relative;
  display: flex;
  margin: 0 0 18px;
}

/* The chevron (last svg) flips while the popover is open. */
.tasks__sort.is-open .tasks__sortbtn svg:last-child {
  transform: rotate(180deg);
}

.tasks__sortmenu {
  left: 0;
  right: auto;
  width: max-content;
  min-width: 190px;
  max-width: calc(100vw - 40px);
  padding: 8px;
}

.tasks__sortmenu .tagmenu__item {
  justify-content: space-between;
}

.tasks__sortmenu .tagmenu__item.is-sel {
  color: var(--pink-text);
}

.tasks__sortmenu .tagmenu__itemcheck svg {
  width: 16px;
  height: 16px;
  color: var(--pink-text);
}

/* One memo's group: title (wraps) + date header row, then a card of rows. */
.tgroup {
  margin: 0 0 18px;
}

.tgroup__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 2px 8px;
}

.tgroup__title {
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.35;
  text-align: left;
  color: var(--text);
  cursor: pointer;
}

.tgroup__date {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--text-tertiary);
  white-space: nowrap;
}

.tgroup__card {
  padding: 0 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
}

.trow {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.trow:last-child {
  border-bottom: none;
}

.trow__check {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  padding: 0;
  border-radius: 6px;
  border: 2px solid var(--pink-line);
  background: transparent;
  color: transparent;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}

.trow__check svg {
  width: 14px;
  height: 14px;
}

.trow__check.is-done {
  border-color: var(--green);
  color: var(--green);
}

.trow__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.trow__text {
  font-size: 14px;
  line-height: 1.4;
  color: var(--text);
  transition: color 160ms ease;
}

.trow.is-done .trow__text {
  color: var(--text-tertiary);
  text-decoration: line-through;
}

.trow__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.trow__owner {
  font-size: 12px;
  color: var(--text-tertiary);
}

/* COMPLETED section: eyebrow + chevron toggle; collapsed keeps the label in
 * the ACTIVE pink state (house rule — collapsed never reads as grayed out). */
.tasks__done {
  margin-top: 22px;
}

.tasks__donetoggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 0 0 10px;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
}

.tasks__donetoggle .dash__eyebrow {
  margin: 0;
}

.tasks__donechev {
  display: inline-flex;
  color: var(--text-tertiary);
  transition: transform 180ms ease, color 180ms ease;
}

.tasks__donechev svg {
  width: 16px;
  height: 16px;
}

.tasks__done.is-collapsed .tasks__donetoggle .dash__eyebrow,
.tasks__done.is-collapsed .tasks__donechev {
  color: var(--pink-text);
}

.tasks__done.is-collapsed .tasks__donechev {
  transform: rotate(-90deg);
}

/* Zero open tasks: honest, minimal all-caught-up state. */
.tasks__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 56px 12px 24px;
}

.tasks__emptyicon {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  margin-bottom: 14px;
  border-radius: 50%;
  background: var(--pink-soft);
  color: var(--pink-text);
}

.tasks__emptyicon svg {
  width: 28px;
  height: 28px;
}

.tasks__emptytext {
  margin: 0;
  font-size: 15px;
  color: var(--text-secondary);
}

/* ============================================================
 * Schedule tab (#/schedule) — scheduled meeting reminders (launch scope:
 * local notification at the lead time; tap → Recording with the title
 * pre-filled). One chronological date-column card (dimmed past, washed
 * today, upcoming) + a floating add button + the create/edit bottom sheet.
 * ============================================================ */

.schedule {
  /* NO FORCED SCROLL: #app wraps every view in safe-area padding, so a plain
   * 100dvh here guaranteed ~(top+bottom inset) of dead scroll on device even
   * with two meetings (border-box means the 98px floating-button padding was
   * NOT the culprit — it only matters once content is long). Subtracting the
   * insets makes the document exactly viewport-height when content fits: no
   * scroll, no bounce (same clamp family as .rec). */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  padding: 14px 20px 40px;
}

/* Quiet empty state (reuses the tile treatment the placeholder had). */
.schedule__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 72px 16px 28px;
}

.schedule__tile {
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  margin-bottom: 18px;
  border-radius: 50%;
  background: var(--pink-soft);
  color: var(--pink-text);
}

.schedule__tile svg {
  width: 34px;
  height: 34px;
}

.schedule__line {
  max-width: 280px;
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Standing note when notifications are denied but meetings are scheduled. */
.sched__perm {
  margin: 0 4px 14px;
}

/* --- Date-column list (v104) --- */

/* ONE card holds every day: a row per day, date on the left, that day's
 * meetings stacked on the right. Time runs downward — dimmed past days
 * above, TODAY (the wash + scroll anchor), upcoming below. overflow:hidden
 * clips the today row's full-bleed wash to the rounded corners. */
.sched-cal {
  display: flex;
  flex-direction: column;
  gap: 15px;
  margin-top: 4px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  background: var(--card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

/* Month eyebrow — rendered before the first day row and at every month
 * change, since "Mon 3" alone is ambiguous across months. */
.sched-cal__month {
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* A month wholly before the current one dims like its rows. */
.sched-cal__month.is-past {
  opacity: 0.45;
}

.sched-cal__row {
  display: flex;
  align-items: stretch;
}

/* Past days dim AS A UNIT — one opacity on the row, plus quieter date inks. */
.sched-cal__row.is-past {
  opacity: 0.45;
}

/* Today: --pink-wash across the card's full width. Negative horizontal
 * margins bleed exactly through the card's 14px padding (never past the
 * border box, so no horizontal scroll); the vertical pair keeps the 15px
 * content rhythm while the wash gets 8px of breathing room. */
.sched-cal__row.is-today {
  margin: -8px -14px;
  padding: 8px 14px;
  background: var(--pink-wash);
}

.sched-cal__date {
  flex: 0 0 38px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding-top: 1px;
}

.sched-cal__dow {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--pink);
}

.sched-cal__row.is-past .sched-cal__dow {
  color: var(--text-tertiary);
}

/* "TODAY" — slightly bolder than the other weekday labels. */
.sched-cal__row.is-today .sched-cal__dow {
  font-weight: 700;
  color: var(--pink-text);
}

/* Day number — Saira Condensed at the app's universal .display weight 600. */
.sched-cal__num {
  font-size: 21px;
  font-weight: 600;
  line-height: 1;
  color: var(--text);
}

.sched-cal__row.is-past .sched-cal__num {
  color: var(--text-secondary);
}

/* Meetings column, split from the date by a vertical hairline (today's stays
 * the plain --line hairline on purpose — the wash is the highlight). */
.sched-cal__items {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* No flex gap (v153) — sibling spacing lives on the margin+padding rule
   * below so the TODAY divider can sit centered inside it. */
  margin-left: 12px;
  padding-left: 12px;
  border-left: 1px solid var(--line);
}

/* v153: meetings within a day sit 30px apart (was a 10px gap — adjacent
 * record buttons on a multi-meeting today were 10px apart, too tight for
 * neighboring tap targets). ONE rule for every day — past, today, future —
 * so spacing never depends on whether a row carries a button. The 15px
 * margin + 15px padding split exists because a border-top draws BETWEEN
 * margin and padding: the today divider below lands exactly centered.
 * (Explicit pair selectors, not `> * + *` — the double class outweighs
 * .sched-cal__item's own `padding: 0` reset further down.) */
.sched-cal__items > .sched-cal__item + .sched-cal__item,
.sched-cal__items > .sched-cal__entry + .sched-cal__entry {
  margin-top: 15px;
  padding-top: 15px;
}

/* Hairline between meetings WITHIN TODAY only — deliberately the app's ONLY
 * horizontal line in this list (no day gets one elsewhere). As a child of
 * the items column it spans that column's content box: it starts 12px right
 * of the vertical rule and never crosses it or the date column. Light does
 * NOT use --line: 7% black over the --pink-soft-adjacent TODAY wash measures
 * 1.16:1 (near-invisible); 14% black measures 1.35:1 — the same read as
 * dark's --line over its wash (1.30:1), so the two themes match. */
.sched-cal__row.is-today .sched-cal__entry + .sched-cal__entry {
  border-top: 0.5px solid rgba(11, 11, 13, 0.14);
}

[data-theme="dark"] .sched-cal__row.is-today .sched-cal__entry + .sched-cal__entry {
  border-top-color: var(--line);
}

.sched-cal__item {
  display: block;
  width: 100%; /* buttons shrink-wrap by default — the nowrap title needs the column's width to ellipsize against */
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.sched-cal__title {
  display: block;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sched-cal__meta {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* Bell before the reminder text (v105) — sits INSIDE the 13px line box
 * (inline, baseline-tucked), so the line never grows taller. currentColor
 * = the meta's --text-secondary; past rows dim it with the row. */
.sched-cal__meta svg {
  width: 13px;
  height: 13px;
  vertical-align: -2px;
}

/* Empty today — quiet; the row exists as the anchor, nothing more. */
.sched-cal__none {
  font-size: 13px;
  color: var(--text-tertiary);
}

/* TODAY rows only: the row button + its record button side by side. The
 * wrapper takes the item's slot in the items column; the item keeps flexing
 * (min-width:0 chain preserved) so the nowrap title still ellipsizes. */
.sched-cal__entry {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.sched-cal__entry .sched-cal__item {
  flex: 1 1 auto;
}

/* On-the-row record button — start this meeting's recording with its title
 * pre-filled. Same glow as the tab bar's record button (that treatment is a
 * one-off box-shadow on .tabbar__rec, not a shared class — the VALUE is
 * reused here). Static shadow, no animation: reduced motion is unaffected. */
.sched-cal__rec {
  flex: 0 0 38px;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--pink);
  color: #fff;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 8px rgba(255, 46, 116, 0.45);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.sched-cal__rec svg {
  width: 18px;
  height: 18px;
}

/* Add-meeting action, FLOATING above the tab bar so it stays reachable
 * however long the history grows. bottom clears the bar (62px + safe area)
 * by 30px: the record button's cradle bump rises 22px above the bar's top
 * edge (.tabbar__rec top: -22px), so the button's bottom edge keeps 8px of
 * daylight above it at every width.
 *
 * OUTLINED since v105 — the solid pink fill stacked too much pink directly
 * above the raised record button; the dashed outline defers to it. The fill
 * is the sticky header's exact translucent-page treatment (--bg at 88% +
 * 14px blur, solid --bg fallback) so list text never reads through.
 *
 * CONTENT-WIDTH PILL since v106 — full width read as a bar, not a control,
 * and its top edge sliced whatever row sat behind it. Sizes to the icon +
 * label, centered, --r-pill radius. The dash is --pink-text rather than
 * --pink-line: on device the 22%-alpha light-theme line all but vanished,
 * and with far less border to draw, each dash can carry a real ink weight
 * (the same ink the label already uses) while a 1.5px dashed hairline stays
 * unmistakably secondary to the solid record button below. */
.sched__add {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 30px);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: max-content;
  padding: 11px 20px;
  border: 1.5px dashed var(--pink-text);
  border-radius: var(--r-pill);
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--pink-text);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 500;
  cursor: pointer;
}

.sched__add svg {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
}

/* Bottom dissolve (v106) — with the pill floating over a scrolling list,
 * rows were sliced mid-height at the viewport bottom with no visual
 * explanation. This fades content into the page background from the tab
 * bar's top edge up. 72px on purpose: the pill's zone (30px offset
 * + ~44px pill), about 1.8 meeting rows — a sliced row dissolves over its
 * full height and the pill always sits on calm ground, while ~8% of the
 * screen stays shallow enough not to hide what's being read. The gradient
 * starts from a TRANSPARENT MIX OF THE PAGE COLOR — not the transparent
 * keyword, whose black component grays the midtones over a light background
 * — so light dissolves cleanly and dark reads as content sinking into true
 * near-black, never a gray smear. The rgba starts are concrete per-theme
 * mirrors of --bg (the tab bar precedent; color-mix would do it tokenized
 * but the iOS 15 target predates it — keep in sync with tokens.css). z 10:
 * over list content, under the Today pill (19), sticky header (20), and the
 * add pill (30). pointer-events: none — scrolling and taps pass straight
 * through. JS shows it only while the list actually overflows; over a short
 * list it would read as a rendering artifact. */
.sched-fade {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  height: 72px;
  z-index: 10;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(244, 244, 245, 0), var(--bg));
  opacity: 0;
  transition: opacity 150ms ease;
}

[data-theme="dark"] .sched-fade,
[data-immersive="dark"] .sched-fade {
  background: linear-gradient(to bottom, rgba(11, 11, 13, 0), var(--bg));
}

.sched-fade.is-on {
  opacity: 1;
}

/* Floating "back to today" pill (v105) — the house .pill, shrunk a notch,
 * fixed and centered just under the sticky header (63px + safe-area tall,
 * so 71px leaves an 8px gap; z 19 slides under the header's 20). Shown ONLY
 * while today's row is off-screen: an IntersectionObserver toggles
 * .is-shown, and visibility:hidden keeps it truly absent (no tap target, no
 * a11y entry) rather than merely transparent. Fade only — no motion. The
 * chevron points where today went: .is-up when the row is above the
 * viewport (user scrolled into the future), down otherwise. */
.sched-topill {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 71px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 19;
  height: 34px;
  padding: 0 13px;
  font-size: 13px;
  color: var(--pink-text);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 150ms ease, visibility 0s linear 150ms;
}

.sched-topill.is-shown {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 150ms ease;
}

.sched-topill svg {
  display: none;
  width: 14px;
  height: 14px;
}

.sched-topill.is-up .sched-topill__up {
  display: block;
}

.sched-topill:not(.is-up) .sched-topill__down {
  display: block;
}

/* --- Create/edit sheet (rides the shared .sharesheet shell) --- */

.schedsheet__form {
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.schedsheet__field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Field label — the same uppercase eyebrow tier as .group-title. */
.schedsheet__label {
  margin-left: 4px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* Text + native date/time inputs share the app's form-field treatment
 * (.auth__input tier: 48px, card-2 fill, hairline, pink-line focus). */
.schedsheet__input {
  box-sizing: border-box;
  height: 48px;
  padding: 0 14px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  outline: none;
  transition: border-color 150ms ease;
}

.schedsheet__input::placeholder {
  color: var(--text-tertiary);
}

.schedsheet__input:focus {
  border-color: var(--pink-line);
}

.schedsheet__when {
  display: flex;
  gap: 10px;
}

/* Native pickers: strip the UA chrome so the field box matches; iOS centers
 * the value text by default — align it left like every other field. The
 * PICKER itself (wheel/calendar) stays the system one on purpose. */
.schedsheet__date,
.schedsheet__time {
  -webkit-appearance: none;
  appearance: none;
}

.schedsheet__date {
  flex: 1.4 1 0;
  min-width: 0;
}

.schedsheet__time {
  flex: 1 1 0;
  min-width: 0;
}

.schedsheet__date::-webkit-date-and-time-value,
.schedsheet__time::-webkit-date-and-time-value {
  text-align: left;
}

/* Lead-time pills — the language picker's selected treatment (--pink-soft
 * fill, --pink-text label) on pill-radius chips. */
.schedsheet__leads {
  display: flex;
  gap: 8px;
}

.schedsheet__lead {
  flex: 1 1 0;
  min-width: 0;
  height: 38px;
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card-2);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.schedsheet__lead.is-selected {
  background: var(--pink-soft);
  border-color: transparent;
  color: var(--pink-text);
}

/* Inline validation — friendly, never a dialog. */
.schedsheet__err {
  margin: -4px 4px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--red-text);
}

/* Hairline between the form fields and the "Start recording now" row (edit
 * sheet only). The form's 16px column gap provides the breathing room. */
.schedsheet__divider {
  height: 0;
  border-top: 0.5px solid var(--line);
}

/* "Start recording now" — the full row is the tap target. Soft-pink leading
 * orb, pink label, quiet sub-label, tertiary trailing chevron. */
.schedsheet__recnow {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: -6px 0;
  padding: 6px 4px;
  border: 0;
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.schedsheet__recnow-orb {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--pink-soft);
  color: var(--pink-text);
  display: grid;
  place-items: center;
}

.schedsheet__recnow-orb svg {
  width: 16px;
  height: 16px;
}

.schedsheet__recnow-text {
  flex: 1 1 auto;
  min-width: 0;
}

.schedsheet__recnow-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--pink-text);
}

.schedsheet__recnow-sub {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  color: var(--text-secondary);
}

.schedsheet__recnow-chev {
  flex: 0 0 auto;
  color: var(--text-tertiary);
  display: grid;
  place-items: center;
}

.schedsheet__recnow-chev svg {
  width: 16px;
  height: 16px;
}

.schedsheet__save {
  width: 100%;
  height: 52px;
  border: none;
  border-radius: 12px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(255, 46, 116, 0.35);
}

/* Quiet PINK remove row (the in-app action; RED is reserved for the confirm
 * dialog's committed step, per the destructive color rule). */
.schedsheet__delete {
  width: 100%;
  margin-top: -4px;
  padding: 10px 0;
  border: 0;
  background: none;
  color: var(--pink-text);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}

/* In-progress (processing/recording) memo card: spinner + subtle pink note. */
.memo-card__spin {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--pink);
}

.memo-card__spin svg {
  width: 22px;
  height: 22px;
  animation: mp-spin 0.8s linear infinite;
}

.memo-card__procnote {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--pink-text);
}

.memo-card__procdot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--pink);
  animation: procPulse 1.2s ease-in-out infinite;
}

@keyframes procPulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}

/* ============================================================
 * Memo cards: swipe-to-delete · "New" (unviewed) · failed + reprocess
 * ============================================================ */

/* Swipe container: a pink delete panel behind + the card as the moving layer. */
.memo-swipe {
  position: relative;
  border-radius: var(--r-memo);
  overflow: hidden;
  /* Transparent at rest: the container's rounded corners coincide exactly with
   * the card's, so an always-on pink backing anti-aliases through as a ~1px
   * pink peek at every corner. JS adds .is-swiping while the card is displaced
   * (and removes it only after the card has settled back), so the pink backing
   * — which keeps the card's exposed corner notches pink instead of the page
   * bg at full swipe — is only painted when it can actually be seen. */
  background: transparent;
}

.memo-swipe.is-swiping,
.memo-swipe.is-open {
  background: var(--pink);
}

.memo-swipe .memo-card {
  position: relative;
  z-index: 1;
  transition: transform 0.2s ease;
  will-change: transform;
}

/* Pink delete panel revealed at the card's right edge when swiped open.
 * Hidden at rest for the same corner-peek reason as the container backing
 * (it hugs the container's right corners under the card). */
.memo-swipe__action {
  visibility: hidden;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 72px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 0;
  border: 0;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
}
.memo-swipe.is-swiping .memo-swipe__action,
.memo-swipe.is-open .memo-swipe__action {
  visibility: visible;
}
.memo-swipe__action svg {
  width: 20px;
  height: 20px;
  transition: transform 130ms ease;
}
.memo-swipe__label { line-height: 1; }

/* Full-swipe armed (dragged past half the card's width): the trash icon
 * bumps — a quiet "release will ask to delete" cue. The panel and backing
 * stay the same solid --pink at every depth. */
.memo-swipe.is-armed .memo-swipe__action svg {
  transform: scale(1.18);
}

/* "New" (unviewed): a 1px solid pink border around the whole card (the New
 * pill stays). Same 1px width + 16px padding as the read card's hairline, so
 * read and unread boxes are IDENTICAL — zero layout shift on mark-read. (The
 * old patchy rendering came from a 0.5px/alpha hairline; a true 1px of
 * opaque --pink rasterizes to whole device pixels on 2x/3x and stays even.) */
.memo-card.is-unviewed {
  border: 1px solid var(--pink);
}
/* Sits top-right in .memo-card__head (the old play-button slot); the margin
 * optically centers it against the title's first line. Read cards render
 * nothing there. */
.memo-new-pill {
  flex: 0 0 auto;
  margin-top: 4px;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* Failed memo: soft-red "Failed" badge (top-right) + friendly line + Reprocess. */
.memo-badge--failed {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  background: var(--red-soft);
  color: var(--red-text);
  font-size: 12px;
  font-weight: 700;
}
.memo-badge--failed svg { width: 14px; height: 14px; }

.memo-card__failnote {
  margin-top: 10px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-secondary);
}
.memo-card__failactions {
  margin-top: 12px;
}
.memo-reprocess {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 38px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.memo-reprocess:disabled { opacity: 0.6; cursor: default; }
.memo-reprocess svg { width: 16px; height: 16px; }

/* Settings SUBSCRIPTION group's Lite→Pro CTA (feature/lite-tier) — the
   memo-reprocess pill treatment on the settings surface. */
.usage-upgrade {
  display: inline-flex;
  align-items: center;
  margin-top: 10px;
  height: 38px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

/* ============================================================
 * Inline editing (tap-to-edit AI text; see js/inline-edit.js)
 * ============================================================ */

/* Resting editable text: geometry-untouched (the editor box compensates for
 * its own border + padding — see .iedit), so this is only a tap affordance. */
.editable {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The live editor box: pink border and padding cancelled by equal negative
 * margins, so the text inside sits EXACTLY where the resting text sat —
 * entering edit mode never shifts the line. Controls sit inside the border,
 * right-aligned below the text. */
.iedit {
  display: block;
  border: 1.5px solid var(--pink);
  border-radius: var(--r-summary);
  padding: 6px 8px;
  margin: -7.5px -9.5px;
}

/* Editing an action item's due label: the editor replaces the pink chip for
 * the edit's duration, on its own full row at the chip's type size. */
.due-edit {
  flex: 1 1 100%;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
}

.iedit__field {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  caret-color: var(--pink);
  resize: none;
  outline: none;
  overflow: hidden;
}

/* Attention pulse: a tap on OTHER editable text while this editor is open
 * flashes a soft ring around the pink border. Shadow only — zero layout
 * shift; --pink-line is theme-correct in light and dark. */
.iedit--pulse {
  animation: ieditPulse 220ms ease;
}

@keyframes ieditPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 46, 116, 0); }
  50% { box-shadow: 0 0 0 4px var(--pink-line); }
}

.iedit__controls {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 6px;
}

.iedit__btn {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.iedit__btn svg {
  width: 16px;
  height: 16px;
}

.iedit__btn--cancel {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text-secondary);
}

.iedit__btn--save {
  border: 0;
  background: var(--pink);
  color: #fff;
}

/* "Split here" (transcript turns only): a --pink-soft pill to the LEFT of the
 * × / ✓, which keep their position and appearance. Splits the open turn at
 * the caret and hands the new turn to the speaker picker. */
.iedit__split {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.iedit__split svg {
  width: 14px;
  height: 14px;
}

/* One-time "tap to edit" discoverability hint on the memo Summary. */
.edit-hint {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--pink-soft);
}

.edit-hint__icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  color: var(--pink);
}

.edit-hint__icon svg {
  width: 18px;
  height: 18px;
}

.edit-hint__text {
  flex: 1;
  font-size: 14px;
  font-weight: 600;
  color: var(--pink-text);
}

.edit-hint__close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--pink-text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.edit-hint__close svg {
  width: 15px;
  height: 15px;
}

/* Transcript footer caption (edits never regenerate the summary). */
.transcript__caption {
  margin-top: 18px;
}

/* ============================================================
 * Ask about this meeting (#/memo/:id/ask)
 * ============================================================ */

.ask {
  /* NO FORCED SCROLL: #app adds the safe-area insets as padding, so a plain
   * 100dvh here guaranteed ~(top+bottom inset) of dead scroll on device —
   * the empty state opened scrolled past its orb (same mechanism as the
   * v105 Schedule / v149 .home+.tasks clamps). */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  padding: 14px 20px 0;
  display: flex;
  flex-direction: column;
}

/* Meeting title beside the back pill: the quiet secondary locator treatment
 * (same recipe as .wordmark-row__title), truncated with an ellipsis. */
.ask__title {
  flex: 1;
  min-width: 0;
  text-align: right;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.2px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ask__clear {
  flex: 0 0 auto;
  color: var(--text-secondary);
}

/* Thread column; bottom padding clears the pinned input bar (the tab bar is
 * hidden on this route — see tabbar.js syncTabbar). */
.ask__thread {
  flex: 1;
  padding: 10px 0 calc(env(safe-area-inset-bottom, 0px) + 86px);
}

/* --- Empty state --- */
.ask__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  /* 8px here + the thread's 10px top padding = 18px below the sticky topbar
   * — the SAME topbar-to-content spacing the transcript screen's .turns uses
   * (margin-top: 18px), not a new value. The old 48px floated the orb in
   * dead space (device-found). */
  padding: 8px 4px 24px;
}

.ask__orb {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--pink-soft);
  color: var(--pink);
}

.ask__orb svg {
  width: 26px;
  height: 26px;
}

.ask__headline {
  margin-top: 14px;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

.ask__subtext {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.ask__suggestions {
  margin-top: 22px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ask__suggestion {
  width: 100%;
  padding: 13px 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card);
  box-shadow: var(--shadow-card);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* --- Conversation --- */
.ask__exchange {
  padding: 18px 0;
  border-top: 1px solid var(--line);
}

.ask__exchange:first-child {
  border-top: 0;
}

.ask__q {
  margin-left: auto;
  width: fit-content;
  max-width: 82%;
  padding: 10px 14px;
  border-radius: 16px;
  background: var(--pink-soft);
  color: var(--text);
  font-size: 15px;
  line-height: 1.5;
}

/* Answers are plain prose — no bubble, no avatar, no label. They read like
 * the rest of the app's content. */
/* Answers are structured HTML from the markdown-lite renderer
 * (js/rich-text.js: <p>/<br>/<ul>/<ol>/<strong> only) — pre-wrap is gone
 * because the renderer consumes the newlines; stray whitespace between
 * blocks would double-space otherwise. */
.ask__a {
  margin-top: 14px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
}

.ask__a p {
  margin: 0;
}

.ask__a ul,
.ask__a ol {
  margin: 0;
  padding-left: 20px;
}

.ask__a > * + * {
  margin-top: 8px;
}

.ask__a li + li {
  margin-top: 4px;
}

.ask__a strong {
  font-weight: 700;
}

.ask__cites {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ask__cite {
  padding: 4px 11px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Thinking indicator: three pulsing dots where the answer will appear. */
.ask__thinking {
  margin-top: 16px;
  display: flex;
  gap: 5px;
}

.ask__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--pink);
  animation: askPulse 1.2s ease-in-out infinite;
}

.ask__dot:nth-child(2) { animation-delay: 0.15s; }
.ask__dot:nth-child(3) { animation-delay: 0.3s; }

@keyframes askPulse {
  0%, 100% { opacity: 0.25; }
  50% { opacity: 1; }
}

.ask__error {
  margin-top: 14px;
  padding: 10px 14px;
  border-radius: var(--r-list);
  background: var(--red-soft);
  color: var(--red-text);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
}

/* --- Pinned input row (bottom of the screen — the tab bar is hidden on the
 * Ask route; JS lifts the row above the keyboard) --- */
.ask__inputbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 20px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
}

.ask__input {
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card);
  box-shadow: var(--shadow-card);
  font-family: inherit;
  font-size: 15px;
  color: var(--text);
  outline: none;
}

.ask__input::placeholder {
  color: var(--text-tertiary);
}

.ask__send {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--pink);
  color: #fff;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.ask__send:disabled {
  opacity: 0.55;
  cursor: default;
}

.ask__send svg {
  width: 20px;
  height: 20px;
}

/* Momentary emphasis on a turn the user jumped to from an Ask citation
 * (the old flagged-turn treatment no longer exists — this is its stand-in).
 * The spread ring lets the wash breathe without any layout shift. The SAME
 * treatment marks the memo card a reprocess just returned the user to. */
.turn.is-jumped,
.memo-card.is-jumped {
  background: var(--pink-soft);
  box-shadow: 0 0 0 8px var(--pink-soft);
  border-radius: var(--r-list);
}

.memo-card.is-jumped {
  border-radius: var(--r-memo); /* keep the card's own radius */
}

.turn {
  transition: background 600ms ease, box-shadow 600ms ease;
}

/* Cards keep the 200ms theme-flip background pace; only the pulse ring gets
 * the slow wash. (Swipe-wrapped cards' own transform transition wins there —
 * the pulse target is the processing card, which isn't swipe-wrapped.) */
.memo-card {
  transition: background-color 200ms ease, box-shadow 600ms ease;
}

/* Friendly toast (e.g. a delete that couldn't reach the server). Rounded
 * rectangle (not a pill) that sizes to its text and wraps to multiple lines —
 * the full message always shows, never truncates. */
.memo-toast {
  position: fixed;
  left: 50%;
  bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 12px);
  z-index: 120;
  width: max-content;
  max-width: min(420px, calc(100vw - 40px));
  padding: 12px 16px;
  border-radius: 5px;
  background: var(--text);
  color: var(--bg);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
  text-align: center;
  overflow-wrap: break-word;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  opacity: 0;
  pointer-events: none;
  /* bottom is in the list so a toast that outlives a screen-scoped offset
   * (below) glides to the default spot instead of snapping. */
  transition: opacity 0.2s ease, transform 0.2s ease, bottom 0.2s ease;
}
.memo-toast.is-on {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* A screen with a FLOATING control in the toast's landing zone declares it by
 * setting this class on <body> (Schedule does, for its centered add pill —
 * mount adds, unmount removes); the shared toast then rises to sit just above
 * the control instead of covering it. Declarative and screen-scoped: every
 * toast source on that screen rides up (saves, errors, deletes), and shared
 * helpers like ensureReminderAfterSave stay position-agnostic. 84px = the
 * pill's 30px bottom offset + its 42px height + 12px of daylight; well clear
 * of the tab bar and the record button's 22px cradle rise. */
body.toast-above-float .memo-toast {
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 84px);
}

/* Paywall (v131): with the content vertically CENTERED and the inner's 88px
 * bottom pad reserving a toast band, the toast belongs back at the bottom —
 * tucked low into that band (the default 96px offset exists to clear the tab
 * bar, which this screen doesn't have). Same body-class mechanism as the
 * Schedule tab above (set by paywall.js mount/unmount); no other screen's
 * toast is touched. Inverted toast surface reads fine on the always-dark
 * paywall (near-white on near-black). */
body.toast-above-paywall .memo-toast {
  bottom: calc(env(safe-area-inset-bottom, 0px) + 18px);
}

/* Toast with an inline action (Tasks tab "Task completed" + Undo). The base
 * toast is pointer-events:none; only a VISIBLE action toast re-enables taps,
 * so a faded-out Undo can never swallow a touch. Plain --pink reads on the
 * inverted toast surface in both themes (near-black in light, near-white in
 * dark). */
.memo-toast--action {
  display: flex;
  align-items: center;
  gap: 16px;
  text-align: left;
}

.memo-toast--action.is-on {
  pointer-events: auto;
}

.memo-toast__action {
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 800;
  letter-spacing: 0.2px;
  color: var(--pink);
  cursor: pointer;
}

/* (The standalone record FAB was removed — the persistent bottom tab bar's
 * raised center button now handles recording. See the tab bar section below.) */

/* --- Recording: paused dimming and friendly states --- */
.rec.is-paused .rec__wave {
  opacity: 0.4;
}

/* Centered variant for mic-denied / saving / save-error states. */
.rec__center--mid {
  gap: 14px;
}

.rec__notice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 320px;
}

.rec__noticetitle {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

.rec__noticetext {
  font-size: 15px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.rec__savingtitle {
  margin-top: 18px;
  font-size: 20px;
  font-weight: 700;
}

.rec__btn {
  margin-top: 4px;
  height: 48px;
  padding: 0 22px;
  border: 0;
  border-radius: 7px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
}

.rec__btn--ghost {
  background: var(--card-2);
  color: var(--text);
  border: 1px solid var(--line);
}

/* ============================================================
 * Processing (#/processing) — theme-following since v107
 * ============================================================ */
.proc {
  position: relative;
  /* NO SCROLL: clamp to the viewport minus #app's safe-area padding (same
   * treatment as .rec). #app already provides the safe-area bottom inset, so
   * the 40px is pure breathing room for the footer line. */
  height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 18px 22px 40px;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}

/* Clean top boundary hairline — same language as the other screens. */
.proc::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
  z-index: 2;
}

/* Back/close control — card circle + hairline (v107; was a hardcoded
 * white-alpha ghost that assumed the always-dark bg). Matches the app's
 * other circular buttons in both themes. Sits clear of the safe-area top. */
.proc__back {
  position: absolute;
  z-index: 2;
  /* #app already offsets the safe area — adding it again here double-counted
   * the inset and left the button floating in dead space. */
  top: 12px;
  left: 16px;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  color: var(--text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.proc__back svg {
  width: 22px;
  height: 22px;
}

.proc__back:active {
  background: var(--card-2);
}

.proc__center {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0; /* may compress (no-scroll screen) */
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Slight upward bias: pure centering read as mid-screen with dead space
   * above — this balances the composition against the top back button. */
  padding-bottom: 5vh;
}

.proc__orb {
  position: relative;
  width: 92px;
  height: 92px;
  display: grid;
  place-items: center;
  margin-bottom: 22px;
}

.proc__ring {
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 3px solid var(--pink-soft);
  border-top-color: var(--pink);
  animation: procSpin 1s linear infinite;
}

.proc__orbcore {
  width: 92px;
  height: 92px;
  border-radius: 50%;
  background: var(--pink);
  box-shadow: var(--shadow-pink);
  /* Flex-center the sparkle (the glyph path is also recentered in its viewBox). */
  display: flex;
  align-items: center;
  justify-content: center;
}

.proc__orbcore svg {
  width: 40px;
  height: 40px;
  color: #fff;
}

.proc__title {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.3px;
  text-align: center; /* v120: only looked centered while it fit one line */
}

.proc__sub {
  margin-top: 6px;
  font-size: 14.5px;
  color: var(--text-secondary);
  text-align: center; /* v120: same latent wrap-raggedness as the title */
}

.proc__steps {
  list-style: none;
  margin: 30px 0 0;
  padding: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}

.proc__step {
  display: flex;
  align-items: center;
  gap: 13px;
}

.proc__step.is-pending {
  opacity: 0.5;
}

.proc__icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
}

.proc__icon--done {
  background: var(--green);
  color: #fff;
}

.proc__icon--done svg {
  width: 16px;
  height: 16px;
}

.proc__icon--active {
  background: var(--pink);
  box-shadow: 0 0 0 4px var(--pink-soft);
}

.proc__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
}

.proc__icon--pending {
  background: transparent;
  border: 2px solid var(--text-tertiary);
}

/* Fill the row so the progress track below the sub-line can span the full
   text area (a shrink-to-fit block would clamp it to the widest text line). */
.proc__steptext {
  flex: 1;
  min-width: 0;
}

.proc__steplabel {
  display: block;
  font-size: 15.5px;
  font-weight: 700;
}

.proc__stepsub {
  display: block;
  margin-top: 1px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* Elapsed-time tail on the active sub-line ("· 0:23") — real wall-clock time
   since processing began, quiet reassurance, never an ETA. Tabular digits so
   the 1s tick doesn't wiggle the line. */
.proc__elapsed {
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* Real milestone progress under the transcribe step: the fill advances ONLY
   when something actually completes server-side (a transcript chunk, or a
   short recording's summary/transcript call) — no fake motion. The empty
   track is visible from the moment the step goes active; --pink-line rings
   it so the unfilled portion reads on the always-dark screen (--pink-soft
   alone is 16% alpha there — near-invisible). */
.proc__bar {
  display: block;
  width: 100%;
  height: 6px;
  margin-top: 8px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  box-shadow: inset 0 0 0 1px var(--pink-line);
  overflow: hidden;
}

/* 2s ease so the fill visibly travels between milestones. HONESTY: the width
   only ever changes in a render triggered by server-reported state (renderSteps
   / animateBarFromPrev) — the transition starts strictly AFTER a real
   milestone lands and never creeps on its own. Keep duration in sync with
   BAR_EASE_MS in processing.js. */
.proc__barfill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--pink);
  transition: width 1000ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Save-step failure: red X icon, red message, inline retry. */
.proc__icon--error {
  background: var(--red);
  color: #fff;
}

.proc__icon--error svg {
  width: 16px;
  height: 16px;
}

.proc__stepsub--error {
  color: var(--red-text);
}

.proc__retry {
  display: inline-block;
  margin-top: 6px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pink-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

.proc__foot {
  position: relative;
  z-index: 1;
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-tertiary);
  /* Centered (v120): the note wraps to two lines on phones, and a wrapped
   * shrink-to-fit box reads ragged-left against the centered composition
   * above. The steps checklist stays left-aligned on purpose — it's a list. */
  text-align: center;
}

/* The stuck-memo escape hatch (v128): a quiet pink text action under the foot
 * note — pink is the in-app action per the destructive color rule; the red
 * step is the confirm dialog's Discard button. Both themes via --pink-text. */
.proc__discard {
  position: relative;
  z-index: 1;
  margin-top: 6px;
  border: 0;
  padding: 8px 12px;
  background: transparent;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--pink-text);
  cursor: pointer;
}

@keyframes procSpin {
  to {
    transform: rotate(360deg);
  }
}

/* ============================================================
 * Summary / outline (#/memo/:id) — the hero
 * ============================================================ */
.summary {
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  padding: 14px 20px 40px;
}

.summary__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-top: 6px;
}

.summary__title {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.5px;
  line-height: 1.1;
}

.meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.meta-row__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-size: 14px;
  color: var(--text-secondary);
}

.meta-row__item svg {
  width: 16px;
  height: 16px;
  color: var(--text-tertiary);
}

/* --- Mini player (visual only) --- */
.miniplayer {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 18px 0;
  padding: 14px 16px 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  box-shadow: var(--shadow-card);
}

/* Transport row: skip · play · skip, centered as a group with even spacing. */
.miniplayer__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 28px;
}

.miniplayer__play {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0; /* drop the UA <button> padding so the glyph centers in the circle */
  border: 0;
  border-radius: 50%;
  background: var(--pink);
  color: #fff;
  cursor: pointer;
}

.miniplayer__play svg {
  width: 40px;
  height: 40px;
}

.miniplayer__skip {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.miniplayer__skip svg {
  width: 26px;
  height: 26px;
}

.miniplayer__skip:active {
  color: var(--pink-text);
}

/* Disabled (no audio yet) + loading spinner states. */
.miniplayer__play:disabled,
.miniplayer__skip:disabled {
  opacity: 0.4;
  cursor: default;
}

.miniplayer__play .spin {
  width: 26px;
  height: 26px;
  animation: mp-spin 0.8s linear infinite;
}

@keyframes mp-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Scrubber row: current time · scrubber (flexes) · total duration */
.miniplayer__scrub {
  display: flex;
  align-items: center;
  gap: 10px;
}

.miniplayer__time {
  flex: 0 0 auto;
  min-width: 34px; /* box stability; digit-level stability comes from .dgt slots (Saira has no tabular figures) */
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.miniplayer__cur {
  text-align: right;
}

.miniplayer__total {
  text-align: left;
  color: var(--text-tertiary);
}

/* Scrubber: padded hit area (easy grab) + thin track + pink fill + white handle */
.scrubber {
  position: relative;
  flex: 1 1 auto;
  padding: 8px 0;
  cursor: pointer;
  touch-action: none; /* let pointer drags scrub instead of scrolling */
}

.scrubber.is-disabled {
  pointer-events: none;
  opacity: 0.45;
}

.scrubber__track {
  position: relative;
  height: 4px;
  border-radius: 999px;
  background: var(--line);
}

.scrubber__fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: var(--pink);
}

.scrubber__handle {
  position: absolute;
  top: 50%;
  left: 0%;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transform: translate(-50%, -50%);
}

/* --- The gist (solid pink, always visible) --- */
.gist {
  padding: 18px;
  border-radius: var(--r-summary);
  background: var(--pink);
  color: #fff;
  box-shadow: 0 10px 26px rgba(255, 46, 116, 0.32);
}

.gist__text {
  margin-top: 8px;
  font-size: 17px;
  line-height: 1.5;
  font-weight: 500;
}

/* Gist rendered inside its collapsible section card: keep the pink emphasis,
 * drop the floating shadow, first line needs no label offset. */
.gist--card {
  box-shadow: none;
  padding: 14px 16px;
}
.gist--card .gist__text {
  margin-top: 0;
}

/* --- Section cards w/ eye toggle --- */
.sections {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 14px;
}

.sec-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.sec-card__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
}

.sec-card__tile {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 5px;
  background: var(--pink-soft);
  color: var(--pink-text);
}

.sec-card__tile svg {
  width: 18px;
  height: 18px;
}

.sec-card__title {
  font-size: 17px;
  font-weight: 700;
}

.sec-card__count {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-tertiary);
}

/* Full-width mark-all row under the last action item: double-check glyph +
 * label, centered, hairline-separated from the list. */
.ai-allrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  margin-top: 14px;
  padding: 13px 0 3px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--pink-text);
  cursor: pointer;
}

.ai-allrow svg {
  width: 17px;
  height: 17px;
}

.sec-card__eye {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--pink-text);
  cursor: pointer;
}

.sec-card__eye svg {
  width: 20px;
  height: 20px;
}

/* Collapsed section: body is removed from the DOM, but the header stays in
 * its ACTIVE pink state (tile, title, eye) — the eye/eye-off glyph alone
 * signals collapsed. Graying it out read as "disabled/empty". */

.sec-card__body {
  padding: 0 16px 16px;
}

/* Key points — pink dot bullets */
.kp-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.kp-list li {
  position: relative;
  padding-left: 18px;
  font-size: 15px;
  line-height: 1.5;
}

.kp-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--pink);
}

/* Decisions — pink-soft check markers */
.dec-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.dec-list li {
  position: relative;
  padding-left: 28px;
  font-size: 15px;
  line-height: 1.5;
}

.dec-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--pink-soft);
}

.dec-list li::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 6px;
  width: 8px;
  height: 5px;
  border-left: 2px solid var(--pink-text);
  border-bottom: 2px solid var(--pink-text);
  transform: rotate(-45deg);
}

/* Action items — checkbox + task + owner + due */
.ai-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ai-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.ai-check {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border-radius: 6px;
  border: 2px solid var(--pink-line);
  background: transparent;
  color: transparent;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}

.ai-check svg {
  width: 14px;
  height: 14px;
}

.ai-check.is-done {
  border-color: var(--green);
  color: var(--green);
}

.ai-task {
  flex: 1 1 140px;
  font-size: 15.5px;
  line-height: 1.45;
  transition: color 160ms ease;
}

.ai-row.is-done .ai-task {
  color: var(--text-tertiary);
  text-decoration: line-through;
}

.ai-owner {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.ai-ownername {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-secondary);
}

/* Attendees — avatar + full name */
.att-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.att-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.att-name {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}

/* Inline speaker rename input in the Attendees section. */
.att-name-input {
  min-width: 0;
  max-width: 200px;
  padding: 3px 9px;
  border: 1px solid var(--pink-line);
  border-radius: 5px;
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
}

.att-name-input:focus {
  outline: none;
  border-color: var(--pink);
}

/* --- Full transcript row --- */
.transcript-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  padding: 14px 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  box-shadow: var(--shadow-card);
  color: inherit;
  text-decoration: none;
}

.transcript-row__tile {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 5px;
  background: var(--pink-soft);
  color: var(--pink-text);
}

.transcript-row__tile svg {
  width: 18px;
  height: 18px;
}

.transcript-row__text {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.transcript-row__title {
  font-size: 15.5px;
  font-weight: 700;
}

.transcript-row__sub {
  margin-top: 1px;
  font-size: 13px;
  color: var(--text-tertiary);
}

.transcript-row__chev {
  color: var(--text-tertiary);
}

.transcript-row__chev svg {
  width: 20px;
  height: 20px;
}

/* ============================================================
 * Transcript (#/memo/:id/transcript)
 * ============================================================ */
.transcript {
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  padding: 14px 20px 40px;
}

.transcript__head {
  margin: 6px 0 8px;
}

.transcript__title {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.5px;
}

.transcript__sub {
  margin-top: 4px;
  font-size: 14px;
  color: var(--text-secondary);
}

.turns {
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin-top: 18px;
}

.turn__head {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* The turn's avatar is a button: tapping it opens the "Who said this?"
 * speaker picker. Zero chrome of its own — the avatar IS the control. */
.turn__avatarbtn {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.turn__name {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 700;
  cursor: pointer;
}

/* Inline speaker rename input — sits in the turn head, matches the name size. */
.turn__name-input {
  min-width: 0;
  max-width: 180px;
  padding: 2px 8px;
  border: 1px solid var(--pink-line);
  border-radius: 5px;
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 700;
}

.turn__name-input:focus {
  outline: none;
  border-color: var(--pink);
}

.turn__ts {
  font-size: 13px;
  color: var(--text-tertiary);
}

.turn__text {
  margin-top: 7px;
  padding-left: 41px; /* aligns under the name (32px avatar + 9px gap) */
  font-size: 15.5px;
  line-height: 1.55;
}

/* --- Docked audio player (verify-by-ear for the turn repair tools) --- */

/* Sticky directly below the sticky topbar: the offset is the topbar's
 * computed height — safe-area + 18px top padding + 38px pill row + 10px
 * bottom padding + 1px hairline. Same full-bleed translucent-blur band so
 * turns scroll UNDER the stack, never over it. z-index one below the topbar
 * so the search overlay (absolute inside the topbar) expands above it. */
.tplayer-dock {
  position: sticky;
  top: calc(env(safe-area-inset-top, 0px) + 67px);
  z-index: 19;
  margin: 0 -20px;
  padding: 10px 20px;
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.tplayer {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-summary);
  box-shadow: var(--shadow-card);
}

.tplayer__play {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0; /* drop the UA <button> padding so the glyph centers in the circle */
  border: 0;
  border-radius: 50%;
  background: var(--pink);
  color: #fff;
  cursor: pointer;
}

.tplayer__play svg {
  width: 26px;
  height: 26px;
}

.tplayer__play .spin {
  width: 18px;
  height: 18px;
  animation: mp-spin 0.8s linear infinite; /* shared with the mini player */
}

/* Transport cluster: skip · play · skip (v102). Tight 6px internal gaps so
 * the scrubber keeps usable width at 320pt (see the width budget on
 * .tplayer__scrub below). */
.tplayer__controls {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Same treatment as .miniplayer__skip — secondary color so the pink play
 * button stays the primary action, pink flash on press. Glyph drawn at 24px
 * (vs the Summary's 26px) to sit proportionally against this player's 34px
 * play circle (the Summary's is 48px); the button keeps a 30px tap box. */
.tplayer__skip {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.tplayer__skip svg {
  width: 24px;
  height: 24px;
}

.tplayer__skip:active {
  color: var(--pink-text);
}

.tplayer__scrub {
  flex: 1 1 auto;
  min-width: 0;
}

/* Slightly slimmer than the summary scrubber: 3px track, smaller thumb. */
.tplayer .scrubber {
  padding: 7px 0 5px;
}

.tplayer .scrubber__track {
  height: 3px;
}

.tplayer .scrubber__handle {
  width: 11px;
  height: 11px;
}

/* Times UNDER the bar: current left, total right. */
.tplayer__times {
  display: flex;
  justify-content: space-between;
}

.tplayer__time {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
}

.tplayer__total {
  color: var(--text-tertiary);
}

/* Speed control: deliberately quiet — secondary text, no button chrome. */
.tplayer__rate {
  flex: 0 0 auto;
  min-width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* --- The turn playback is inside --- */

/* Same spread-wash geometry as the momentary .is-jumped emphasis (no layout
 * shift); what distinguishes them is behavior — this one tracks the playhead
 * and persists, the jump wash fades in 1.8s. */
.turn.is-playing {
  background: var(--pink-soft);
  box-shadow: 0 0 0 8px var(--pink-soft);
  border-radius: var(--r-list);
}

/* Timestamp as a tap target (only rendered as a <button> when audio exists;
 * the audio-less <span> stays tertiary metadata). v102: pink play triangle +
 * pink medium time so it reads as the control it is — same 13px size from
 * the shared .turn__ts rule. The padding/negative-margin pair enlarges the
 * hit area without moving the text; 8px keeps it just inside the 9px flex
 * gap to the name button so the two hit boxes can't overlap even at the
 * smallest widths (the triangle widens the visible box but the expansion
 * stays anchored to its edges, so the clearance math is unchanged). */
.turn__ts--btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin: -8px;
  padding: 8px;
  border: 0;
  background: none;
  color: var(--pink-text);
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The 10×11 glyphs sit inside the 13px text's line box — row height is
 * untouched. On the playing row the triangle swaps for a quiet static
 * equalizer ("sound is here"), NOT a pause glyph — tapping a timestamp
 * re-seeks to the turn's start rather than pausing, and the glyph must not
 * promise otherwise. Pure CSS swap so the highlight's classList toggle
 * (which never re-renders the list) carries it. */
.turn__ts-glyph {
  flex: 0 0 auto;
}

.turn__ts-glyph--live {
  display: none;
}

.turn.is-playing .turn__ts-glyph--play {
  display: none;
}

.turn.is-playing .turn__ts-glyph--live {
  display: block;
}

/* Keep the followed (auto-scrolled) turn clear of the sticky topbar + player
 * stack above and the tab bar below. */
.turn {
  scroll-margin-top: calc(env(safe-area-inset-top, 0px) + 172px);
  scroll-margin-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 32px);
}

/* ============================================================
 * Settings (#/settings)
 * ============================================================ */
.settings {
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  padding: 14px 20px 40px;
}

.settings-group {
  margin-bottom: 26px;
}

.group-title {
  margin: 0 0 10px 4px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.group-caption {
  margin: 10px 4px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-tertiary);
}

.settings-card {
  padding: 4px 16px;
}

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 13px 0;
  border-top: 1px solid var(--line);
}

.switch-row:first-child {
  border-top: 0;
}

.switch-row__label {
  font-size: 15.5px;
}

/* Notifications rows (v113) — the same card-row rhythm with a quiet second
 * line under the label; the interruption row stacks a pill radio beneath
 * instead of a switch. */
.switch-row__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.switch-row__sub {
  font-size: 13px;
  color: var(--text-secondary);
}

.switch-row--stack {
  display: block;
}

/* The three interruption-alert pills — value-for-value the schedule sheet's
 * lead pills (.schedsheet__lead): the language picker's selected treatment
 * (--pink-soft fill, --pink-text label) on pill-radius chips. Kept as its own
 * block, house style (like the tab bar's token mirrors) — keep in sync. */
.notif-pills {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.notif-pill {
  flex: 1 1 0;
  min-width: 0;
  height: 38px;
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card-2);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.notif-pill.is-selected {
  background: var(--pink-soft);
  border-color: transparent;
  color: var(--pink-text);
}

/* Language rows — same card-row rhythm as the switches / legal rows (13px
 * vertical padding, hairline divider, 15px label), as a full-width button:
 * label left, current value + chevron right. */
.lang-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  padding: 13px 0;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  font-family: inherit;
  font-size: 15px;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.lang-row:first-child {
  border-top: 0;
}

.lang-row__value {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--text-secondary);
}

/* --- 3-way segmented control (appearance) --- */
.segmented {
  display: flex;
  gap: 6px;
  padding: 5px;
  border-radius: 8px;
  background: var(--card-2);
  border: 1px solid var(--line);
}

.segmented__seg {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 11px 6px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  cursor: pointer;
}

.segmented__seg svg {
  width: 22px;
  height: 22px;
}

.segmented__label {
  font-size: 13px;
  font-weight: 600;
}

/* Theme-option subtitle under the label (Light "Blend in" / Dark "Stand
 * out"; System stays label-only). The schedule sheet's sub-label tier
 * (.schedsheet__recnow-sub): 11.5px --text-secondary — explicit color so it
 * stays quiet inside the selected segment's --text too. */
.segmented__sub {
  margin-top: -3px;
  font-size: 11.5px;
  color: var(--text-secondary);
}

.segmented__seg.is-selected {
  background: var(--card);
  box-shadow: var(--shadow-card);
  color: var(--text);
}

.segmented__seg.is-selected .segmented__icon {
  color: var(--pink);
}

/* --- iOS-style switch (reused on all toggle rows) ---
 *
 * State model (toggle-flash fix):
 *   • CLASSLESS base = the loading SKELETON: bordered --card-2 track with a
 *     small pulsing center dot and NO knob. It cannot be read as on or off,
 *     and any future gap between insertion and state application paints this
 *     neutral skeleton — never the off position.
 *   • .is-off / .is-on are EXPLICIT committed states (knob shown, border off).
 *   • .no-anim suppresses transitions for one frame so the skeleton→state
 *     reveal SNAPS; user taps (no .no-anim) keep the 200ms animation. */
.iosswitch {
  flex: 0 0 auto;
  position: relative;
  box-sizing: border-box;
  width: 50px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card-2);
  cursor: default;
  transition: background-color 0.2s ease;
}

/* Skeleton center dot — obviously "not loaded yet", not a switch position. */
.iosswitch::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: var(--text-tertiary);
  animation: switch-load-pulse 1.1s ease-in-out infinite;
}

@keyframes switch-load-pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 0.8; }
}

/* Knob exists only in committed states. */
.iosswitch__knob {
  display: none;
  position: absolute;
  top: 3px;
  left: 3px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}

.iosswitch.is-off,
.iosswitch.is-on {
  border: 0;
  cursor: pointer;
}

.iosswitch.is-off::after,
.iosswitch.is-on::after {
  content: none;
}

.iosswitch.is-off .iosswitch__knob,
.iosswitch.is-on .iosswitch__knob {
  display: block;
}

.iosswitch.is-off {
  background: var(--text-tertiary);
}

.iosswitch.is-on {
  background: var(--pink);
}

.iosswitch.is-on .iosswitch__knob {
  transform: translateX(20px);
}

/* One-frame transition suppression for the skeleton→state reveal (applySwitch
 * adds it, forces a reflow, removes it next frame). The reveal must SNAP. */
.iosswitch.no-anim,
.iosswitch.no-anim .iosswitch__knob {
  transition: none !important;
}

/* Respect reduced-motion for the new animated bits. */
@media (prefers-reduced-motion: reduce) {
  .proc__ring {
    animation: none;
  }
  .iosswitch__knob,
  .iosswitch {
    transition: none;
  }
  .iosswitch.is-loading {
    animation: none;
  }
}

/* ============================================================
 * Bottom tab bar (persistent shell chrome) — Option B
 *
 * Rendered once by the shell as a sibling of #app and kept in sync by the
 * router. Hidden on the immersive capture routes (#/recording, #/processing)
 * via .is-hidden; body.has-tabbar drives content bottom-padding so views
 * aren't clipped behind the bar.
 * ============================================================ */

/* Bar height excluding the iOS safe-area inset (layout constant, not a color). */
:root {
  --tabbar-h: 62px;
}


.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  /* Extend the container's own box 120px PAST the viewport bottom so the
   * container background — the most reliably-rendered layer we have (the
   * red/lime experiments proved it paints where child layers did not) —
   * covers the whole below-surface region in standalone PWA and the native
   * webview. KNOWN, ACCEPTED LIMITATION: in iOS Safari BROWSER mode this
   * does NOT recolor the band behind Safari's translucent bottom toolbar —
   * Safari composites the DOCUMENT there, never fixed elements (proven
   * across five attempts, v68–v75), so page content stays faintly visible
   * behind Safari's own chrome. The only real fix is an app-shell scroll
   * refactor (html/body locked, #app as the 100dvh scroller), deliberately
   * deferred: it risks pull-to-refresh / sticky bars / scroll-to-top, and
   * the shipping targets (native app + installed PWA) are unaffected.
   * The bar's visible top edge is unchanged: the extra 120px of height
   * exactly cancels the -120px bottom. */
  bottom: -120px;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  justify-content: space-around;
  box-sizing: border-box;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 120px);
  /* Clear the iOS home indicator. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
  /* The notched SVG cradle draws the bar SURFACE (top 62px); the container
   * paints everything BELOW it (y=61px down through the overshoot) itself,
   * as a positioned solid-color gradient band. Using the background this way
   * keeps y<61 unpainted, so the notch scoop stays a true see-through cutout
   * while the below-surface region (home-indicator inset in standalone/
   * native) is opaque bar-surface color. See the accepted-limitation note
   * above for why this cannot reach behind Safari's browser-mode toolbar.
   * Concrete per-theme values mirror .tabbar__bgfill (#FFFFFF / #1F1F24). */
  background-image: linear-gradient(#FFFFFF, #FFFFFF);
  background-repeat: no-repeat;
  background-position: 0 61px;
  background-size: 100% 1000px;
  overflow: visible;
}

[data-theme="dark"] .tabbar,
[data-immersive="dark"] .tabbar {
  background-image: linear-gradient(#1F1F24, #1F1F24);
}

.tabbar.is-hidden {
  display: none;
}

/* Keyboard up (v135): body.kb-open is toggled by js/keyboard-watch.js while
 * the on-screen keyboard shrinks the visual viewport. The bar is unreachable
 * behind the keyboard, and WKWebView's fixed-position anchoring slides
 * against the visible screen for the whole presentation (the "floating tab
 * bar") — so the bar is simply gone until the keyboard is. */
body.kb-open .tabbar {
  display: none;
}

/* Cradle surface: a notched shape filled with the theme bar color, with the top
 * edge (incl. the curve) stroked as the hairline — no straight border. Stretches
 * to device width; the notch stays centered. */
.tabbar__bg {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--tabbar-h);
  z-index: 0;
  overflow: visible; /* let the 1px top stroke show, not clip */
  pointer-events: none;
}

/* Bar surface — the "see-through bar" was CONTRAST, not paint (the v73 layer
 * probe showed every layer renders opaquely): in dark mode the old surface
 * (--card #161619) was near-identical to the page (--bg #0B0B0D) and IDENTICAL
 * to the memo cards, so cards visually "continued into" the bar and the bar
 * read as transparent. The surface is therefore the ELEVATED tier (--card-2's
 * values: #1F1F24 dark — lighter than both the page and the cards) with a
 * stronger top hairline, so the bar reads as its own solid plane in both
 * themes. Values are concrete (mirroring tokens.css --card / --card-2; keep in
 * sync) with per-theme rules on the same [data-theme]/[data-immersive]
 * selector pair tokens.css uses; theme switching snaps, as everywhere else. */
.tabbar__bgfill {
  fill: #FFFFFF;
}

[data-theme="dark"] .tabbar__bgfill,
[data-immersive="dark"] .tabbar__bgfill {
  fill: #1F1F24;
}

.tabbar__bgline {
  fill: none;
  stroke: rgba(11, 11, 13, 0.1);
  stroke-width: 1;
  vector-effect: non-scaling-stroke; /* crisp 1px hairline despite x-stretch */
}

[data-theme="dark"] .tabbar__bgline,
[data-immersive="dark"] .tabbar__bgline {
  stroke: rgba(255, 255, 255, 0.16);
}

/* Opaque underlay below the notched surface. It starts 1px under the SVG (no
 * subpixel seam) and OVERSHOOTS the viewport bottom by 120px on purpose: on
 * mobile-web Safari/Chrome the browser reports env(safe-area-inset-bottom)=0
 * and its bottom chrome is translucent, so anything the page renders below the
 * layout viewport's bottom edge shows through the toolbar — previously the
 * scrolling memo content, which made the bar look suspended. The overshoot
 * means that region is always bar-colored. Fixed-position children don't
 * affect the document's scroll size, so the off-screen 120px is free; in
 * standalone/native the same strip covers the home-indicator inset as before. */
/* Redundant second painter for the below-surface region (belt and braces with
 * the container's gradient band above). Geometry is deliberately DUMB — static
 * top + bottom pinning inside the extended container, no env(), no calc(), no
 * height — so there is nothing left that can collapse to 0 or fail to parse
 * the way the old `height: calc(env(...) + 121px)` strip evidently did on the
 * affected iPhone (the v73 probe showed lime/container where this strip's
 * orange should have been). Color must match .tabbar__bgfill exactly. */
.tabbar__safe {
  position: absolute;
  left: 0;
  right: 0;
  top: 61px; /* var(--tabbar-h) - 1px, hardcoded on purpose */
  bottom: 0; /* the container box already extends 120px past the viewport */
  background: #FFFFFF;
  z-index: 0;
  pointer-events: none;
}

[data-theme="dark"] .tabbar__safe,
[data-immersive="dark"] .tabbar__safe {
  background: #1F1F24;
}

/* Memos / Settings items: stacked glyph + label, tertiary when inactive. */
.tabbar__item {
  position: relative;
  z-index: 1; /* above the SVG surface */
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: var(--tabbar-h);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: color 150ms ease;
}

.tabbar__item.is-active {
  color: var(--pink-text);
}

.tabbar__glyph {
  display: grid;
  place-items: center;
}

.tabbar__glyph svg {
  width: 22px;
  height: 22px;
}

.tabbar__label {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1px;
}

/* Raised center record button — always pink, nested into the cradle with its top
 * rising above the bar's top edge. The curved bar frames it, so NO border (a
 * hardcoded ring previously read as an ugly dark notch on light theme). */
.tabbar__rec {
  flex: 0 0 auto;
  position: relative;
  z-index: 1; /* above the SVG surface */
  top: -22px;
  display: grid;
  place-items: center;
  width: 67px; /* +20% over the original 56px; cradle notch widened to match */
  height: 67px;
  /* Thin page-background ring so the pink button never dissolves into a pink
   * surface behind it (e.g. the gist card on scroll). Theme-aware via var(--bg):
   * light-on-light, dark-on-dark — barely visible, just an edge separator. With
   * box-sizing:border-box the size is unchanged, and the cradle/notch shape
   * is NOT touched — this ring lives only on the button circle. */
  border: 1px solid var(--bg);
  border-radius: 50%;
  background: var(--pink);
  color: #fff;
  /* Tight subtle halo — barely past the button edge, no big bloom below. */
  box-shadow: 0 2px 8px rgba(255, 46, 116, 0.45);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.tabbar__recglyph {
  display: grid;
  place-items: center;
}

.tabbar__rec svg {
  width: 30px;
  height: 30px;
}

/* Content clearance: when the bar is shown, pad scrollable views so the last
 * card/content isn't covered. Hidden-bar routes get no extra padding. */
body.has-tabbar .memos,
body.has-tabbar .home,
body.has-tabbar .summary,
body.has-tabbar .transcript,
body.has-tabbar .tasks,
body.has-tabbar .schedule,
body.has-tabbar .settings,
body.has-tabbar .about {
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 24px);
}

/* Schedule additionally clears its FLOATING add pill (30px offset + ~44px
 * pill + 14px daylight), so the last meeting can always scroll out from
 * behind it — 88px also fully clears the 72px bottom dissolve, so the last
 * row is readable, not just reachable. */
body.has-tabbar .schedule {
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 88px);
}

/* ============================================================
 * Auth screen (#/auth) — pre-login, always dark via data-immersive
 * ============================================================ */
.auth {
  position: relative;
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: calc(env(safe-area-inset-top, 0px) + 32px) 24px
    calc(env(safe-area-inset-bottom, 0px) + 32px);
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}

/* Ambient pink glow at the top (matches the recording screen language). */
.auth__glow {
  position: absolute;
  top: -150px;
  left: 50%;
  transform: translateX(-50%);
  width: 520px;
  height: 360px;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--pink) 30%, transparent),
    transparent 72%
  );
  pointer-events: none;
  z-index: 0;
}

.auth__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Pink mic orb with the emoji mic glyph. */
.auth__orb {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--pink);
  box-shadow: 0 5px 15px rgba(255, 46, 116, 0.53);
  display: grid;
  place-items: center;
  font-size: 34px;
  line-height: 1;
}

.auth__heading {
  margin-top: 22px;
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.4px;
  text-align: center;
}

.auth__sub {
  margin-top: 8px;
  font-size: 14.5px;
  color: var(--text-secondary);
  text-align: center;
}

/* Log in | Sign up toggle pill. */
.auth__toggle {
  display: flex;
  gap: 6px;
  width: 100%;
  margin-top: 24px;
  padding: 5px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
}

.auth__tab {
  flex: 1;
  padding: 10px 6px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-tertiary);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.auth__tab.is-selected {
  /* Raised dark pill, white text. */
  background: var(--card);
  color: #fff;
  box-shadow: var(--shadow-card);
}

.auth__body {
  width: 100%;
}

.auth__form {
  width: 100%;
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.auth__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.auth__label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-secondary);
}

.auth__input {
  height: 48px;
  padding: 0 14px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  outline: none;
  transition: border-color 150ms ease;
}

.auth__input::placeholder {
  color: var(--text-tertiary);
}

.auth__input:focus {
  border-color: var(--pink-line);
}

/* Password field + in-field show/hide eye toggle. */
.auth__pwwrap {
  position: relative;
  display: flex;
}

/* Room so typed text doesn't slide under the eye (field box size unchanged). */
.auth__input--pw {
  flex: 1;
  padding-right: 44px;
}

.auth__eye {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  /* ALWAYS pink, both states (fix/yearly-window-and-upgrade-polish): the old
     gray-until-shown read as a disabled control. Only the glyph SHAPE (eye vs
     eye-off, swapped in auth.js) distinguishes hidden from shown. */
  color: var(--pink-text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.auth__eye svg {
  width: 20px;
  height: 20px;
}

.auth__forgot {
  align-self: flex-end;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pink-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.auth__error {
  min-height: 17px;
  margin: 2px 0 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--pink-text);
  text-align: center;
}

.auth__error.is-ok {
  color: var(--text-secondary);
}

.auth__btn {
  height: 50px;
  border: 0;
  border-radius: 7px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.auth__btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.auth__terms {
  margin-top: 14px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-tertiary);
  text-align: center;
}

.auth__terms a {
  color: var(--text-secondary);
}

/* --- Settings: account / log out --- */
/* Legal rows (Terms / Privacy) — same card-row rhythm as the switches. */
.legal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 13px 0;
  border-top: 1px solid var(--line);
  font-size: 15px;
  color: var(--text);
  text-decoration: none;
}

.legal-row:first-child {
  border-top: 0;
}

.legal-row__chev {
  color: var(--text-tertiary);
  font-size: 18px;
  line-height: 1;
}

/* (The transcript search overlay's positioning context is the topbar itself:
 * position:sticky establishes a containing block for the absolute
 * .search--pop, so no extra rule is needed. A v97 `position: relative` here
 * silently overrode the shared sticky rule — same specificity, later in the
 * file — and unstuck the transcript header; removed when the docked audio
 * player made the sticky header + player stack load-bearing.) */

/* Legal long-form pages (#/terms, #/privacy) */
.legal {
  min-height: 100vh; /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: 100dvh;
  padding: 14px 20px calc(48px + env(safe-area-inset-bottom, 0px));
}

/* ============================================================
 * Paste text (#/paste) — create a memo from pasted text
 * ============================================================ */

.paste {
  min-height: 100vh; /* same dvh fallback as .legal */
  min-height: 100dvh;
  padding: 14px 20px calc(24px + env(safe-area-inset-bottom, 0px));
}

/* The paste field: the inline-edit contract's textarea treatment on a
 * standalone card — 1.5px border that turns --pink on focus (.iedit's border
 * value), --r-summary like the editor box, pink caret. Autogrow in JS. */
.paste__field {
  display: block;
  width: 100%;
  min-height: 240px;
  padding: 12px 14px;
  border: 1.5px solid var(--line);
  border-radius: var(--r-summary);
  background: var(--card);
  color: var(--text);
  font: inherit;
  line-height: 1.5;
  caret-color: var(--pink);
  resize: none;
  outline: none;
  overflow: hidden;
}

.paste__field:focus {
  border-color: var(--pink);
}

.paste__field::placeholder {
  color: var(--text-tertiary);
}

/* Create rides the house .pill; enabled adds .is-on (pink-soft primary-lite),
 * disabled reads quiet — same non-committal dimming the iosswitch skeleton
 * uses, never a color that could be misread as a second state. */
.paste__create:disabled {
  opacity: 0.45;
  cursor: default;
}

.paste__caption {
  margin: 10px 4px 0;
}

.legal__title {
  margin: 6px 0 14px;
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.6px;
  color: var(--text);
}

.legal__body {
  max-width: 640px;
}

.legal__body p {
  margin: 0 0 14px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text-secondary);
}

/* Long-form document constructs (generated from docs/legal/*.md).
 * Sizes come from the app's existing scale: section headings at the 17px/700
 * card-heading tier — hierarchical under the 26px page title, not shouty. */
.legal__body h2 {
  margin: 4px 0 10px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.2px;
  color: var(--text);
}

/* Bold runs inside secondary body text step up to full --text so emphasis
 * reads in BOTH themes (dark's secondary is translucent white). */
.legal__body strong {
  font-weight: 700;
  color: var(--text);
}

.legal__body ul {
  margin: 0 0 14px;
  padding-left: 22px;
}

.legal__body li {
  margin: 0 0 8px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.legal__body li::marker {
  color: var(--text-tertiary);
}

/* Section separators from the documents' "---" rules — the house hairline. */
.legal__body hr {
  height: 1px;
  margin: 20px 0;
  border: 0;
  background: var(--line);
}

/* The support email (mailto:) — tappable, brand-colored in both themes. */
.legal__body a {
  color: var(--pink-text);
  font-weight: 600;
  text-decoration: none;
}

/* --- About page (#/settings/about) — rides the .legal shell above. --- */
/* The app icon's own artwork is a dark square, so the hairline gives it an
 * edge against dark --bg (invisible-in-practice on light, by design). */
.about__mark {
  display: block;
  width: 56px;
  height: 56px;
  margin: 6px 0 18px;
  border-radius: var(--r-list);
  border: 1px solid var(--line);
}

/* Byline under the divider — the quiet 13px secondary tier
 * (.switch-row__sub / .settings-account). Scoped under .legal__body to
 * outweigh its 15px paragraph rule. */
.legal__body .about__byline {
  font-size: 13px;
  color: var(--text-secondary);
}

.settings-greet {
  margin: 0 0 2px;
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
}

.settings-account {
  margin: 0 0 10px;
  font-size: 13.5px;
  color: var(--text-secondary);
}

/* App/build version footer — glanceable, unobtrusive. */
.settings-version {
  margin: 28px 0 0;
  text-align: center;
  font-size: 12px;
  color: var(--text-tertiary);
}

/* Change email — the account-identity action that sits with the session
 * exits: the same quiet text-link row as Delete account, in brand pink
 * (nothing here is destructive — red stays reserved), above Log out so the
 * exits keep the bottom. Keep the two rows' values in sync. */
.change-email-row {
  display: block;
  width: 100%;
  margin-bottom: 14px;
  padding: 8px 0;
  border: 0;
  background: none;
  color: var(--pink-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}

/* Log out: solid pink primary, matching the auth submit / dashboard Schedule
 * button family (pink fill, white bold text, 12px radius, soft pink glow). */
.logout-row {
  width: 100%;
  height: 52px;
  border: 0;
  border-radius: 12px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(255, 46, 116, 0.35);
}

/* Destructive account deletion — a quiet RED text link (--red-text, never
 * brand pink), no button box, spaced well away from Log out so the two can't
 * be mis-tapped. */
.delete-account-row {
  display: block;
  width: 100%;
  margin-top: 26px;
  padding: 8px 0;
  border: 0;
  background: none;
  color: var(--red-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}

/* ============================================================
 * Tags — chips, picker (summary), filter bar (home), settings
 * ============================================================ */

/* --- Tag chips (shared base) --- */
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 28px;
  padding: 0 11px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 13px;
  font-weight: 600;
}

.tag-chip__label {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* Compact, read-only variant on memo cards. */
.tag-chip--mini {
  height: 22px;
  padding: 0 9px;
  font-size: 12px;
}

/* --- Summary: tags row + "+ Add tag" + picker popover --- */
.tags-row {
  position: relative;
  margin-top: 4px;
}

.tags-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.tag-add {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 13px;
  border: 1px dashed var(--pink-line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--pink-text);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}

.tagpicker {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 60;
  width: 244px;
  max-width: 86vw;
  padding: 8px;
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.18);
}

.tagpicker__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 240px;
  overflow-y: auto;
}

.tagpicker__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  height: 38px;
  padding: 0 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.tagpicker__opt:hover {
  background: var(--line);
}

.tagpicker__opt.is-sel {
  background: var(--pink-soft);
  color: var(--pink-text);
}

.tagpicker__optcheck {
  display: inline-flex;
}

.tagpicker__optcheck svg {
  width: 16px;
  height: 16px;
}

.tagpicker__empty {
  padding: 10px;
  font-size: 13.5px;
  color: var(--text-secondary);
}

.tagpicker__foot {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line);
}

.tagpicker__newbtn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 38px;
  padding: 0 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--pink-text);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.tagpicker__newbtn:hover {
  background: var(--line);
}

/* "Edit tag names…" — routes to Settings → Tags (global rename lives there). */
.tagpicker__manage {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line);
}

.tagpicker__editlink {
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.tagpicker__editlink:hover {
  background: var(--line);
}

.tagpicker__newbtn svg {
  width: 16px;
  height: 16px;
}

.tagpicker__input {
  width: 100%;
  box-sizing: border-box;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--pink-line);
  border-radius: 5px;
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 14.5px;
}

.tagpicker__input:focus {
  outline: none;
  border-color: var(--pink);
}

/* --- Home: filter bar --- */
/* Filter row: the chip bar plus the expanding search overlay share one
 * positioning context so the field can grow OVER the chips without any
 * reflow underneath. */
.filterrow {
  position: relative;
  margin-top: 16px;
}

.filterrow .filterbar {
  margin-top: 0;
}

.filterbar {
  position: relative; /* positioning context for the Tags popover (content width) */
  display: flex;
  gap: 8px;
  margin-top: 12px;
  /* Four compact controls (Search icon / All / Tags / Pinned) — never
   * overflows (see the narrow-width squeeze below), so keep overflow visible
   * to let the Tags popover escape the bar. */
  overflow: visible;
}

/* Search collapsed to a BARE icon, flush right (margin-left:auto) — no pill
 * fill or border, just the glyph at 21px so it reads as row furniture next
 * to the 34px pills. The button itself is 44x44 (vertical overhang absorbed
 * by negative margins so the 34px row height is unchanged) to keep a full
 * tap target. Active state = pink glyph, the containerless analogue of the
 * chips' pink fill. */
.filterbar__search {
  flex: 0 0 auto;
  margin-left: auto;
  margin-top: -5px;
  margin-bottom: -5px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 150ms ease;
}

.filterbar__search svg {
  width: 21px;
  height: 21px;
}

.filterbar__search.is-active {
  color: var(--pink-text);
}

/* Expanded search: overlays the chip row at chip height, RIGHT-anchored to
 * match the icon's flush-right position — it grows leftward from the icon's
 * footprint until it covers the full row (icon, input, and X all inside the
 * one label, so everything stays reachable at any width). Hidden state keeps
 * pointer-events off so the chips underneath stay tappable. */
.search--pop {
  position: absolute;
  top: 0;
  right: 0;
  left: auto;
  height: 34px;
  width: 44px;
  margin-top: 0;
  padding: 0 11px;
  border-radius: var(--r-pill);
  gap: 7px;
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  transition: width 220ms ease, opacity 150ms ease;
}

.search--pop.is-open {
  width: 100%;
  opacity: 1;
  pointer-events: auto;
}

.search--pop .search__icon svg {
  width: 15px;
  height: 15px;
}

.search--pop .search__input {
  font-size: 14px;
}

.search__close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-tertiary);
  cursor: pointer;
}

.search__close svg {
  width: 14px;
  height: 14px;
}

/* Narrow-iPhone squeeze (320pt SE class): three chips plus the flush-right
 * 44px search icon measure ~306px against 280px of content at the default
 * sizes — tighten the chips only under 360px (≈271px total, ~9px slack) so
 * everything else keeps the standard look. The icon's 44pt tap target is
 * never shrunk. */
@media (max-width: 359px) {
  .filterbar {
    gap: 6px;
  }
  .filter-chip {
    padding: 0 10px;
    font-size: 12.5px;
  }
}

.filter-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.filter-chip svg {
  width: 14px;
  height: 14px;
}

.filter-chip.is-active {
  background: var(--pink);
  border-color: transparent;
  color: #fff;
}

/* --- Tags pill + popover (collapses all per-tag chips) --- */
.filter-tags {
  /* Positioning context for the popover, which drops directly beneath the Tags
   * pill with their LEFT edges aligned (left:0), extending right. JS clamps the
   * width so its right edge never passes the content/card right edge. */
  position: relative;
  flex: 0 0 auto;
}

/* The chevron (last svg) flips when the popover is open. */
.filter-tags.is-open .filter-chip--tags svg:last-child {
  transform: rotate(180deg);
}

.filter-chip--tags span {
  max-width: 42vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Drops directly beneath the Tags pill: LEFT edges aligned (left:0, relative to
 * .filter-tags), extending downward and to the right. Width is content-driven up
 * to a cap: min-width keeps short tag lists looking like before; the JS clamp
 * (clampTagsPopover) caps max-width so the right edge lands at the content/card
 * edge — never the device edge. The CSS max-width (content width) is a defensive
 * upper bound for the instant before the JS clamp runs. Long tags wrap (below).
 * Reuses .tagmenu styling from the Settings tag menu. */
.filter-tagmenu {
  left: 0;
  right: auto;
  width: max-content;
  min-width: 200px;
  max-width: calc(100vw - 40px);
  padding: 8px;
}

.tagmenu__header {
  margin: 0;
  padding: 4px 10px 8px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.tagmenu__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 264px;
  overflow-y: auto;
}

.filter-tagmenu .tagmenu__item {
  justify-content: space-between;
  /* Grow if a tag longer than the max width has to wrap to two lines. */
  min-height: 38px;
  height: auto;
  padding-top: 6px;
  padding-bottom: 6px;
}

.filter-tagmenu .tagmenu__item.is-sel {
  color: var(--pink-text);
}

.tagmenu__itemname {
  /* No hard truncation: within the popover's allowed width the name fits on one
   * line (the box grows to it); only a name longer than the max width wraps. */
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}

.tagmenu__itemcheck {
  display: inline-flex;
  flex: 0 0 auto;
}

.filter-tagmenu .tagmenu__itemcheck svg {
  width: 16px;
  height: 16px;
  color: var(--pink-text);
}

.tagmenu__empty {
  margin: 0;
  padding: 8px 10px;
  font-size: 13.5px;
  color: var(--text-secondary);
}
.tagmenu__empty--error {
  color: var(--red-text);
}

/* --- Home: memo-card tags + pin indicator --- */
.memo-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}

.memo-card__pinmark {
  display: inline-flex;
  vertical-align: -2px;
  margin-right: 5px;
  color: var(--pink-text);
}

.memo-card__pinmark svg {
  width: 13px;
  height: 13px;
}

/* --- Settings: tag management --- */
.tags-manage {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 0;
}

.tags-empty {
  padding: 8px 0;
  font-size: 14px;
  color: var(--text-secondary);
}
.tags-empty--error {
  color: var(--red-text);
}

/* --- Transcript: inline rename notice --- */
.transcript__notice {
  display: none;
}

.transcript__notice.is-on {
  display: block;
  margin-bottom: 10px;
  padding: 10px 14px;
  border-radius: var(--r-list);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 13.5px;
  font-weight: 600;
  text-align: center;
}

/* --- Settings: tag rename (inline input) + notice --- */
.tag-chip--editing {
  padding: 0;
  background: transparent;
}

.tag-chip__input {
  height: 28px;
  width: 130px;
  max-width: 60vw;
  box-sizing: border-box;
  padding: 0 11px;
  border: 1px solid var(--pink-line);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
}

.tag-chip__input:focus {
  outline: none;
  border-color: var(--pink);
}

.tags-notice,
.lang-notice {
  display: none;
}

.lang-notice.is-on,
.tags-notice.is-on {
  display: block;
  margin: 8px 2px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--pink-text);
}

/* --- Settings: tap-first tag manage (chip + action menu) --- */
.tag-chip-wrap {
  position: relative;
  display: inline-flex;
}

.tag-chip--tappable {
  border: 0;
  font-family: inherit;
  cursor: pointer;
}

.tag-chip--tappable.is-selected {
  background: var(--pink);
  color: #fff;
}

.tagmenu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 60;
  min-width: 150px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: 7px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.18);
}

.tagmenu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.tagmenu__item svg {
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
}

.tagmenu__item:hover {
  background: var(--line);
}

/* Delete sits below Rename, visually separated, in the destructive color. */
.tagmenu__item--danger {
  margin-top: 2px;
  padding-top: 2px;
  border-top: 1px solid var(--line);
}

.tagmenu__item--danger,
.tagmenu__item--danger svg {
  color: var(--red-text);
}

/* ============================================================
 * PASS 2 — AI tag suggestions + speaker-name guesses (Summary)
 * ============================================================ */

/* Subtle caption under the tag row when AI auto-applied existing tags. */
.tags-applied-note {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 8px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-secondary);
}

.tags-applied-note svg {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  color: var(--pink-text);
}

/* Suggested NEW tag cards. */
.ai-suggest {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 16px 0 4px;
}

.suggest-card {
  padding: 12px 14px;
  border: 1px solid var(--pink-line);
  border-radius: var(--r-list);
  background: var(--pink-soft);
}

[data-theme="dark"] .suggest-card,
[data-immersive="dark"] .suggest-card {
  background: color-mix(in srgb, var(--pink) 10%, transparent);
}

.suggest-card__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--pink-text);
}

.suggest-card__eyebrow svg {
  width: 13px;
  height: 13px;
}

.suggest-card__row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}

.tag-chip--dashed {
  background: transparent;
  border: 1px dashed var(--pink-line);
}

.suggest-card__input {
  flex: 1;
  min-width: 0;
  height: 30px;
  padding: 0 11px;
  border: 1px solid var(--pink-line);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
}

.suggest-card__input:focus {
  outline: none;
  border-color: var(--pink);
}

.suggest-card__actions {
  display: flex;
  gap: 6px;
  margin-left: auto;
}

/* Small round action buttons reused by tag + speaker suggestions. */
.suggest-act {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  color: var(--text-secondary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.suggest-act svg {
  width: 17px;
  height: 17px;
}

.suggest-act:active {
  background: var(--card-2);
}

.suggest-act--approve {
  background: var(--pink);
  border-color: transparent;
  color: #fff;
}

.suggest-card__reason {
  margin-top: 8px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--text-secondary);
}

/* Attendee row with an AI speaker-name guess. */
.att-row--guess {
  align-items: flex-start;
}

.att-guess {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}

.att-guess__line {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

.att-guess__badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2px;
}

.att-guess__badge svg {
  width: 12px;
  height: 12px;
}

.att-guess__name {
  font-size: 15px;
  font-weight: 700;
}

.att-guess__reason {
  font-size: 12px;
  color: var(--text-tertiary);
}

.att-guess__actions {
  display: flex;
  gap: 6px;
  margin-left: auto;
  flex: 0 0 auto;
}

/* ============================================================
   Subscription (Settings) — usage panel, plan row, soft-cap warning
   ============================================================ */

/* The usage panel is a PANEL inside the settings card, not a plain row. */
.usage-panel {
  padding: 14px 0 12px;
  border-bottom: 1px solid var(--line);
}

.usage-panel__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.usage-panel__label {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.usage-panel__value {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}

.usage-panel__of {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-tertiary);
}

.usage-panel__bar {
  height: 5px;
  margin-top: 10px;
  border-radius: var(--r-pill);
  background: var(--line);
  overflow: hidden;
}

.usage-panel__fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--pink);
}

.usage-panel__resets {
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-tertiary);
}

/* Soft-cap warning: the caption slot, in the pink-soft block treatment.
   Copy is written so there is never any doubt recordings keep being saved. */
.usage-warning {
  margin: 10px 0 0;
  padding: 12px 14px;
  border-radius: var(--r-list);
  background: var(--pink-soft);
  color: var(--pink-text);
  font-size: 13px;
  line-height: 1.45;
  font-weight: 600;
}

/* ============================================================
   AI-accuracy note (memo Summary) — the permanent one-liner.
   (The dismissible notice reuses the .edit-hint treatment.)
   ============================================================ */

.summary__ainote {
  margin: 14px 4px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-tertiary);
  text-align: center;
}

/* ============================================================
   Paywall (#/paywall) — ALWAYS DARK (data-immersive="dark", like Auth).
   Tokens resolve to the dark set regardless of the user's theme.
   ============================================================ */

.paywall {
  position: relative;
  /* NO double safe-area (v131 — the v105 Schedule lesson, again): #app
   * already carries the safe-area insets as padding on top of its own
   * 100dvh, so a plain 100dvh here overflowed the viewport by exactly the
   * inset amount and guaranteed dead scroll on any notched phone. Subtract
   * the insets; min-height (not height) lets the 320pt SE — the one size
   * the content genuinely cannot fit — degrade to natural scroll. */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  display: flex;
  background: var(--bg);
  color: var(--text);
}

/* margin:auto centers the composition in the available space. Padding adds
   NO safe-area (#app provides it — the second half of the v131 fix; the old
   +safe-area here pushed everything bottom-heavy). The larger bottom pad is
   the reserved TOAST BAND: the centered stack sits slightly high and toasts
   land below "Back to my memos" without covering any control. */
.paywall__inner {
  margin: auto;
  width: 100%;
  max-width: 420px;
  padding: 28px 24px 88px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.paywall__orb {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink);
}

.paywall__orb svg {
  width: 26px;
  height: 26px;
}

.paywall__heading {
  margin-top: 16px;
  font-size: 23px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

.paywall__sub {
  margin-top: 6px;
  font-size: 14.5px;
  color: var(--text-secondary);
}

.paywall__benefits {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  width: 100%;
  display: grid;
  gap: 12px;
}

.paywall__benefit {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.paywall__check {
  flex: 0 0 auto;
  color: var(--pink);
  margin-top: 1px;
}

.paywall__check svg {
  width: 18px;
  height: 18px;
}

.paywall__plans {
  width: 100%;
  margin-top: 22px;
  display: grid;
  gap: 10px;
}

.paywall__plan {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  border-radius: var(--r-summary);
  background: var(--card);
  border: 1.5px solid var(--line);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.paywall__plan.is-selected {
  background: var(--pink-soft);
  border-color: var(--pink);
}

/* The Lite tile (feature/lite-tier): visually SECONDARY — the --card-2 +
   hairline treatment the sharesheet/confirm secondary buttons already use,
   tighter padding, lighter name weight. Selection keeps the house
   --pink-soft state so "chosen" reads identically across tiles. */
.paywall__plan--lite {
  background: var(--card-2);
  border: 1px solid var(--line);
  padding: 11px 16px;
}
.paywall__plan--lite .paywall__plan-name {
  font-size: 14px;
  font-weight: 600;
}
.paywall__plan-note {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-secondary);
}
.paywall__plan--lite.is-selected {
  background: var(--pink-soft);
  border-color: var(--pink);
}

.paywall__plan-name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 700;
}

.paywall__badge {
  padding: 2px 7px;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.6px;
}

.paywall__plan-price {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

.paywall__plan.is-selected .paywall__plan-price {
  color: var(--pink-text);
}

.paywall__cta {
  width: 100%;
  margin-top: 18px;
  height: 50px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

/* Guideline 3.1.2 auto-renewal disclosure (v144) — between the CTA and the
 * Restore/Terms/Privacy row; copy updates with the selected plan. */
.paywall__disclosure {
  margin: 12px 4px 0;
  max-width: 330px;
  font-size: 11px;
  line-height: 1.55;
  text-align: center;
  color: var(--text-tertiary);
}

/* Server-refused purchase (v157): a dead end the user must read and act on,
   so it persists on the screen instead of passing through a toast. Red =
   error state per the destructive-color rule. */
.paywall__notice {
  margin: 14px 0 0;
  max-width: 330px;
  padding: 12px 14px;
  border-radius: var(--r-list);
  background: var(--red-soft);
  text-align: left;
}

.paywall__notice-text {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--red-text);
}

.paywall__notice-support {
  margin-top: 8px;
  padding: 0;
  border: 0;
  background: none;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--red-text);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.paywall__links {
  margin-top: 16px;
  display: flex;
  gap: 22px;
}

.paywall__link {
  border: 0;
  padding: 0;
  background: transparent;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-decoration: none;
  cursor: pointer;
}

/* Escape hatch for a LAPSED user (only rendered when they have memos to
   return to) — a RETURN PATH, so it wears the app's standard secondary
   interactive affordance: the hairline pill (the same language as every
   topbar back pill) with a back chevron. Unmistakably tappable, visually
   quiet — card-on-dark with a hairline competes with neither the pink CTA
   nor the tertiary links above it (v131; the old bare 13.5px caption read
   as un-tappable text). */
.paywall__notnow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 20px;
  height: 38px;
  padding: 0 16px;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.paywall__notnow svg {
  width: 15px;
  height: 15px;
  color: var(--text-secondary);
}

/* ============================================================
   AI-processing consent (#/consent + the pre-action/Settings sheets).
   Guidelines 5.1.1/5.1.2 — the disclosure that names AssemblyAI and Claude
   (Anthropic) and ends in an explicit agree-or-skip choice. Theme-following;
   layout follows the paywall's centered-stack conventions (same v131
   safe-area math, same orb/CTA/quiet-exit tiers). The vendor cards and fine
   print are SHARED with the two sheets (js/consent.js disclosureCardsHtml),
   so the .consent__card/__fine classes must render correctly both on the
   full screen and inside a .sharesheet__panel.
   ============================================================ */

.consent {
  position: relative;
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  display: flex;
  background: var(--bg);
  color: var(--text);
}

.consent__inner {
  margin: auto;
  width: 100%;
  max-width: 420px;
  padding: 28px 24px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.consent__orb {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink);
}

.consent__orb svg {
  width: 26px;
  height: 26px;
}

.consent__orb--sheet {
  margin: 2px auto 10px;
}

.consent__heading {
  margin-top: 16px;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.3px;
  text-align: center;
}

.consent__lede {
  margin-top: 8px;
  max-width: 330px;
  font-size: 14.5px;
  line-height: 1.5;
  text-align: center;
  color: var(--text-secondary);
}

.consent__card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  margin-top: 12px;
  padding: 14px 16px;
  border-radius: var(--r-summary);
  background: var(--card);
  border: 1px solid var(--line);
  text-align: left;
}

.consent__card:first-of-type {
  margin-top: 20px;
}

.consent__glyph {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink-text);
}

.consent__glyph svg {
  width: 18px;
  height: 18px;
}

.consent__vendor {
  display: block;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.consent__what {
  margin: 4px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.consent__what strong {
  color: var(--text);
  font-weight: 700;
}

.consent__fine {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 14px 2px 0;
  font-size: 12px;
  line-height: 1.55;
  text-align: left;
  color: var(--text-tertiary);
}

.consent__lockglyph {
  flex: 0 0 auto;
  margin-top: 1px;
}

.consent__lockglyph svg {
  width: 14px;
  height: 14px;
}

.consent__cta {
  width: 100%;
  margin-top: 18px;
  height: 50px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.consent__policy {
  display: inline-block;
  margin-top: 16px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--pink-text);
  text-decoration: none;
  text-align: center;
  cursor: pointer;
}

.consent__notnow {
  margin-top: 14px;
  border: 0;
  padding: 6px 10px;
  background: transparent;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-tertiary);
  cursor: pointer;
}

/* The shared agree / policy / not-now stack (v159, consentActionsHtml in
   js/consent.js): its own flex column so the three actions stack on EVERY
   surface — inside the sheet's plain block panel, loose inline-blocks flowed
   the Agree pill and the policy link onto one crowded row (device-found).
   On the full screen the wrapper is spacing-neutral: the children keep their
   own margins and the column centers exactly like .consent__inner did. */
.consent__actions {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Sheet variants (pre-action gate + Settings): the shared cards sit inside a
   .sharesheet__panel, whose background is the sheet surface — the card tier
   still reads against it in both themes via --card + --line. */
.consentsheet {
  text-align: center;
}

.consentsheet .consent__card {
  text-align: left;
}

.consentsheet .consent__fine {
  text-align: left;
}

/* The lede is a max-width block; the sheet panel is a plain block (unlike the
   full screen's centering flex column), so it centers itself here. v159: the
   sheets used .sharesheet__sub for this line, whose single-line ellipsis —
   correct for the share sheet's arbitrary meeting title — truncated the
   disclosure lede on device. The consent surfaces now all use .consent__lede,
   which wraps to as many lines as the copy needs; .sharesheet__sub keeps its
   clamp for share.js. */
.consentsheet .consent__lede {
  margin-left: auto;
  margin-right: auto;
}

.consentsheet__switch {
  margin-top: 14px;
  padding: 12px 14px;
  border-top: 0; /* .switch-row's hairline reads wrong on a filled row */
  border-radius: var(--r-list);
  background: var(--card-2);
}

/* The Settings PRIVACY row reuses the two-line text stack inside a button —
   keep it left-aligned (a button's default centering would drift the sub). */
.lang-row .switch-row__text {
  text-align: left;
}

.consentsheet__note {
  margin: 10px 2px 0;
  font-size: 12px;
  line-height: 1.55;
  text-align: left;
  color: var(--text-tertiary);
}

/* ============================================================
   App lock (v132) — the optional Face ID / Touch ID overlay.
   Pure DOM above every view and the tab bar; native capture and
   notifications run untouched beneath it. Theme-aware via tokens.
   ============================================================ */

.applock {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--bg);
  color: var(--text);
  padding: calc(env(safe-area-inset-top, 0px) + 18px) 28px
    calc(env(safe-area-inset-bottom, 0px) + 24px);
}

.applock__top {
  align-self: flex-start;
}

.applock__inner {
  margin: auto;
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* 68px circle per the approved returning-user design (v133). */
.applock__glyph {
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  border-radius: var(--r-pill);
  background: var(--pink-soft);
  color: var(--pink);
}

.applock__glyph svg {
  width: 34px;
  height: 34px;
}

.applock__hello {
  margin-top: 16px;
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -0.3px;
}

/* The user's NAME (email only as the no-name fallback); long values ellipsize. */
.applock__who {
  margin-top: 4px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 15px;
  color: var(--text-secondary);
}

.applock__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 24px;
  height: 50px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.applock__btn svg {
  width: 20px;
  height: 20px;
}

.applock__pw {
  margin-top: 16px;
  border: 0;
  padding: 8px 12px;
  background: transparent;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
}

/* ---- Login checkboxes (v133): Remember me + Use Face ID ---- */

.auth__opts {
  display: flex;
  gap: 22px;
  margin: 2px 2px 0;
}

.authcheck {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.authcheck__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.authcheck__box {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  border: 1.5px solid var(--text-tertiary);
  color: transparent;
}

.authcheck__box svg {
  width: 13px;
  height: 13px;
}

.authcheck__input:checked ~ .authcheck__box {
  background: var(--pink);
  border-color: var(--pink);
  color: #fff;
}

.authcheck__label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* The one quiet line explaining the Remember-me dependency. */
.auth__optnote {
  margin: 8px 2px 0;
  font-size: 12px;
  color: var(--text-tertiary);
}

/* ---- Account name rows (v133; First/Last since v136): tap a value to edit
   in place. Stacked rows separate with the settings-card hairline. ---- */

.acct-name + .acct-name {
  border-top: 1px solid var(--line);
}

.acct-name {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 6px 0;
}

.acct-name__label {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.acct-name__value {
  border: 0;
  padding: 6px 0;
  background: transparent;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  text-align: right;
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

/* No name yet: the value slot reads as an affordance, pink like the app's
   other add-actions. */
.acct-name__value--empty {
  color: var(--pink-text);
}

/* v135: editing swaps the value button for a SINGLE-LINE name editor — the
   inline editor's visual language (pink box, round × / ✓) sized for one short
   value, with the controls beside the text instead of on a row below. The
   placeholder states what's wanted. Reuses .iedit__btn for the buttons. */
.nameedit {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 6px;
  min-width: 0;
  border: 1.5px solid var(--pink);
  border-radius: var(--r-summary);
  padding: 4px 4px 4px 10px;
}

.nameedit__field {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 4px 0;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  caret-color: var(--pink);
  outline: none;
}

.nameedit__field::placeholder {
  color: var(--text-tertiary);
  font-weight: 500;
}

/* Boot splash (v133): the pre-router wordmark inside #app. Fixed dark
   backdrop regardless of theme — it matches the native splash + webview
   background (#0B0B0D) so launch is one continuous surface; the wordmark
   colors are the brand pair that reads on dark. */
.boot-splash {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #0b0b0d;
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -0.3px;
}

.boot-splash__slash {
  color: rgba(235, 235, 245, 0.34);
}

.boot-splash__name {
  color: #ff2e74;
}

/* ============================================================
 * Report a problem sheet (v140) — 6th openBottomSheet rider.
 *
 * Field/label/checkbox/button treatments MIRROR the schedule sheet's tiers
 * (.schedsheet__label / __input / __save) and the auth checkbox — mirrored
 * rather than reused by class, per the .notif-pill precedent, so a schedule
 * redesign can't silently restyle this sheet. Keep the values in sync.
 * ============================================================ */

.reportsheet {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 4px 6px;
}

.reportsheet__caption {
  margin: -6px 0 4px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* Eyebrow — mirrors .schedsheet__label. */
.reportsheet__label {
  margin-left: 4px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* Multi-line sibling of .schedsheet__input (same form-field treatment). */
.reportsheet__text {
  box-sizing: border-box;
  width: 100%;
  min-height: 96px;
  padding: 12px 14px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  line-height: 1.45;
  outline: none;
  resize: none;
  transition: border-color 150ms ease;
}

.reportsheet__text::placeholder {
  color: var(--text-tertiary);
}

.reportsheet__text:focus {
  border-color: var(--pink-line);
}

/* Checkbox row on the same form-field surface as the text area. */
.reportsheet__attachbox {
  padding: 12px 14px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
}

.reportsheet__attach.is-disabled {
  opacity: 0.55;
  cursor: default;
}

.reportsheet__attachsub {
  margin: 6px 0 0 28px; /* aligns under the checkbox label (20px box + 8px gap) */
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* "Also included" disclosure — quiet text row, chevron rotates when open. */
.reportsheet__disclose {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  align-self: flex-start;
  padding: 4px 4px;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.reportsheet__chev {
  display: inline-block;
  transition: transform 150ms ease;
  color: var(--text-tertiary);
}

.reportsheet__disclose.is-open .reportsheet__chev {
  transform: rotate(90deg);
}

.reportsheet__included {
  margin: 0 0 2px;
  padding-left: 22px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.reportsheet__notice {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* Primary — mirrors .schedsheet__save. */
.reportsheet__send {
  width: 100%;
  height: 52px;
  border: none;
  border-radius: 12px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(255, 46, 116, 0.35);
  transition: opacity 150ms ease;
}

/* Disabled until the description has real text (v141) — visibly inert and
   self-explaining (the label reads "Describe the problem first"), same
   dimmed treatment as the app's other gated primaries. */
.reportsheet__send:disabled {
  opacity: 0.45;
  cursor: default;
  box-shadow: none;
}

/* ============================================================
 * Change email sheet — openBottomSheet rider (Settings).
 *
 * Field/label/error/button treatments MIRROR the schedule sheet's tiers
 * (.schedsheet__label / __input / __err / __save), mirrored rather than
 * reused by class per the .reportsheet precedent, so a schedule redesign
 * can't silently restyle this sheet. Keep the values in sync.
 * ============================================================ */

.emailsheet {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 16px;
}

.emailsheet__caption {
  margin: -6px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.emailsheet__field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Eyebrow — mirrors .schedsheet__label. */
.emailsheet__label {
  margin-left: 4px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* Form field — mirrors .schedsheet__input (.auth__input tier). */
.emailsheet__input {
  box-sizing: border-box;
  height: 48px;
  padding: 0 14px;
  border-radius: 7px;
  background: var(--card-2);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  outline: none;
  transition: border-color 150ms ease;
}

.emailsheet__input::placeholder {
  color: var(--text-tertiary);
}

.emailsheet__input:focus {
  border-color: var(--pink-line);
}

/* Inline validation/failure line — mirrors .schedsheet__err. */
.emailsheet__err {
  margin: -4px 4px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--red-text);
}

/* Primary — mirrors .schedsheet__save; disabled state mirrors
 * .reportsheet__send:disabled (inert until both fields have text). */
.emailsheet__confirm {
  width: 100%;
  height: 52px;
  border: none;
  border-radius: 12px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(255, 46, 116, 0.35);
  transition: opacity 150ms ease;
}

.emailsheet__confirm:disabled {
  opacity: 0.45;
  cursor: default;
  box-shadow: none;
}

/* Quiet cancel row — the .schedsheet__delete tier in neutral secondary
 * (nothing here is destructive OR pink-primary; the X close still works). */
.emailsheet__cancel {
  width: 100%;
  margin-top: -4px;
  padding: 10px 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}

/* Post-send hedge: enumeration protection means an in-use address is
 * deliberately never reported — the link just doesn't send. */
.emailsheet__hedge {
  margin: -6px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-tertiary);
}

/* ============================================================
 * Growth dashboard (#/admin/growth) — hidden, admin-only internal tool.
 * Theme-following tokens throughout, like every other screen; the pink
 * accent card is the one deliberate emphasis (active subscriptions).
 * ============================================================ */

.growth {
  /* NO FORCED SCROLL: insets subtracted, same clamp as .home/.tasks/.sched. */
  min-height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); /* iOS 15.0-15.3 fallback (v137): dvh is Safari 15.4+ */
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  padding: 14px 20px 40px;
}

.growth__msg {
  padding: 40px 0;
  text-align: center;
  font-size: 14.5px;
  color: var(--text-secondary);
}

/* 2x2 metric card grid */
.growth__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.growth__stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px;
}

.growth__stat-num {
  font-size: 26px;
  font-weight: 800;
  color: var(--text);
}

.growth__stat-label {
  font-size: 12.5px;
  color: var(--text-secondary);
}

.growth__stat--accent {
  background: var(--pink-soft);
  border-color: var(--pink-line);
}

.growth__stat--accent .growth__stat-num {
  color: var(--pink-text);
}

/* Apple vs Stripe breakdown row */
.growth__split {
  display: flex;
  margin-top: 10px;
}

.growth__split-half {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 16px;
}

.growth__split-half + .growth__split-half {
  border-left: 1px solid var(--line);
}

/* Signups chart + signups list cards */
.growth__chart,
.growth__recent {
  margin-top: 10px;
  padding: 14px 16px;
}

.growth__section-title {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-secondary);
}

/* Today / 7 / 14 / 30 range filter — the Appearance picker's segmented
 * control, text-only, so the segments sit tighter than the icon+label ones. */
.growth__range {
  margin-bottom: 12px;
}

.growth__range .segmented__seg {
  padding: 8px 4px;
}

/* Tapped-bar readout ("9/1: 4 signups") above the chart; the hint text is
 * the same quiet tier so the line never jumps when a bar is selected. */
.growth__readout {
  margin: 0 0 6px;
  min-height: 17px;
  font-size: 12.5px;
  color: var(--text-secondary);
}

.growth__readout strong {
  font-weight: 700;
  color: var(--text);
}

/* Fixed viewBox aspect (labels are real text — never stretch them). */
.growth__chart-svg {
  display: block;
  width: 100%;
  height: auto;
  touch-action: manipulation;
}

.growth__bar {
  fill: var(--pink);
}

.growth__bar--zero {
  fill: var(--text-tertiary);
  opacity: 0.5;
}

.growth__bar.is-selected {
  fill: var(--pink-text);
}

.growth__bar--zero.is-selected {
  fill: var(--text-secondary);
  opacity: 1;
}

/* M/D label under each bar (every 3rd at 30 days so they never collide). */
.growth__axis {
  fill: var(--text-tertiary);
  font-family: inherit;
}

.growth__axis.is-selected {
  fill: var(--text);
  font-weight: 700;
}

/* Full-height transparent slot over each bar: the real tap target. */
.growth__hit {
  cursor: pointer;
}

/* "Load more" under the signups list; gone once the last page is in. */
.growth__more-wrap {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}

.growth__more[disabled] {
  opacity: 0.6;
  cursor: default;
}

.growth__recent-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.growth__recent-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 0;
}

.growth__recent-row + .growth__recent-row {
  border-top: 1px solid var(--line);
}

.growth__recent-email {
  font-size: 14px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.growth__recent-when {
  flex-shrink: 0;
  font-size: 12.5px;
  color: var(--text-secondary);
}

.growth__recent-empty {
  font-size: 14px;
  color: var(--text-secondary);
}
