/* Reader video stage — six modes x three container widths (see site/video_stage.js).
   Container "reader": desktop >= 1200px, laptop/tablet 680-1199px, phone < 680px. */

body.stage-wide .app { max-width: 1600px; }
.reader.has-stage { max-width: none; container: reader / inline-size; }
.stage-grid { display: grid; gap: 1.25rem; align-items: start; }
.stage-main { min-width: 0; }

/* ── slots ─────────────────────────────────────────────────────────────── */
.vslot {
  position: relative; margin: 0; border-radius: 14px; overflow: hidden;
  background: #000; aspect-ratio: 9 / 16; min-width: 0;
  box-shadow: var(--shadow); transition: opacity .25s, box-shadow .25s;
}
.vslot { cursor: pointer; }
.vslot :is(video, .vslot-poster) { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; background: #000; }
/* The portrait still sits OVER the video (same framing rules, see video_stage.js makeSlot) and dissolves away
   once the clip's first frame is on screen (Dom 2026-10-12: the still -> first-frame swap at the moment the
   voice started was a visible jump). It never comes back for that slot; later clips cut frame to frame. */
.vslot .vslot-poster { position: absolute; inset: 0; z-index: 1; pointer-events: none; -webkit-user-select: none; user-select: none; transition: opacity .9s ease; }
.vslot.live-once .vslot-poster { opacity: 0; }
/* Resting face (Dom 2026-10-17): a reader who has finished speaking shows their original portrait again, not
   the paused clip's last frame (mid-word, often grim). Slow in, quick out so the next line's lips are not hidden. */
.vslot.live-once.resting .vslot-poster { opacity: 1; transition: opacity .6s ease; }
.vslot.live-once.waking .vslot-poster { transition: opacity .25s ease; }
@media (prefers-reduced-motion: reduce) { .vslot .vslot-poster { transition: opacity .3s linear; } }
.vslot-cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: .9rem .8rem .6rem;
  display: flex; flex-direction: column; gap: .1rem; color: #fff;
  background: linear-gradient(transparent, rgba(0,0,0,.65)); pointer-events: none;
}
.vslot-role { font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; opacity: .8; }
.vslot-name { font-family: 'Fraunces', serif; font-size: .95rem; }
.vslot-ph {
  position: absolute; top: .5rem; right: .5rem; font-size: .62rem; letter-spacing: .06em;
  background: rgba(0,0,0,.55); color: #f3d9a4; padding: .15rem .5rem; border-radius: 99px;
}
.vslot[data-has-video="0"] .vslot-role::after { content: ' · audio'; }
.reader[data-speaker="poet"] .vslot-poet,
.reader[data-speaker="trans"] .vslot-trans,
.reader[data-speaker="lesson"] .vslot-lesson { box-shadow: 0 0 0 3px var(--gold), var(--shadow); }
.stage-side { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.stage-caption, .study-panel { display: none; }

.layout-chip { white-space: nowrap; }

/* Active-speaker modes show ONE slot: whoever is speaking. */
.reader[data-layout="classic"] .vslot,
.reader[data-layout="cinema"] .vslot,
.reader[data-layout="study"] .vslot { display: none; }
.reader[data-layout="classic"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="classic"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="classic"][data-speaker="lesson"] .vslot-lesson,
.reader[data-layout="cinema"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="cinema"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="cinema"][data-speaker="lesson"] .vslot-lesson,
.reader[data-layout="study"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="study"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="study"][data-speaker="lesson"] .vslot-lesson { display: block; }

/* ── Trio (Dom 2026-10-11): Mingjue LEFT of the poem, Edward RIGHT of it, Charles UNDERNEATH ── */
.reader[data-layout="trio"] .stage-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-areas: "poet trans" "main main"; gap: .6rem;
}
.reader[data-layout="trio"] .stage-side { display: contents; }
.reader[data-layout="trio"] .vslot { display: block; aspect-ratio: 9 / 16; }
.reader[data-layout="trio"] .vslot-poet { grid-area: poet; }
.reader[data-layout="trio"] .vslot-trans { grid-area: trans; }
.reader[data-layout="trio"] .vslot-lesson { width: min(100%, 260px); margin: 1.25rem auto .5rem; }
.reader[data-layout="trio"] .stage-main { grid-area: main; min-width: 0; }
.reader[data-layout="trio"] .vslot-name { font-size: .85rem; }
@container reader (min-width: 900px) {
  /* Side columns take every spare pixel; each face is as large as the column allows while the whole
     face still fits on screen (height-capped at the viewport minus the top bar), hugging the poem. */
  .reader[data-layout="trio"] .stage-grid {
    grid-template-columns: minmax(180px, 1fr) minmax(0, 44rem) minmax(180px, 1fr);
    grid-template-areas: "poet main trans"; gap: 1rem clamp(1rem, 2vw, 2.5rem);
  }
  .reader[data-layout="trio"] .vslot-poet, .reader[data-layout="trio"] .vslot-trans {
    position: sticky; top: 5rem; width: min(100%, calc((100vh - 6.5rem) * 9 / 16));
  }
  .reader[data-layout="trio"] .vslot-poet { justify-self: end; }
  .reader[data-layout="trio"] .vslot-trans { justify-self: start; }
  .reader[data-layout="trio"] .vslot-lesson { width: min(100%, calc((100vh - 8rem) * 9 / 16), 420px); }
  .reader[data-layout="trio"] .vslot-name { font-size: 1.05rem; }
  .reader[data-layout="trio"] .vslot-role { font-size: .72rem; }
}
body.stage-wide .app:has(.reader[data-layout="trio"]) { max-width: none; }
.layout-ico-trio { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#3b2f2a, #3b2f2a), linear-gradient(#2a2f3a, #2a2f3a), linear-gradient(#2f3a2a, #2f3a2a); background-size: 16px 30px, 9px 16px, 9px 16px, 9px 16px; background-position: 3px 5px, 22px 5px, 32px 5px, 42px 5px; }
.play-all { font-weight: 600; }

/* ── Focus: today's reader, no video ──────────────────────────────────── */
.reader[data-layout="focus"] .stage-grid { display: block; max-width: 40rem; margin: 0 auto; }
.reader[data-layout="focus"] .vslot,
.reader[data-layout="focus"] .stage-side { display: none; }

/* ── Classic: column + PiP bubble ─────────────────────────────────────── */
.reader[data-layout="classic"] .stage-grid { display: block; max-width: 40rem; margin: 0 auto; }
.reader[data-layout="classic"] .stage-side { display: contents; }
.reader[data-layout="classic"] .vslot {
  position: fixed; right: max(16px, env(safe-area-inset-right)); bottom: max(16px, env(safe-area-inset-bottom));
  width: 132px; z-index: 30; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,.35);
}
.reader[data-layout="classic"] .vslot-cap { padding: .5rem .5rem .35rem; }
.reader[data-layout="classic"] .vslot-name { font-size: .78rem; }

/* ── Stage ────────────────────────────────────────────────────────────── */
/* phone: speaker docked on top (sticky), text below */
.reader[data-layout="stage"] .stage-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "poet" "main"; }
.reader[data-layout="stage"] .stage-side { display: contents; }
.reader[data-layout="stage"] .vslot { grid-area: poet; display: none; aspect-ratio: auto; height: 34vh; position: sticky; top: 4.6rem; z-index: 5; }
.reader[data-layout="stage"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="stage"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="stage"][data-speaker="lesson"] .vslot-lesson { display: block; }
.reader[data-layout="stage"] .stage-main { grid-area: main; }
@container reader (min-width: 680px) {
  .reader[data-layout="stage"] .stage-grid { grid-template-columns: 220px minmax(0, 1fr); grid-template-areas: "poet main"; }
  .reader[data-layout="stage"] .vslot { height: auto; aspect-ratio: 9 / 16; top: 5.5rem; }
}
@container reader (min-width: 1200px) {
  .reader[data-layout="stage"] .stage-grid { grid-template-columns: 280px minmax(0, 1fr) 280px; grid-template-areas: "poet main side"; gap: 2rem; }
  .reader[data-layout="stage"] .stage-side { display: flex; grid-area: side; position: sticky; top: 5.5rem; }
  .reader[data-layout="stage"] .vslot { display: block; position: sticky; top: 5.5rem; }
  .reader[data-layout="stage"] .vslot-poet { grid-area: poet; }
  .reader[data-layout="stage"] .stage-side .vslot { grid-area: auto; position: relative; top: 0; }
  .reader[data-layout="stage"] .vslot-lesson { display: none; }
  .reader[data-layout="stage"][data-lesson-on="1"] .vslot-lesson { display: block; animation: vslot-in .3s ease-out; }
  .reader[data-layout="stage"][data-lesson-on="1"] .stage-side .vslot { aspect-ratio: 1 / 1; }
  .reader[data-layout="stage"] .stage-main { max-width: 44rem; justify-self: center; width: 100%; }
}
@keyframes vslot-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* ── Duet: two readers in a row over the text ─────────────────────────── */
.reader[data-layout="duet"] .stage-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-areas: "poet side" "main main";
  grid-template-rows: 190px auto; gap: .75rem;
}
.reader[data-layout="duet"] .stage-side { display: block; grid-area: side; height: 100%; }
.reader[data-layout="duet"] .vslot { aspect-ratio: auto; height: 100%; width: 100%; }
.reader[data-layout="duet"] .vslot-poet { grid-area: poet; }
.reader[data-layout="duet"] .vslot-lesson { display: none; }
.reader[data-layout="duet"][data-lesson-on="1"] .vslot-lesson { display: block; }
.reader[data-layout="duet"][data-lesson-on="1"] .vslot-trans { display: none; }
.reader[data-layout="duet"] .stage-main { grid-area: main; max-width: 44rem; width: 100%; justify-self: center; }
.reader[data-layout="duet"][data-speaker="poet"] .stage-side .vslot,
.reader[data-layout="duet"]:not([data-speaker="poet"]) .vslot-poet { opacity: .55; }
@container reader (min-width: 680px) {
  .reader[data-layout="duet"] .stage-grid { grid-template-rows: 340px auto; gap: 1rem; }
  .reader[data-layout="duet"] .vslot { aspect-ratio: 3 / 4; width: auto; max-width: 100%; }
  .reader[data-layout="duet"] .vslot-poet { justify-self: end; }
  .reader[data-layout="duet"] .stage-side { justify-self: start; }
}
@container reader (min-width: 1200px) { .reader[data-layout="duet"] .stage-grid { grid-template-rows: 420px auto; } }

/* ── Cinema: the video is the page ────────────────────────────────────── */
.reader[data-layout="cinema"] .stage-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "poet" "main"; }
.reader[data-layout="cinema"] .stage-side { display: contents; }
.reader[data-layout="cinema"] .vslot {
  grid-area: poet; aspect-ratio: auto; height: min(78svh, 720px); border-radius: 0; background: #000;
  margin-inline: calc(-1 * clamp(1rem, 4vw, 2rem));
}
.reader[data-layout="cinema"] .stage-caption {
  display: block; grid-area: poet; align-self: end; z-index: 3; text-align: center; color: #fff;
  padding: 2.5rem 1rem 3.2rem; margin-inline: calc(-1 * clamp(1rem, 4vw, 2rem));
  background: linear-gradient(transparent, rgba(0,0,0,.78) 45%); pointer-events: none;
  opacity: 0; transition: opacity .2s;
}
/* phone: video + captions stay pinned under the top bar while the karaoke auto-scroll moves the text */
@container reader (max-width: 679px) {
  .reader[data-layout="cinema"] .vslot,
  .reader[data-layout="cinema"] .stage-caption { position: sticky; top: 4.15rem; height: 56svh; z-index: 5; }
  .reader[data-layout="cinema"] .stage-caption {
    z-index: 6; align-self: start; display: flex; flex-direction: column; justify-content: flex-end;
    padding-bottom: 1rem; background: linear-gradient(transparent 55%, rgba(0,0,0,.8));
  }
  .reader[data-layout="cinema"] .stage-main .line { scroll-margin-top: calc(56svh + 4.4rem); }
}
.reader[data-layout="cinema"] .stage-caption.on { opacity: 1; }
.stage-caption-src { font-family: 'Noto Serif SC', 'Songti SC', serif; font-size: 1.35rem; line-height: 1.35; }
.stage-caption-gloss { font-size: .9rem; color: #f3c6b2; margin-top: .3rem; }
.reader[data-layout="cinema"] .stage-main { grid-area: main; }
@container reader (min-width: 680px) {
  .reader[data-layout="cinema"] .stage-grid { grid-template-columns: minmax(0, 1fr) 360px; grid-template-areas: "poet main"; gap: 0; }
  .reader[data-layout="cinema"] .vslot, .reader[data-layout="cinema"] .stage-caption { margin-inline: 0; }
  .reader[data-layout="cinema"] .vslot { height: calc(100vh - 7rem); position: sticky; top: 5.5rem; border-radius: 14px 0 0 14px; }
  .reader[data-layout="cinema"] .vslot :is(video, .vslot-poster) { object-fit: contain; }
  .reader[data-layout="cinema"] .stage-caption { position: sticky; bottom: 0; border-radius: 0 0 0 14px; }
  .reader[data-layout="cinema"] .stage-main {
    max-height: calc(100vh - 7rem); overflow: auto; position: sticky; top: 5.5rem;
    padding: 1rem 1.2rem; background: var(--paper-raised); border: 1px solid var(--rule); border-left: 0; border-radius: 0 14px 14px 0;
  }
}

/* ── Study: clean text + learning panel ───────────────────────────────── */
.reader[data-layout="study"] .stage-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "panel"; }
.reader[data-layout="study"] .stage-side { display: none; }
.reader[data-layout="study"] .stage-main { grid-area: main; }
.reader[data-layout="study"] .stage-main .poem-body .gloss,
.reader[data-layout="study"] .stage-main .poem-body .note { display: none; }
.reader[data-layout="study"] .study-panel {
  display: block; grid-area: panel; position: sticky; bottom: 0; z-index: 6; max-height: 46vh; overflow: auto;
  background: var(--paper-raised); border: 1px solid var(--rule); border-radius: 14px 14px 0 0; padding: .8rem 1rem 1rem;
  box-shadow: 0 -8px 24px rgba(0,0,0,.12);
}
.study-tabs { display: flex; gap: .4rem; border-bottom: 1px solid var(--rule); margin-bottom: .7rem; }
.study-tab { font: inherit; font-size: .85rem; padding: .35rem .8rem; border: 0; border-radius: 8px 8px 0 0; background: none; color: var(--ink-soft); cursor: pointer; }
.study-tab.on { background: var(--paper); color: var(--accent); font-weight: 600; }
.study-media .vslot { aspect-ratio: 16 / 10; margin-bottom: .7rem; }
.study-list { margin: 0; padding-left: 1.3rem; display: none; }
.study-panel[data-tab="trans"] .study-list-trans, .study-panel[data-tab="lesson"] .study-list-lesson { display: block; }
.study-list li { padding: .35rem .4rem; border-radius: 6px; cursor: pointer; font-size: .95rem; line-height: 1.45; }
.study-list li:hover { background: color-mix(in srgb, var(--gold) 10%, transparent); }
.study-list li.on { background: color-mix(in srgb, var(--gold) 22%, transparent); }
.study-list li.fallback { color: var(--ink-soft); }
@container reader (min-width: 680px) {
  .reader[data-layout="study"] .stage-grid { grid-template-columns: minmax(0, 1fr) 300px; grid-template-areas: "main panel"; }
  .reader[data-layout="study"] .study-panel { top: 5.5rem; bottom: auto; max-height: calc(100vh - 7rem); border-radius: 14px; box-shadow: var(--shadow); }
}
@container reader (min-width: 1200px) {
  .reader[data-layout="study"] .stage-grid { grid-template-columns: minmax(0, 46rem) 360px; justify-content: center; gap: 2rem; }
}

/* ── Layout sheet ─────────────────────────────────────────────────────── */
.layout-sheet-overlay { position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,.45); display: flex; align-items: flex-end; justify-content: center; }
.layout-sheet {
  width: min(640px, 100%); max-height: 90vh; overflow: auto; background: var(--paper); color: var(--ink);
  border-radius: 18px 18px 0 0; padding: 1.2rem 1rem calc(1rem + env(safe-area-inset-bottom));
  box-shadow: 0 -12px 40px rgba(0,0,0,.35);
}
@media (min-width: 700px) { .layout-sheet-overlay { align-items: center; } .layout-sheet { border-radius: 18px; padding: 1.4rem 1.4rem 1.2rem; } }
.layout-sheet h3 { font-family: 'Fraunces', serif; font-weight: 500; margin: 0 0 .2rem; font-size: 1.4rem; }
.layout-sheet-sub { margin: 0 0 1rem; color: var(--ink-faint); font-size: .85rem; }
.layout-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: .6rem; }
.layout-opt {
  font: inherit; text-align: left; display: grid; grid-template-columns: 54px 1fr; grid-template-rows: auto auto; column-gap: .7rem;
  padding: .7rem; border-radius: 12px; border: 1px solid var(--rule); background: var(--paper-raised); color: var(--ink); cursor: pointer;
}
.layout-opt b { font-weight: 600; font-size: .95rem; align-self: end; }
.layout-opt small { color: var(--ink-faint); font-size: .75rem; line-height: 1.3; grid-column: 2; }
.layout-opt[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }
.layout-opt:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.layout-ico { grid-row: 1 / span 2; width: 54px; height: 40px; border-radius: 6px; border: 1px solid var(--rule); background-color: var(--paper); background-repeat: no-repeat; }
/* tiny diagrams: dark = video, gold = text column */
.layout-ico-classic { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#3b2f2a, #3b2f2a); background-size: 22px 30px, 10px 16px; background-position: 15px 5px, 40px 20px; }
.layout-ico-stage { background-image: linear-gradient(#3b2f2a, #3b2f2a), linear-gradient(var(--gold), var(--gold)), linear-gradient(#2a2f3a, #2a2f3a); background-size: 12px 30px, 22px 30px, 12px 30px; background-position: 3px 5px, 16px 5px, 39px 5px; }
.layout-ico-duet { background-image: linear-gradient(#3b2f2a, #3b2f2a), linear-gradient(#2a2f3a, #2a2f3a), linear-gradient(var(--gold), var(--gold)); background-size: 22px 14px, 22px 14px, 46px 14px; background-position: 4px 4px, 28px 4px, 4px 22px; }
.layout-ico-cinema { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#17120e, #17120e); background-size: 30px 3px, 100% 100%; background-position: 12px 30px, 0 0; }
.layout-ico-study { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#2a2f3a, #2a2f3a); background-size: 26px 30px, 18px 30px; background-position: 4px 5px, 32px 5px; }
.layout-ico-focus { background-image: linear-gradient(var(--gold), var(--gold)); background-size: 26px 30px; background-position: 14px 5px; }
.layout-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: 1rem; flex-wrap: wrap; }

@media (prefers-reduced-motion: reduce) { .vslot, .stage-caption { transition: none; } .reader[data-layout="stage"][data-lesson-on="1"] .vslot-lesson { animation: none; } }
.reader[data-layout="cinema"] .vslot-cap { top: 0; bottom: auto; padding: .7rem .9rem 1.4rem; background: linear-gradient(rgba(0,0,0,.55), transparent); }
.reader[data-layout="cinema"] .vslot-ph { top: auto; bottom: .5rem; }
@container reader (max-width: 679px) { .reader[data-layout="study"] .study-media .vslot { aspect-ratio: 2 / 1; } }

/* ── Backdrop (Dom 2026-10-11): black so the black-studio portraits float ── */
body.backdrop-black, body.backdrop-split {
  --paper: #000; --paper-raised: #0b0b0b; --rule: #262626;
  --ink: #ece4d6; --ink-soft: #b6ab99; --ink-faint: #8a7f70;
  --accent: #eae5d9; --accent-ink: #090c10; --accent-soft: #635e54; --highlight: #202225; --gold: #d8b25e; --en: #6fb3a6; --es: #eae5d9;   /* brand kit on the dark stage (24 Oct): ivory accent, no oxblood/pink */
  --shadow: none; background: #000; color: var(--ink); color-scheme: dark;
}
body.backdrop-black .topbar, body.backdrop-split .topbar,
body.backdrop-black header, body.backdrop-split header { background: #000; }
body.backdrop-black .vslot, body.backdrop-split .vslot { box-shadow: none; }
body.backdrop-split .stage-main {
  background: #15130f; border: 1px solid #322c23; border-radius: 16px; padding: 1.25rem clamp(1rem, 2vw, 1.75rem);
  --paper: #15130f; --paper-raised: #1d1a15; --rule: #322c23;
}
.layout-sheet-h4 { margin: 1.1rem 0 .5rem; font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.backdrop-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: .6rem; }
.backdrop-opt {
  font: inherit; text-align: left; display: grid; grid-template-columns: 34px 1fr; column-gap: .6rem; align-items: center;
  padding: .6rem .7rem; border-radius: 12px; border: 1px solid var(--rule); background: var(--paper-raised); color: var(--ink); cursor: pointer;
}
.backdrop-opt small { grid-column: 2; color: var(--ink-faint); font-size: .75rem; }
.backdrop-opt[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }
.backdrop-swatch { grid-row: 1 / span 2; width: 34px; height: 34px; border-radius: 8px; border: 1px solid #444; background: #000; }
.backdrop-opt-split .backdrop-swatch { background: linear-gradient(90deg, #000 30%, #15130f 30% 70%, #000 70%); }
.backdrop-opt-theme .backdrop-swatch { background: linear-gradient(135deg, #15130f 50%, #f6f1e7 50%); }
/* Floating readers: the studio black in the portraits is ~#0a0a0a, not #000, so feather every edge into the page. */
body.backdrop-black .vslot, body.backdrop-split .vslot { border-radius: 0; background: transparent; box-shadow: none !important; }
body.backdrop-black .vslot :is(video, .vslot-poster), body.backdrop-split .vslot :is(video, .vslot-poster) {
  background: transparent;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, transparent 0, #000 6%, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, transparent 0, #000 6%, #000 78%, transparent 100%);
  mask-composite: intersect;
}
body.backdrop-black .vslot-cap, body.backdrop-split .vslot-cap { background: none; }
/* speaker cue without a frame: the speaking reader's name turns gold */
body.backdrop-black .reader[data-speaker="poet"] .vslot-poet .vslot-name,
body.backdrop-black .reader[data-speaker="trans"] .vslot-trans .vslot-name,
body.backdrop-black .reader[data-speaker="lesson"] .vslot-lesson .vslot-name,
body.backdrop-split .reader[data-speaker="poet"] .vslot-poet .vslot-name,
body.backdrop-split .reader[data-speaker="trans"] .vslot-trans .vslot-name,
body.backdrop-split .reader[data-speaker="lesson"] .vslot-lesson .vslot-name { color: var(--gold); }

/* ── Dock: all reader controls in the top bar (Dom 2026-10-11) ── */
.stage-dock { display: flex; align-items: center; gap: .9rem; flex: 1; min-width: 0; margin: 0 1.25rem; }
.dock-title { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; margin-right: auto; }
.dock-title b { font-family: 'Noto Serif SC', 'Songti SC', serif; font-size: 1.15rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dock-title span { font-size: .78rem; color: var(--ink-faint); }
.dock-play, .dock-nav { display: flex; gap: .35rem; align-items: center; }
.stage-dock .btn { padding: .4rem .75rem; font-size: .85rem; white-space: nowrap; }
.dock-nav button { font: inherit; font-size: .85rem; background: none; border: 1px solid var(--rule); color: var(--ink-soft); border-radius: 8px; padding: .4rem .65rem; cursor: pointer; }
.dock-gear-wrap { position: relative; }
.dock-gear { font-size: 1.05rem; padding: .35rem .6rem; }
.dock-panel {
  position: absolute; right: 0; top: calc(100% + .5rem); width: min(420px, 92vw); max-height: 75vh; overflow: auto; z-index: 50;
  background: var(--paper-raised); border: 1px solid var(--rule); border-radius: 14px; padding: .9rem; box-shadow: 0 16px 40px rgba(0,0,0,.5);
  display: flex; flex-direction: column; gap: .8rem;
}
.dock-panel[hidden] { display: none; }
.dock-panel-row { display: flex; flex-wrap: wrap; gap: .4rem; }
.dock-panel .ai-disclosure { font-size: .72rem; margin: 0; }
/* the page itself: no nav row, head or empty controls block in docked mode */
body.stage-docked .reader .reader-nav,
body.stage-docked .reader .reader-head,
body.stage-docked .reader .controls { display: none; }
body.stage-docked .reader .poem-body { margin-top: 0; }
@media (max-width: 1180px) { .dock-title { display: none; } }
/* narrow: pinned bottom bar, settings open upwards */
body.dock-bottom .stage-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; margin: 0; flex-wrap: nowrap; overflow-x: auto;
  padding: .55rem .75rem calc(.55rem + env(safe-area-inset-bottom)); background: color-mix(in srgb, var(--paper) 94%, transparent);
  border-top: 1px solid var(--rule); backdrop-filter: blur(8px);
}
body.dock-bottom .dock-title { display: none; }
body.dock-bottom .dock-panel { position: fixed; top: auto; bottom: calc(3.6rem + env(safe-area-inset-bottom)); right: .5rem; }
body.dock-bottom .app { padding-bottom: 5rem; }
body.dock-bottom .stage-dock { flex-wrap: wrap; justify-content: center; overflow-x: visible; gap: .35rem .5rem; padding-top: .45rem; }
body.dock-bottom .stage-dock .btn, body.dock-bottom .dock-nav button { padding: .32rem .55rem; font-size: .78rem; }
body.dock-bottom .dock-gear { font-size: .95rem; }
.dock-music { font-size: 1rem; padding: .35rem .6rem; }
.dock-music[hidden] { display: none; }

/* ── Spotlight (Dom 2026-10-11): poem on one half, the speaking reader fills the other; readers take turns
   in the light with a crossfade. All three slots share one grid cell; only data-speaker is visible. ── */
.reader[data-layout="spotlight"] .stage-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-areas: "main stage"; gap: clamp(1rem, 3vw, 3rem); align-items: start;
}
.reader[data-layout="spotlight"][data-side="left"] .stage-grid { grid-template-areas: "stage main"; }
.reader[data-layout="spotlight"] .stage-side { display: contents; }
.reader[data-layout="spotlight"] .stage-main { grid-area: main; min-width: 0; max-width: 44rem; width: 100%; justify-self: center; }
.reader[data-layout="spotlight"] .vslot {
  grid-area: stage; display: block; position: sticky; top: 4.75rem; justify-self: center;
  height: calc(100vh - 8.25rem); width: auto; aspect-ratio: 9 / 16; max-width: 100%;
  opacity: 0; transform: scale(.985); pointer-events: none;
  transition: opacity .6s ease, transform .9s ease;
}
.reader[data-layout="spotlight"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="spotlight"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="spotlight"][data-speaker="lesson"] .vslot-lesson { opacity: 1; transform: none; pointer-events: auto; }
.reader[data-layout="spotlight"] .vslot-name { font-size: 1.25rem; }
.reader[data-layout="spotlight"] .vslot-role { font-size: .78rem; }
@media (max-width: 899px) {
  .reader[data-layout="spotlight"] .stage-grid,
  .reader[data-layout="spotlight"][data-side="left"] .stage-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "stage" "main"; }
  .reader[data-layout="spotlight"] .vslot { height: 46svh; top: 4.2rem; z-index: 5; }
}
@media (prefers-reduced-motion: reduce) { .reader[data-layout="spotlight"] .vslot { transition: opacity .2s linear; transform: none; } }
.layout-ico-spotlight { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#3b2f2a, #3b2f2a); background-size: 20px 30px, 22px 30px; background-position: 4px 5px, 28px 5px; }
.dock-side[hidden] { display: none; }
body.dock-bottom .dock-side { display: none; }
/* 1180-1300px: keep every dock button on one line (the pilot saw Previous / Next wrap at 1180) */
.dock-nav button, .stage-dock .btn { white-space: nowrap; }
@media (max-width: 1320px) {
  .stage-dock { gap: .45rem; margin: 0 .6rem; }
  .stage-dock .btn, .dock-nav button { padding: .35rem .55rem; font-size: .8rem; }
}
/* Spotlight, wide screens (Dom 2026-10-11): the speaker is LOCKED to the screen in his half (position:
   fixed), so long poems, revealed glosses and auto-scroll never move him; the poem half scrolls on its own.
   The reader is a CSS container elsewhere, and containment turns position:fixed into "fixed to the reader",
   so Spotlight drops it and uses media queries. --topbar-h is the live top-bar height (video_stage.js). */
.reader.has-stage[data-layout="spotlight"] { container-type: normal; }
@media (min-width: 900px) {
  .reader[data-layout="spotlight"] {
    --spot-top: calc(var(--topbar-h, 4.5rem) + .35rem);
    --spot-w: min(calc((100vh - var(--topbar-h, 4.5rem) - .7rem) * 9 / 16), calc(50vw - 3rem));
  }
  .reader[data-layout="spotlight"] .stage-grid { min-height: calc(100vh - var(--topbar-h, 4.5rem) - 3rem); }
  .reader[data-layout="spotlight"] .stage-main { align-self: center; }
  .reader[data-layout="spotlight"] .vslot {
    position: fixed; top: var(--spot-top); left: calc(75vw - var(--spot-w) / 2);
    width: var(--spot-w); height: auto; max-width: none; aspect-ratio: 9 / 16; z-index: 4;
  }
  .reader[data-layout="spotlight"][data-side="left"] .vslot { left: calc(25vw - var(--spot-w) / 2); }
}
/* Charles's calm mood clip is framed with more headroom (head top at 12.4% of the frame vs Mingjue 7.7%,
   Edward 8.1%, measured 2026-10-11), so he sat lower after the crossfade. Scale from the bottom edge:
   (1 - 0.079) / (1 - 0.124) = 1.051 lifts his head to ~7.9% without moving his shoulders. */
.vslot-lesson :is(video, .vslot-poster) { transform: scale(1.051); transform-origin: 50% 100%; }

.dock-lessons:disabled { opacity: .38; cursor: not-allowed; filter: grayscale(1); }

.dock-order { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.dock-order-label { font-size: .78rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); width: 100%; }
.dock-mix-note { font-size: .74rem; color: var(--ink-faint); width: 100%; }
.dock-order-opt[aria-pressed="true"] { border-color: var(--accent); color: var(--ink); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); }
/* The line being read (Dom 2026-10-11): its Chinese and its English translation grow and go bold, then
   settle back when the next line starts. Only in the video reader. */
.reader.has-stage .line .src, .reader.has-stage .line .gloss { transition: font-size .25s ease, font-weight .25s ease, color .25s ease; }
.reader.has-stage .line.cu-fitting .src, .reader.has-stage .line.cu-fitting .gloss, .reader.has-stage .line.cu-fitting .note { transition: none !important; }   /* while fitLine measures */
.reader.has-stage .line.speaking .src { font-size: 1.32em; font-weight: 700; }
/* a title or stanza-number line the poet does not read (its start equals the next line's): muted, never highlighted */
.reader.has-stage .line.unspoken .src { opacity: .55; font-weight: 500; letter-spacing: .02em; }
.reader.has-stage .line.speaking .gloss { font-size: 1.14em; font-weight: 650; color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .reader.has-stage .line .src, .reader.has-stage .line .gloss { transition: none; } }

/* ── Phones (< 900px, Dom 2026-10-11): every control in the top bar, readers in the top half, poem below ──
   The dock is a second, compact row of the top bar (no bottom bar). Buttons show their data-icon; the app's
   own label text stays in the DOM for screen readers. */
body.dock-compact .topbar { flex-wrap: wrap; row-gap: .5rem; padding: .6rem .75rem .55rem; }
body.dock-compact .stage-dock {
  order: 3; flex: 1 0 100%; margin: 0; display: flex; flex-wrap: nowrap; align-items: center;
  justify-content: space-between; gap: .15rem;
}
body.dock-compact .dock-title { display: none; }
body.dock-compact .dock-play, body.dock-compact .dock-nav { gap: .15rem; }
body.dock-compact .stage-dock .btn, body.dock-compact .dock-nav button, body.dock-compact .dock-gear, body.dock-compact .dock-music {
  padding: .4rem .24rem; min-width: 1.85rem; min-height: 2.1rem; font-size: .9rem; line-height: 1; justify-content: center; flex: 0 1 auto;
}
/* Spotlight stacks on a phone, so "swap sides" has nothing to swap */
body.dock-compact .dock-side { display: none !important; }
body.dock-compact .stage-dock [data-icon] { font-size: 0 !important; }
body.dock-compact .stage-dock [data-icon]::before { content: attr(data-icon); font-size: .84rem; line-height: 1; white-space: nowrap; letter-spacing: -.02em; }
body.dock-compact .dock-panel {
  position: absolute; top: calc(100% + .5rem); right: 0; left: auto; bottom: auto;
  width: min(94vw, 380px); max-height: calc(100svh - var(--topbar-h, 7rem) - 1rem);
}

/* Trio + Spotlight on a phone: the page itself never scrolls (overflow: clip, so even scrollIntoView cannot
   move it); the readers fill what is left of the top half, the poem scrolls in its own pane below. */
body.phone-split { height: 100svh; overflow: clip; --avatar-h: calc(50svh - var(--topbar-h, 7rem) - .4rem); }
body.phone-split .footer { display: none; }
body.phone-split .app { padding: 0; max-width: none; flex: 1 1 auto; min-height: 0; overflow: clip; }
body.phone-split .reader.has-stage { height: 100%; max-width: none; margin: 0; }
body.phone-split .reader.has-stage .stage-grid {
  height: 100%; display: grid; gap: .4rem .35rem; padding: .4rem .5rem 0; box-sizing: border-box;
  grid-template-rows: var(--avatar-h) minmax(0, 1fr); grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-areas: "poet trans lesson" "main main main"; align-items: stretch;
}
body.phone-split .reader.has-stage .stage-side { display: contents; }
body.phone-split .reader.has-stage .vslot {
  position: relative; top: auto; left: auto; height: 100%; width: 100%; max-width: 100%; aspect-ratio: auto;
  margin: 0; border-radius: 12px; display: block; justify-self: stretch;
}
body.phone-split .reader.has-stage .vslot-poet { grid-area: poet; }
body.phone-split .reader.has-stage .vslot-trans { grid-area: trans; }
body.phone-split .reader.has-stage .vslot-lesson { grid-area: lesson; }
body.phone-split .reader.has-stage .vslot-cap { padding: .45rem .45rem .35rem; }
body.phone-split .reader.has-stage .vslot-name { font-size: .78rem; }
body.phone-split .reader.has-stage .vslot-role { font-size: .58rem; letter-spacing: .08em; }
body.phone-split .reader.has-stage .stage-main {
  grid-area: main; min-height: 0; max-width: none; width: auto; align-self: stretch; justify-self: stretch;
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding: .5rem .3rem calc(1.5rem + env(safe-area-inset-bottom));
}
/* Spotlight on a phone: one large speaker centred in the top half (crossfade unchanged) */
body.phone-split .reader[data-layout="spotlight"] .stage-grid,
body.phone-split .reader[data-layout="spotlight"][data-side="left"] .stage-grid {
  grid-template-columns: minmax(0, 1fr); grid-template-areas: "stage" "main";
}
body.phone-split .reader[data-layout="spotlight"] .vslot {
  grid-area: stage; width: auto; aspect-ratio: 9 / 16; justify-self: center; z-index: auto;
}
body.phone-split .reader[data-layout="spotlight"] .vslot-name { font-size: 1rem; }
body.phone-split .reader[data-layout="spotlight"] .vslot-role { font-size: .66rem; }

/* "On phones: full-screen text, faces over it" (the pre-split look, kept as an option): the floating
   speaker now sits just under the taller two-row top bar. */
@media (max-width: 899px) {
  .reader[data-layout="spotlight"] .vslot { top: calc(var(--topbar-h, 4.2rem) + .2rem); }
}
/* Poem colours section in the gear panel */
.dock-theme { display: grid; grid-template-columns: 1fr; gap: .35rem; }
.dock-theme .dock-order-label { margin-bottom: .1rem; }
.dock-theme-row { display: flex; align-items: center; justify-content: space-between; gap: .75rem; font-size: .85rem; color: var(--ink-soft); }
.dock-theme-row input[type="color"] { width: 2.4rem; height: 1.7rem; padding: 0; border: 1px solid var(--rule); border-radius: 6px; background: none; cursor: pointer; }
.dock-theme-row input[type="range"] { flex: 0 1 9rem; accent-color: var(--accent); }
.dock-theme .btn { justify-self: start; margin-top: .2rem; }

/* Poem lines in the video reader (Dom 2026-10-11). A couplet line (16 characters with its punctuation)
   stays on one row: the base size and the enlarged "line being read" are both capped by the poem's own
   width (container units), and the line keeps equal side padding (the ☆ sits in it) instead of 2 rem on
   the right only. Centred by default; "Lines: Left" in the gear panel restores the old alignment. */
.reader.has-stage .stage-main .poem-body { container-type: inline-size; }
.reader.has-stage .stage-main .poem-body .line { padding-left: 1.3rem; padding-right: 1.3rem; margin-left: 0; margin-right: 0; }
.reader.has-stage .stage-main .poem-body .line .src { font-size: min(1em, calc((100cqi - 2.8rem) / 16.2)); }
.reader.has-stage .stage-main .poem-body .line.speaking .src { font-size: min(1.32em, calc((100cqi - 2.8rem) / 16.2)); }
body.poem-centred .reader.has-stage .stage-main .poem-body .line { text-align: center; }
body.poem-centred .reader.has-stage .stage-main .poem-body .line .gloss { padding-left: 0; }
.dock-theme-seg { display: inline-flex; gap: .3rem; }
.dock-theme-seg .btn { padding: .3rem .6rem; font-size: .8rem; }
/* phones: the Chinese line is already at the width cap, so its translation grows only a touch (it was
   ending up larger than the poem) */
@media (max-width: 899px) { .reader.has-stage .line.speaking .gloss { font-size: calc(.95rem * 1.1); } }
@media (max-width: 379px) {
  body.dock-compact .stage-dock, body.dock-compact .dock-play, body.dock-compact .dock-nav { gap: .08rem; }
  body.dock-compact .stage-dock .btn, body.dock-compact .dock-nav button, body.dock-compact .dock-gear, body.dock-compact .dock-music { min-width: 1.72rem; padding-left: .18rem; padding-right: .18rem; }
  body.dock-compact .topbar { padding-left: .5rem; padding-right: .5rem; }
}

/* ── Phones, "Controls: Bottom, in thumb reach" (Dom 2026-10-12): one slim row hugging the bottom edge.
   ▾ tucks it away; a small ▴ tab in the corner brings it back. The Trio/Spotlight toggle moves into the
   gear panel's layout chip there, so the row keeps a single line. */
.dock-tuck, .dock-reveal { display: none; }
body.dock-thumb .stage-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; margin: 0;
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: .12rem;
  padding: .3rem .35rem calc(.3rem + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--paper) 90%, transparent); border-top: 1px solid var(--rule);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: transform .25s ease;
}
body.dock-thumb .dock-mode { display: none !important; }
body.dock-thumb .dock-tuck { display: inline-flex; }
body.dock-thumb .dock-panel {
  position: fixed; top: auto; bottom: calc(3rem + env(safe-area-inset-bottom)); right: .5rem; left: auto;
  max-height: calc(100svh - var(--topbar-h, 4.5rem) - 4rem);
}
body.dock-thumb.dock-tucked .stage-dock { transform: translateY(110%); }
body.dock-thumb.dock-tucked .dock-reveal:not([hidden]) {
  display: grid; place-items: center; position: fixed; right: .6rem; bottom: calc(.6rem + env(safe-area-inset-bottom));
  z-index: 41; width: 2.4rem; height: 2.4rem; border-radius: 50%; border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 88%, transparent); color: var(--ink); font-size: 1rem;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); cursor: pointer;
}
/* keep the end of the poem pane (review buttons) clear of the bar */
body.dock-thumb:not(.dock-tucked).phone-split .reader.has-stage .stage-main { padding-bottom: calc(3.6rem + env(safe-area-inset-bottom)); }
body.dock-thumb:not(.dock-tucked):not(.phone-split) .app { padding-bottom: calc(3.6rem + env(safe-area-inset-bottom)); }
body.dock-thumb.dock-tucked.phone-split .reader.has-stage .stage-main { padding-bottom: calc(3.2rem + env(safe-area-inset-bottom)); }
/* Poem colours: presets, auto / yours tags, readability warning (Dom 2026-10-12) */
.dock-theme-presets { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: .3rem; margin-bottom: .25rem; }
.dock-theme-swatch {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .05rem;
  height: 2.9rem; border-radius: 9px; border: 1px solid var(--rule); cursor: pointer; padding: 0; font: inherit;
}
.dock-theme-swatch span { font-family: 'Noto Serif SC', 'Songti SC', serif; font-size: 1rem; line-height: 1; }
.dock-theme-swatch small { font-size: .55rem; line-height: 1; opacity: .85; }
.dock-theme-swatch:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.dock-theme-row label { flex: 1 1 auto; }
.dock-theme-ctl { display: inline-flex; align-items: center; gap: .4rem; }
.dock-theme-tag {
  font: inherit; font-size: .68rem; padding: .1rem .4rem; border-radius: 99px; border: 1px solid var(--rule);
  background: none; color: var(--ink-faint); cursor: pointer; white-space: nowrap;
}
.dock-theme-tag:disabled { cursor: default; opacity: .75; }
.dock-theme-tag:not(:disabled) { color: var(--ink); border-color: var(--accent); }
.dock-theme-warn { font-size: .68rem; color: #e8a33d; white-space: nowrap; }
.dock-theme-warn[hidden] { display: none; }
/* Bottom bar (thumb mode): the two language buttons sit bottom-right, next to the hide button at the far
   right, where a right thumb rests (Dom 2026-10-12). The groups dissolve so single buttons can reorder. */
body.dock-thumb .dock-play, body.dock-thumb .dock-nav { display: contents; }
body.dock-thumb .stage-dock > *, body.dock-thumb .dock-play > *, body.dock-thumb .dock-nav > * { order: 1; }
body.dock-thumb .dock-listen { order: 90; }
body.dock-thumb .dock-english { order: 91; }
body.dock-thumb .dock-lessons { order: 92; }   /* Dom 2026-10-12: poem, translation, lesson together on the right */
body.dock-thumb .dock-tuck { order: 99; }
/* Phones (Dom 2026-10-12, iPhone screenshot): never crop the top of a head. When the box is wider than the
   9:16 clip, cover trims top AND bottom around object-position (50% 30% took the hair off); anchor the clip
   to its top so any trim comes off the shoulders, and keep only a slim fade on the top edge. */
body.phone-split .reader.has-stage .vslot :is(video, .vslot-poster),
body.dock-compact .reader.has-stage .vslot :is(video, .vslot-poster) { object-position: 50% 0%; }
body.backdrop-black.phone-split .vslot :is(video, .vslot-poster), body.backdrop-split.phone-split .vslot :is(video, .vslot-poster) {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, transparent 0, #000 2.5%, #000 80%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, transparent 0, #000 2.5%, #000 80%, transparent 100%);
}
body.phone-split .reader.has-stage .stage-grid { padding-top: .5rem; }
/* top bar hugs the status bar on phones */
body.dock-compact .topbar { padding-top: .2rem; padding-bottom: .4rem; row-gap: .4rem; }
body.dock-compact.dock-thumb .topbar { padding-top: .15rem; padding-bottom: .35rem; }
body.dock-compact .topbar .brand-mark { width: 26px; height: 26px; }
body.dock-compact .topbar .brand-word { width: 86px; height: 13px; }
body.dock-compact .topbar .nav-link { padding-top: .3rem; padding-bottom: .3rem; }
/* Phones, take two (Dom 2026-10-12): top-anchoring left the clip's own ~8% headroom showing. Scale each face
   from the bottom edge so the top of the head sits ~1.5% below the box ("on the line, tiny gap, nothing cut"):
   heads are at 7.7% (Mingjue) / 8.1% (Edward) of the frame; Charles's clip is already scaled 1.051 to match. */
/* Scale = (100 - gap) / (100 - head top %), gap 2.5% of the box. Measured 2026-10-12 on the live files:
   head top in the poster stills 8.2% (Mingjue), 8.3% (Edward), 12.7% (Charles); in the dubbed video frames
   6.7% (Mingjue, Edward) and 11.8% (Charles). Using the poster number for the video put the head 0.2% from
   the edge, so it was cut as soon as play swapped the poster for the video (Dom's iPhone). */
body.phone-split .reader.has-stage .vslot :is(video, .vslot-poster) { transform: scale(1.062); transform-origin: 50% 100%; }
body.phone-split .reader.has-stage .vslot :is(video, .vslot-poster)[data-live="1"] { transform: scale(1.045); }
body.phone-split .reader.has-stage .vslot-lesson :is(video, .vslot-poster) { transform: scale(1.117); transform-origin: 50% 100%; }
body.phone-split .reader.has-stage .vslot-lesson :is(video, .vslot-poster)[data-live="1"] { transform: scale(1.105); }
body.backdrop-black.phone-split .vslot :is(video, .vslot-poster), body.backdrop-split.phone-split .vslot :is(video, .vslot-poster) {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, #000 0, #000 80%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%), linear-gradient(to bottom, #000 0, #000 80%, transparent 100%);
}
body.phone-split .reader.has-stage .stage-grid { padding-top: .3rem; }
/* Narrow layout widths (small phones, or Safari text zoom: a 393 px iPhone at 115% lays out at ~342 px):
   the top bar and the controls must still fit; nothing may push the page wider than the screen. */
body.dock-compact { overflow-x: clip; }
body.dock-compact .topbar { flex-wrap: nowrap; gap: .4rem; min-width: 0; }
body.dock-compact .topbar .topnav { display: flex; gap: .3rem; min-width: 0; flex-shrink: 1; }
body.dock-compact:not(.dock-thumb) .topbar { flex-wrap: wrap; }
@media (max-width: 379px) {
  body.dock-compact .topbar .brand { gap: .4rem; }
  body.dock-compact .topbar .brand-mark { width: 22px; height: 22px; }
  body.dock-compact .topbar .brand-word { width: 73px; height: 11px; }
  body.dock-compact .topbar .nav-link { font-size: .8rem; padding: .28rem .45rem; }
}
@media (max-width: 359px) {   /* was 339: 340-359 px phones overlapped once the demo index sat in the bar (2026-10-15) */
  body.dock-compact .topbar .nav-link { font-size: .72rem; padding: .25rem .35rem; }
  body.dock-compact .stage-dock .btn, body.dock-compact .dock-nav button, body.dock-compact .dock-gear, body.dock-compact .dock-music { min-width: 1.5rem; padding-left: .1rem; padding-right: .1rem; }
  body.dock-compact .stage-dock [data-icon]::before { font-size: .74rem; }
}
/* No name / role labels on the readers in the reader (Dom 2026-10-12: obvious, wastes space). The speaking
   cue is now light: while audio plays, the readers who are not speaking dim a little. */
.reader.has-stage .vslot-cap, .reader.has-stage .vslot-ph { display: none !important; }
.reader.has-stage .vslot { transition: opacity .35s ease, filter .35s ease; }
.reader.has-stage[data-playing="1"]:where(:not([data-layout="spotlight"]):not(:is([data-layout="closeup"], [data-layout="lips"])):not([data-layout="cinema"]):not([data-layout="classic"])) .vslot { opacity: .5; filter: saturate(.75); }
.reader.has-stage[data-playing="1"][data-speaker="poet"] .vslot-poet,
.reader.has-stage[data-playing="1"][data-speaker="trans"] .vslot-trans,
.reader.has-stage[data-playing="1"][data-speaker="lesson"] .vslot-lesson { opacity: 1; filter: none; }
/* Next poem directly under the poem; the rating section follows with less air above it */
.reader.has-stage .poem-next { text-align: center; margin: .5rem 0 .25rem; }
.reader.has-stage .poem-next .recall-next { margin: 0 auto; }
.reader.has-stage .recall { margin-top: 1rem; padding-top: 1rem; }
/* Phones, split view: the page is exactly the visible screen (dvh tracks iOS's collapsing toolbars; svh is
   the fallback) and cannot scroll, so the karaoke can never push the readers up (Dom 2026-10-12). */
html.phone-split-root { overflow: hidden; height: 100%; overscroll-behavior: none; }
body.phone-split { height: 100svh; height: 100dvh; overscroll-behavior: none; }
/* Phones: the poem pane's top edge sliced the text in a hard line under the avatar (Dom 2026-10-12).
   Lines now melt out over the top 28 px of the pane as they scroll up; the first line starts below the fade. */
body.phone-split .reader.has-stage .stage-main {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, rgba(0,0,0,.35) 10px, #000 28px);
  mask-image: linear-gradient(to bottom, transparent 0, rgba(0,0,0,.35) 10px, #000 28px);
  padding-top: 1.1rem;
}

/* Stage, Duet and Cinema on a phone (Dom 2026-10-12): the same pinned top as Trio/Spotlight, each keeping its
   own arrangement. Stage and Cinema show whoever is speaking (Cinema full width with captions over the face,
   a bigger share of the screen); Duet keeps the poet beside the translator, or the tutor during a lesson. */
body.phone-split .reader[data-layout="stage"] .stage-grid,
body.phone-split .reader[data-layout="cinema"] .stage-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "stage" "main"; }
body.phone-split .reader[data-layout="cinema"] .stage-grid { grid-template-rows: calc(var(--avatar-h) + 8svh) minmax(0, 1fr); }
body.phone-split .reader[data-layout="stage"] .vslot,
body.phone-split .reader[data-layout="cinema"] .vslot { grid-area: stage; display: none; position: relative; top: auto; height: 100%; margin: 0; }
body.phone-split .reader[data-layout="stage"] .vslot { width: auto; aspect-ratio: 9 / 16; justify-self: center; }
body.phone-split .reader[data-layout="cinema"] .vslot { width: 100%; aspect-ratio: auto; border-radius: 12px; }
body.phone-split .reader[data-layout="stage"][data-speaker="poet"] .vslot-poet,
body.phone-split .reader[data-layout="stage"][data-speaker="trans"] .vslot-trans,
body.phone-split .reader[data-layout="stage"][data-speaker="lesson"] .vslot-lesson,
body.phone-split .reader[data-layout="cinema"][data-speaker="poet"] .vslot-poet,
body.phone-split .reader[data-layout="cinema"][data-speaker="trans"] .vslot-trans,
body.phone-split .reader[data-layout="cinema"][data-speaker="lesson"] .vslot-lesson { display: block; }
body.phone-split .reader[data-layout="cinema"] .stage-caption {
  grid-area: stage; position: relative; top: auto; height: auto; margin: 0; align-self: end; border-radius: 0 0 12px 12px;
}
body.phone-split .reader[data-layout="duet"] .stage-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-areas: "poet side" "main main";
}
body.phone-split .reader[data-layout="duet"] .stage-side { display: block; grid-area: side; height: 100%; min-height: 0; }
body.phone-split .reader[data-layout="duet"] .vslot-trans,
body.phone-split .reader[data-layout="duet"] .vslot-lesson { height: 100%; }
body.phone-split .reader[data-layout="duet"] .vslot-lesson { display: none; }
body.phone-split .reader[data-layout="duet"][data-lesson-on="1"] .vslot-lesson { display: block; }
body.phone-split .reader[data-layout="duet"][data-lesson-on="1"] .vslot-trans { display: none; }

/* ── Close-up (Dom 2026-10-12): a second Spotlight. The speaker takes about two thirds of the screen, a closer
   crop of the face; under it, only the line being read with its translation and lesson note, which moves on
   as the next line is spoken (video_stage.js closeupTrack). Readers crossfade as in Spotlight. ── */
.layout-ico-closeup { background-image: linear-gradient(var(--gold), var(--gold)), linear-gradient(#3b2f2a, #3b2f2a); background-size: 44px 24px, 30px 6px; background-position: 4px 3px, 11px 30px; }
.reader.has-stage:is([data-layout="closeup"], [data-layout="lips"]) { container-type: normal; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-grid {
  grid-template-columns: minmax(0, 1fr); grid-template-areas: "stage" "main"; gap: .8rem;
  grid-template-rows: calc(100vh - var(--topbar-h, 4.5rem) - 12.5rem) auto;
}
.reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-side { display: contents; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .vslot {
  grid-area: stage; display: block; height: 100%; width: auto; aspect-ratio: 9 / 16; max-width: 100%;
  justify-self: center; opacity: 0; pointer-events: none; transition: opacity .6s ease;
}
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="poet"] .vslot-poet,
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="trans"] .vslot-trans,
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="lesson"] .vslot-lesson { opacity: 1; pointer-events: auto; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-main { grid-area: main; max-width: 44rem; width: 100%; justify-self: center; text-align: center; }
/* the pane: one line, everything else of the poem tucked away (Next poem still shows at the last line) */
.reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-main > :not(.poem-body):not(.cu-bar):not(.poem-next):not(.recall) { display: none !important; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body > :not(.line):not(.stanza) { display: none !important; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .stanza { margin: 0 !important; padding: 0 !important; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line:not(.cu-current) { display: none !important; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current {
  display: block; margin: 0 auto; animation: cu-in .35s ease-out;
}
/* The line, its translation and its note fill the pane (Dom 2026-10-13: "the third of the screen must be filled",
   "easier on the eye"): video_stage.js fitLine() sets --cu-fs on the poem body, the largest scale at which the
   block still fits the space under the face, re-done on every line and on rotation. Base sizes below x scale. */
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .src { display: block; font-size: calc(1.45em * var(--cu-fs, 1)); font-weight: 700; line-height: 1.3; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .gloss { display: block; visibility: visible; opacity: 1; font-size: calc(1.05em * var(--cu-fs, 1)); margin-top: .3rem; color: var(--ink); line-height: 1.35; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .note { display: block; visibility: visible; opacity: 1; font-size: calc(.85em * var(--cu-fs, 1)); margin-top: .45rem; color: var(--ink-soft); line-height: 1.35; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current.speaking .src { font-size: calc(1.45em * var(--cu-fs, 1)); }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current.speaking .gloss { font-size: calc(1.05em * var(--cu-fs, 1)); }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body:not([data-cu-last="1"]) ~ .poem-next,
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body:not([data-cu-last="1"]) ~ .recall { display: none !important; }
@keyframes cu-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current { animation: none; } }
.cu-bar { display: none; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .cu-bar {
  display: flex; align-items: center; justify-content: center; gap: .9rem; margin: .5rem 0 .2rem; color: var(--ink-faint); font-size: .8rem;
}
.reader:is([data-layout="closeup"], [data-layout="lips"]) .cu-step {
  background: none; border: 1px solid var(--rule); color: var(--ink-soft); border-radius: 999px; width: 2rem; height: 2rem;
  font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.reader:is([data-layout="closeup"], [data-layout="lips"]) .cu-step:disabled { opacity: .3; cursor: default; }
/* phones: the face fills the top two thirds edge to edge, a closer crop; the line sits in the bottom third */
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-grid {
  grid-template-columns: minmax(0, 1fr); grid-template-areas: "stage" "main";
  grid-template-rows: calc(66svh - var(--topbar-h, 7rem)) minmax(0, 1fr);
}
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .vslot { grid-area: stage; width: 100%; aspect-ratio: auto; border-radius: 14px; }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-main { -webkit-mask-image: none; mask-image: none; padding-top: .6rem; display: flex; flex-direction: column; justify-content: safe center; }
/* phones: the couplet stays on one row (16-18 characters), and line + translation + note + counter fit the bottom third */
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-grid { grid-template-rows: calc(64svh - var(--topbar-h, 7rem)) minmax(0, 1fr); }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .src { font-size: calc(min(1.45em, (100cqi - 3rem) / 16.6) * var(--cu-fs, 1)); }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .gloss { font-size: calc(.95rem * var(--cu-fs, 1)); line-height: 1.35; margin-top: .25rem; }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body .line.cu-current .note { font-size: calc(.8rem * var(--cu-fs, 1)); line-height: 1.35; margin-top: .3rem; }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .cu-bar { margin: .35rem 0 0; }
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .cu-step { width: 1.8rem; height: 1.8rem; }

/* The "show the controls" corner button (Dom 2026-10-12: too discreet, people must know the bar is there):
   fluorescent green, bigger, a soft glow, and three pulses each time it appears. The ▾ that hides the bar
   is the same green, so the pair reads as one control. */
body.dock-thumb.dock-tucked .dock-reveal:not([hidden]) {
  width: 3.1rem; height: 3.1rem; right: .7rem; bottom: calc(.7rem + env(safe-area-inset-bottom));
  background: #39ff14; color: #062b00; border: 2px solid #b6ff9e; font-size: 1.35rem; font-weight: 900;
  box-shadow: 0 0 0 3px rgba(57, 255, 20, .25), 0 0 18px 4px rgba(57, 255, 20, .55), 0 6px 16px rgba(0, 0, 0, .45);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  animation: reveal-pulse 1.4s ease-out 3;
}
body.dock-thumb.dock-tucked .dock-reveal:not([hidden]):active { transform: scale(.94); }
body.dock-thumb .dock-tuck { background: #39ff14 !important; color: #062b00 !important; border-color: #b6ff9e !important; font-weight: 900; }
@keyframes reveal-pulse {
  0% { box-shadow: 0 0 0 0 rgba(57, 255, 20, .7), 0 0 18px 4px rgba(57, 255, 20, .55), 0 6px 16px rgba(0, 0, 0, .45); }
  70% { box-shadow: 0 0 0 16px rgba(57, 255, 20, 0), 0 0 18px 4px rgba(57, 255, 20, .55), 0 6px 16px rgba(0, 0, 0, .45); }
  100% { box-shadow: 0 0 0 3px rgba(57, 255, 20, .25), 0 0 18px 4px rgba(57, 255, 20, .55), 0 6px 16px rgba(0, 0, 0, .45); }
}
@media (prefers-reduced-motion: reduce) { body.dock-thumb.dock-tucked .dock-reveal:not([hidden]) { animation: none; } }

/* Laptop / tablet widths: the controls fall back to their icon labels when the words don't fit beside the
   links (video_stage.js fitDock). Hover still shows each button's full name. */
body.dock-icons .stage-dock { gap: .5rem; }
body.dock-icons .dock-play, body.dock-icons .dock-nav { gap: .25rem; }
body.dock-icons .stage-dock [data-icon] { font-size: 0 !important; padding: .4rem .5rem; min-width: 2.1rem; justify-content: center; }
body.dock-icons .stage-dock [data-icon]::before { content: attr(data-icon); font-size: .9rem; line-height: 1; white-space: nowrap; }

/* Phones in the reader: the top bar folds away to the logo alone, top left (video_stage.js barAuto). The bar
   floats over the page, so the readers get its room; tapping the logo brings it down for a few seconds. */
body.bar-auto .topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 45;
  transition: background-color .2s ease, border-color .2s ease, box-shadow .2s ease;
}
body.bar-auto .topbar > :not(.brand) { transition: opacity .2s ease, visibility .2s ease; }
body.bar-auto:not(.bar-open) .topbar {
  background: transparent; border-bottom-color: transparent; box-shadow: none; pointer-events: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
body.bar-auto:not(.bar-open) .topbar > :not(.brand) { opacity: 0; visibility: hidden; }
body.bar-auto:not(.bar-open) .topbar .brand {
  pointer-events: auto; padding: .2rem .65rem .25rem; border-radius: 999px; color: #fff;   /* the badge as on the videos: white on dark */
  background: rgba(0, 0, 0, .5); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
body.bar-auto.bar-open .topbar { background: var(--paper); box-shadow: 0 8px 22px rgba(0, 0, 0, .4); }
body.bar-auto.phone-split .reader.has-stage .stage-grid { padding-top: calc(.3rem + env(safe-area-inset-top)); }

/* Phones: the rating question pinned to the bottom of the poem pane (video_stage.js pinRecall) */
.recall.recall-pinned { position: sticky; z-index: 3; margin-top: 2rem; padding-top: .9rem; }
.recall.recall-pinned > p { margin-bottom: .9rem; }

/* The green "show the controls" button, take two (Dom 2026-10-12: far too big): small and discreet, still
   luminous green, bottom right, sitting just above the pinned rating question instead of over it. */
body.dock-thumb.dock-tucked .dock-reveal:not([hidden]) {
  width: 2rem; height: 2rem; right: .6rem; font-size: .8rem; font-weight: 900;
  bottom: calc(max(.6rem, var(--recall-peek, 0px) - .4rem) + env(safe-area-inset-bottom));
  background: #39ff14; color: #062b00; border: 1px solid #b6ff9e;
  box-shadow: 0 0 8px 1px rgba(57, 255, 20, .55);
  animation: reveal-pulse-sm 1.6s ease-out 1;
}
@keyframes reveal-pulse-sm {
  0% { box-shadow: 0 0 0 0 rgba(57, 255, 20, .6), 0 0 8px 1px rgba(57, 255, 20, .55); }
  100% { box-shadow: 0 0 0 9px rgba(57, 255, 20, 0), 0 0 8px 1px rgba(57, 255, 20, .55); }
}
body.dock-thumb .dock-tuck { font-weight: 700; }
/* Close-up, last line: content starts from the top so Next poem never sits under the pinned question */
body.phone-split .reader:is([data-layout="closeup"], [data-layout="lips"]) .stage-main.has-pinned-recall { justify-content: flex-start; }
.cu-nextpoem { display: none; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-next { display: none !important; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body[data-cu-last="1"] + .cu-bar .cu-step:nth-of-type(2) { display: none; }
.reader:is([data-layout="closeup"], [data-layout="lips"]) .poem-body[data-cu-last="1"] + .cu-bar .cu-nextpoem {
  display: inline-flex; align-items: center; height: 2rem; padding: 0 .9rem; border-radius: 999px; cursor: pointer;
  background: none; border: 1px solid var(--rule); color: var(--ink); font: inherit; font-size: .85rem; font-weight: 600;
}

/* Take three (Dom 2026-10-12): not a solid green disc. A dark button with a luminous green ring and a soft
   halo, the green ☰ menu sign in the middle. */
body.dock-thumb.dock-tucked .dock-reveal:not([hidden]) {
  background: rgba(0, 0, 0, .55); color: #39ff14; border: 1.5px solid #39ff14;
  font-size: 1.1rem; font-weight: 400; line-height: 1;
  box-shadow: 0 0 6px 1px rgba(57, 255, 20, .5), inset 0 0 4px rgba(57, 255, 20, .25);
  text-shadow: 0 0 4px rgba(57, 255, 20, .7);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  animation: reveal-halo 1.6s ease-out 1;
}
@keyframes reveal-halo {
  0% { box-shadow: 0 0 0 0 rgba(57, 255, 20, .55), 0 0 6px 1px rgba(57, 255, 20, .5), inset 0 0 4px rgba(57, 255, 20, .25); }
  100% { box-shadow: 0 0 0 9px rgba(57, 255, 20, 0), 0 0 6px 1px rgba(57, 255, 20, .5), inset 0 0 4px rgba(57, 255, 20, .25); }
}
body.dock-thumb .dock-tuck {
  background: transparent !important; color: #39ff14 !important; border: 1.5px solid #39ff14 !important;
  box-shadow: 0 0 5px rgba(57, 255, 20, .4); text-shadow: 0 0 4px rgba(57, 255, 20, .6);
}

/* Wide and ultra-wide screens, Spotlight (Dom 2026-10-12: "so much wasted space"). The page loses its 1600px
   cap, the speaker pins to the right edge (left edge when swapped), and the poem takes all the rest. Its text
   scales with the screen so it reads from the sofa; the reader's own text size setting still applies on top. */
@media (min-width: 1400px) {
  body.stage-wide .app:has(.reader[data-layout="spotlight"]) { max-width: none; padding-inline: 3vw; }
  .reader[data-layout="spotlight"] .stage-grid { grid-template-columns: minmax(0, 1fr) var(--spot-w); gap: 4vw; }
  .reader[data-layout="spotlight"][data-side="left"] .stage-grid { grid-template-columns: var(--spot-w) minmax(0, 1fr); }
  .reader[data-layout="spotlight"] .vslot { left: auto; right: 3vw; }
  .reader[data-layout="spotlight"][data-side="left"] .vslot { right: auto; left: 3vw; }
  .reader[data-layout="spotlight"] .stage-main { max-width: 48em; font-size: clamp(1rem, min(1.15vw, 2.6vh), 2.4rem); }
  :where(.reader[data-layout="spotlight"]) .stage-main .poem-body { font-size: 1.3em; }
  /* the translation and notes grow with the poem (their base sizes are fixed rem) */
  .reader[data-layout="spotlight"] .stage-main .poem-body .line .gloss { font-size: .73em; }
  .reader[data-layout="spotlight"] .stage-main .poem-body .line .note { font-size: .63em; }
  .reader[data-layout="spotlight"] .stage-main .poem-body .line.speaking .gloss { font-size: 1.14em; }
}
/* Desktop with the folded bar: the readers and poem use the top of the screen; the logo floats top-left */
@media (min-width: 900px) {
  body.bar-auto .app { padding-top: 3.4rem; }
  body.bar-auto:not(.bar-open) .topbar .brand { padding: .3rem .8rem; }
}

/* Speaker changes (Dom 2026-10-12: "too harsh"). A focus-pull dissolve: the outgoing reader drifts out of
   focus, dims and eases a touch closer as it fades; the incoming one sharpens out of that blur and settles.
   Every layout that swaps readers in one place uses it (Spotlight, Close-up, Cinema, Classic's bubble, and
   Stage on a phone, which used to hard-cut). Long, slow-out curves; reduce-motion gets a plain soft fade. */
.reader[data-layout="spotlight"] .vslot,
.reader:is([data-layout="closeup"], [data-layout="lips"]) .vslot,
.reader[data-layout="cinema"] .vslot,
.reader[data-layout="classic"] .vslot,
body.phone-split .reader[data-layout="stage"] .vslot {
  display: block; opacity: 0; pointer-events: none;
  filter: blur(14px) saturate(.7) brightness(.8);
  transform: scale(1.035);
  transition:
    opacity 1.1s cubic-bezier(.33, 0, .15, 1),
    filter 1.3s cubic-bezier(.33, 0, .15, 1),
    transform 1.6s cubic-bezier(.22, 1, .36, 1);
  will-change: opacity, filter, transform;
}
.reader[data-layout="spotlight"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="spotlight"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="spotlight"][data-speaker="lesson"] .vslot-lesson,
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="poet"] .vslot-poet,
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="trans"] .vslot-trans,
.reader:is([data-layout="closeup"], [data-layout="lips"])[data-speaker="lesson"] .vslot-lesson,
.reader[data-layout="cinema"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="cinema"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="cinema"][data-speaker="lesson"] .vslot-lesson,
.reader[data-layout="classic"][data-speaker="poet"] .vslot-poet,
.reader[data-layout="classic"][data-speaker="trans"] .vslot-trans,
.reader[data-layout="classic"][data-speaker="lesson"] .vslot-lesson,
body.phone-split .reader[data-layout="stage"][data-speaker="poet"] .vslot-poet,
body.phone-split .reader[data-layout="stage"][data-speaker="trans"] .vslot-trans,
body.phone-split .reader[data-layout="stage"][data-speaker="lesson"] .vslot-lesson {
  opacity: 1; pointer-events: auto; filter: none; transform: none;
}
/* the readers who stay on screen (Trio, Duet) dim and brighten just as softly */
.reader.has-stage .vslot { transition: opacity .9s cubic-bezier(.33, 0, .15, 1), filter .9s cubic-bezier(.33, 0, .15, 1); }
@media (prefers-reduced-motion: reduce) {
  .reader[data-layout="spotlight"] .vslot, .reader:is([data-layout="closeup"], [data-layout="lips"]) .vslot, .reader[data-layout="cinema"] .vslot,
  .reader[data-layout="classic"] .vslot, body.phone-split .reader[data-layout="stage"] .vslot {
    filter: none; transform: none; transition: opacity .5s linear;
  }
}
/* phone Stage swaps readers in one place too, so while playing the hidden ones stay hidden (not dimmed) */
body.phone-split .reader.has-stage[data-playing="1"][data-layout="stage"] .vslot { opacity: 0; filter: blur(14px) saturate(.7) brightness(.8); }
body.phone-split .reader.has-stage[data-playing="1"][data-layout="stage"][data-speaker="poet"] .vslot-poet,
body.phone-split .reader.has-stage[data-playing="1"][data-layout="stage"][data-speaker="trans"] .vslot-trans,
body.phone-split .reader.has-stage[data-playing="1"][data-layout="stage"][data-speaker="lesson"] .vslot-lesson { opacity: 1; filter: none; transform: none; }
/* :where() above keeps the dimming rule weaker than "the speaker stays bright" (its :not() list used to
   outrank it, so in Trio every reader, the speaker included, sat at half opacity while audio played). */
body.phone-split .reader[data-layout="cinema"] .vslot { display: block; }

/* The dock's scrolling track (video_stage.js): desktop lays it out exactly as before; on phones it scrolls
   sideways when the buttons don't fit (Dom 2026-10-12: off-screen buttons were unreachable), while the gear
   and ▾ sit outside it, pinned right, so the settings panel is never clipped. Scrollbar hidden. */
.dock-track { display: flex; align-items: center; gap: inherit; flex: 1 1 auto; min-width: 0; }
body.dock-thumb .dock-track, body.dock-compact .dock-track {
  overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap; justify-content: flex-start;
  -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; scrollbar-width: none;
  scroll-snap-type: x proximity; scroll-padding-inline: .35rem; gap: .15rem;
}
body.dock-thumb .dock-track::-webkit-scrollbar, body.dock-compact .dock-track::-webkit-scrollbar { display: none; }
body.dock-thumb .dock-track > *, body.dock-compact .dock-track > * { flex: 0 0 auto; scroll-snap-align: start; }
body.dock-thumb .dock-track .dock-play, body.dock-thumb .dock-track .dock-nav { display: contents; }
body.dock-thumb .dock-gear-wrap, body.dock-thumb .dock-tuck { flex: 0 0 auto; }
/* All three asks for a tap when the phone refused to autoplay after Next */
.stage-dock .play-all.needs-tap { animation: reveal-halo 1.2s ease-out 3; border-color: #39ff14 !important; }
.dock-build { margin-top: .6rem; font-size: .7rem; color: var(--ink-faint); text-align: right; letter-spacing: .04em; }

/* ── Lips (Dom 2026-10-12): Close-up's one line, translation and lesson, with the speaker zoomed right in on the
   mouth, for a learner who wants to watch the lips on a hard line. video_stage.js lipsFrame() moves each
   video (transform from its top-left corner) along that clip's mouth track (video_pilot/mouth/<stem>.json),
   so the mouth stays centred as the head moves. Centred framing here, so the maths can assume 50% 50%. ── */
.layout-ico-lips { background-image: radial-gradient(ellipse 15px 7px at 50% 40%, var(--gold) 98%, transparent), linear-gradient(#3b2f2a, #3b2f2a); background-size: 100% 100%, 30px 6px; background-position: 0 0, 11px 30px; background-repeat: no-repeat; }
.reader[data-layout="lips"] .vslot { background: #000; }
.reader[data-layout="lips"] .vslot :is(video, .vslot-poster) { object-position: 50% 50%; transform-origin: 0 0; will-change: transform; }
.reader[data-layout="lips"] .vslot-cap { display: none; }

/* ── Gestures (Dom 2026-10-12): the faces are ours to swipe (down/up = next/previous view, left/right = next/
   previous poem), so the browser must not scroll or pull-to-refresh from them; a pill names where you landed. ── */
.reader.has-stage .vslot { touch-action: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.swipe-toast {
  position: fixed; left: 50%; top: 30%; transform: translate(-50%, -50%) scale(.96); z-index: 60;
  padding: .55rem 1.1rem; border-radius: 999px; background: rgba(12, 10, 9, .72); color: #f4ede4;
  font-size: 1rem; font-weight: 600; letter-spacing: .01em; pointer-events: none; opacity: 0;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: 0 4px 24px rgba(0, 0, 0, .35);
  transition: opacity .25s ease, transform .35s cubic-bezier(.22, 1, .36, 1);
}
.swipe-toast.on { opacity: 1; transform: translate(-50%, -50%) scale(1); transition-duration: .15s, .3s; }
@media (prefers-reduced-motion: reduce) { .swipe-toast { transition: opacity .2s linear; transform: translate(-50%, -50%); } }
/* phone debug log (?dbg=1): see video_stage.js dbg() */
.stage-dbg { position: fixed; left: 0; top: 0; z-index: 9999; margin: 0; max-width: 100vw; padding: 2px 4px; font: 10px/1.25 ui-monospace, Menlo, monospace; color: #9f9; background: rgba(0,0,0,.72); pointer-events: none; white-space: pre-wrap; }

/* Title and poet in the top bar (Dom 2026-10-13): a pill after the logo while a poem is open; with the folded
   bar it shows for a few seconds when the poem opens (body.bar-peek) and whenever the bar is brought back. */
.topbar .bar-poem { display: none; }
body.stage-docked .topbar .bar-poem {
  display: inline-flex; align-items: baseline; gap: .45rem; min-width: 0; max-width: min(60vw, 34rem);
  margin-left: .4rem; padding: .25rem .75rem; border-radius: 999px; font-size: .85rem; line-height: 1.2;
  color: var(--ink); background: rgba(0, 0, 0, .5); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
}
.topbar .bar-poem b { font-family: 'Noto Serif SC', 'Songti SC', serif; font-weight: 600; font-size: .95rem; overflow: hidden; text-overflow: ellipsis; }
.topbar .bar-poem span { color: var(--ink-soft); font-size: .78rem; overflow: hidden; text-overflow: ellipsis; }
body.bar-auto.bar-peek:not(.bar-open) .topbar .bar-poem { opacity: 1; visibility: visible; pointer-events: auto; }
body.bar-auto.bar-open .topbar .bar-poem { background: var(--paper-raised); }
/* phones: the pill takes a row of its own under the logo (the bar already wraps to two rows), so the poet's
   name is never squeezed out by the nav buttons */
@media (max-width: 899px) {
  body.stage-docked .topbar { flex-wrap: wrap; }
  body.stage-docked .topbar .bar-poem { flex: 1 1 100%; order: 10; max-width: none; margin: .15rem 0 0; font-size: .8rem; padding: .2rem .6rem; justify-content: center; }
  body.bar-auto:not(.bar-open) .topbar .bar-poem { flex: 0 1 auto; max-width: calc(100vw - 1.5rem); margin-left: .5rem; }
}
@media (prefers-reduced-motion: no-preference) { .topbar .bar-poem { transition: opacity .25s ease, visibility .25s ease; } }

/* ── Word-by-word karaoke (Dom 2026-10-12): inside the enlarged line being read, the character / word
   being spoken grows a further step. Transform only (inline-block so it can scale), so the line never
   reflows; it grows upward from its baseline. Spans exist only on the speaking line (video_stage.js). */
/* Grows in 60 ms and lets go at once (2026-10-13): the old 0.11 s both ways was longer than a quick syllable, so a
   fast character was already shrinking before it had visibly grown, and two overlapped mid-transition. */
.reader.has-stage .line .tok { display: inline-block; transform-origin: 50% 100%; transition: none; }
.reader.has-stage .line.speaking .tok.now { transform: scale(1.25); color: var(--accent); transition: transform .06s ease-out, color .06s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .reader.has-stage .line .tok { transition: none; }
  .reader.has-stage .line.speaking .tok.now { transform: none; text-decoration: underline 2px var(--accent); text-underline-offset: .18em; }
}

/* Demo index dropdown between ‹ and › (2026-10-15). Native select: the full "n. title · English" list opens in
   the platform picker; the closed control just shows the current one, clipped to its width. */
.dock-jump { font: inherit; font-size: .82rem; color: var(--ink-soft); background: none; border: 1px solid var(--rule);
  border-radius: 8px; padding: .38rem .45rem; min-width: 5.5rem; max-width: 13rem; text-overflow: ellipsis; cursor: pointer; flex: 0 1 auto; }
.dock-jump option { color: initial; background: initial; }
body.dock-icons .dock-jump { max-width: 8.5rem; }
body.dock-compact .dock-jump, body.dock-thumb .dock-jump { min-width: 3.4rem; max-width: 5.6rem; padding: .3rem .25rem; font-size: .76rem; }
@media (max-width: 379px) { body.dock-thumb .dock-jump { min-width: 2.6rem; max-width: 3.1rem; padding: .3rem .15rem; font-size: .72rem; } }

/* Music off (the default since Dom 2026-10-20): the bar's 🔇 wears the same luminous green ring as the ▾ / ☰. */
.stage-dock .dock-music.is-muted, .dock-music.is-muted {
  background: transparent !important; color: #39ff14 !important; border: 1.5px solid #39ff14 !important;
  box-shadow: 0 0 5px rgba(57, 255, 20, .4); text-shadow: 0 0 4px rgba(57, 255, 20, .6); border-radius: 999px;
}

/* Volume (Dom 2026-10-20 night): Voices + Music sliders at the top of ⚙, and a pop-up over the music button
   (hover with a mouse, long-press on a phone). The sliders reuse the voice-speed slider's look. */
.vol-row { display: flex; flex-direction: column; gap: .35rem; }
.vol-head { display: flex; align-items: center; gap: .45rem; font-size: .85rem; }
.vol-icon { font-size: .95rem; }
.vol-title { font-weight: 500; color: var(--ink); }
.vol-val {
  margin-left: auto; font-weight: 600; font-size: .85rem; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  padding: .1rem .5rem; border-radius: 6px; min-width: 3.2em; text-align: center;
}
.vol-row.vol-locked .vol-slider { opacity: .45; cursor: not-allowed; }
.vol-note { font-size: .74rem; color: var(--ink-faint); }
.dock-panel .trans-speed-panel { margin-top: 0; padding-top: 0; border-top: 0; }
.vol-pop {
  position: fixed; z-index: 120; width: min(240px, 80vw); padding: .75rem .85rem; display: flex; flex-direction: column; gap: .7rem;
  background: var(--paper-raised); border: 1.5px solid #39ff14; border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0,0,0,.5), 0 0 8px rgba(57, 255, 20, .25);
}
.vol-pop[hidden] { display: none; }
.dock-music { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; touch-action: manipulation; }

/* AI-generated label on every face (S2, AI Act art. 50): small, persistent, never over the mouth. */
.vslot-ai { position: absolute; left: 6px; bottom: 6px; z-index: 4; font: 600 9px/1.2 Inter, system-ui, sans-serif; letter-spacing: .04em;
  text-transform: uppercase; color: rgba(255,255,255,.88); background: rgba(0,0,0,.45); padding: 2px 5px; border-radius: 4px; pointer-events: none; }
