:root {
  --ink: #04060a;
  --paper: #e9dfcb;
  --gold: #d4a65c;
  --gold-bright: #f0d6a4;
  --gold-soft: color-mix(in srgb, var(--gold) 47%, transparent);
  --blue: #39c6ff;
  --ember: #ff9a3c;
  --muted: #a69d90;
}

/* Chapter 7 — The Last Audit. The SOC 2 shadow bleaches the archive: the warm
   archive gold cools to sterile evidence white, the accent blue sharpens to
   Griddler cyan, and the Worry List ember becomes the only warmth left on
   screen. Set by applyChapterDom(); every other chapter keeps the series gold. */
:root[data-chapter="07"] {
  --gold: #ccd7e1;
  --gold-bright: #eaf2f8;
  --gold-soft: color-mix(in srgb, var(--gold) 47%, transparent);
  --blue: #6fe3ff;
  --ember: #ff8a2b;
}
/* The gate title and eyebrow carry their own gilt gradients rather than the
   --gold token, so Chapter 7 relights them directly: audit light, not gilt. */
/* background-image, NOT the `background` shorthand: the shorthand resets
   background-clip to border-box, which paints the gradient over the title's box
   while the glyphs stay color:transparent — a grey slab where the title was. */
:root[data-chapter="07"] .gate-title {
  background-image: linear-gradient(180deg, #ffffff, #cfdce6 58%, #7d8b98);
  text-shadow: 0 0 70px rgba(207, 220, 230, .38), 0 2px 30px rgba(0, 0, 0, .8);
}
:root[data-chapter="07"] .gate-eyebrow { color: #dbe6ef; }
:root[data-chapter="07"] .gate-quote { color: #dfe7ee; }

/* Chapter 8 — Beyond the Beta Line. The audit's white drains back out and the
   chapter goes cartographic: the series gold returns warmer and brighter than it
   has ever been, because Chapter 7 bleached it and this chapter hands it back at
   empire scale. Blue cools towards telemetry; the ember becomes G-RIDL's stage
   magenta. Set by applyChapterDom(). */
:root[data-chapter="08"] {
  --gold: #e0b063;
  --gold-bright: #ffe6ad;
  --gold-soft: color-mix(in srgb, var(--gold) 47%, transparent);
  --blue: #5fd0ff;
  --ember: #ff5fa8;
}
:root[data-chapter="08"] .gate-title {
  background-image: linear-gradient(180deg, #ffeec2, #e0b063 58%, #8a6326);
  text-shadow: 0 0 70px rgba(224, 176, 99, .38), 0 2px 30px rgba(0, 0, 0, .8);
}
:root[data-chapter="08"] .gate-eyebrow { color: #f0d9a8; }
:root[data-chapter="08"] .gate-quote { color: #efe3cc; }

/* Chapter 9 — The Gates of Ralph. Chapter 8's cartography gold cools to gate
   iron: the accent becomes the cold grey of unpainted riveted metal, blue drops
   to the hold-amber of a status band that has not moved all day, and the ember
   becomes Ralph's arterial verdict red. This is the only chapter whose accent is
   a metal rather than a light. Set by applyChapterDom(). */
:root[data-chapter="09"] {
  --gold: #9aa4ad;
  --gold-bright: #d8e0e7;
  --gold-soft: color-mix(in srgb, var(--gold) 47%, transparent);
  --blue: #f0a825;
  --ember: #e0342c;
}
:root[data-chapter="09"] .gate-title {
  background-image: linear-gradient(180deg, #dfe6ec, #9aa4ad 58%, #4c545c);
  text-shadow: 0 0 70px rgba(154, 164, 173, .34), 0 2px 30px rgba(0, 0, 0, .85);
}
:root[data-chapter="09"] .gate-eyebrow { color: #c3ccd4; }
:root[data-chapter="09"] .gate-quote { color: #ccd4db; }

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--ink); color: var(--paper); overflow: hidden; overscroll-behavior: none; }
body { font-family: "IM Fell English", Georgia, serif; }
button { cursor: pointer; }
.mono { font: 10px "DM Mono"; letter-spacing: .16em; color: var(--gold); }

/* ---------- entrance gate ---------- */
.gate {
  position: fixed; inset: 0; z-index: 40;
  display: grid; place-items: center; overflow: hidden;
  background: var(--ink);
  transition: opacity .7s ease, visibility .7s;
}
.gate.open { opacity: 0; visibility: hidden; pointer-events: none; }
.gate-art { position: absolute; inset: 0; }
.gate-art img {
  width: 100%; height: 100%; object-fit: cover;
  filter: brightness(.95) saturate(1.12);
  animation: gate-zoom 34s ease-out both;
}
.gate-veil {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at 50% 46%, rgba(4, 6, 10, .38), rgba(4, 6, 10, .12) 55%, rgba(4, 6, 10, .72) 100%),
    linear-gradient(180deg, rgba(4, 6, 10, .6), rgba(4, 6, 10, .08) 30%, rgba(4, 6, 10, .1) 62%, rgba(4, 6, 10, .88));
}
.gate-frame {
  position: absolute; inset: clamp(14px, 3vw, 34px);
  border: 1px solid var(--gold-soft);
  clip-path: polygon(0 0, calc(100% - 26px) 0, 100% 26px, 100% 100%, 26px 100%, 0 calc(100% - 26px));
  animation: gate-breathe 5s ease-in-out infinite;
}
.gate-inner {
  position: relative; text-align: center; max-width: 1180px;
  width: min(1180px, calc(100vw - 64px)); box-sizing: border-box;
  padding: 0 26px;
}
.gate-eyebrow { letter-spacing: .5em; color: #e8c98c; text-shadow: 0 0 18px #000; animation: rise-in 1.4s .2s ease both; }
.gate-title {
  /* fit-content: with background-clip:text the gradient only paints inside
     the element's box, so a title wider than .gate-inner's 760px cap (e.g.
     "The Unassigned") gets its overflowing glyphs invisibly clipped. Let the
     box grow to the text and center it instead. */
  width: fit-content;
  margin: 20px auto 14px;
  padding: 0 .06em;
  font: 700 clamp(52px, 9vw, 110px)/1 Cinzel, serif;
  letter-spacing: .05em;
  background: linear-gradient(180deg, #fff3d6, #e8bd75 58%, #a87f3f);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 70px rgba(232, 189, 117, .38), 0 2px 30px rgba(0, 0, 0, .8);
  animation: rise-in 1.4s .5s ease both;
}
.gate-divider {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  color: var(--gold); font-size: 13px; margin-bottom: 14px;
  animation: rise-in 1.4s .65s ease both;
}
.gate-divider:before, .gate-divider:after {
  content: ""; width: clamp(60px, 12vw, 130px); height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--gold) 67%, transparent));
}
.gate-divider:after { background: linear-gradient(-90deg, transparent, color-mix(in srgb, var(--gold) 67%, transparent)); }
.gate-chapter { letter-spacing: .4em; color: #7fd6ff; text-shadow: 0 0 22px #0a3a55; animation: rise-in 1.4s .8s ease both; }
.gate-quote {
  min-height: 76px; margin: 30px auto 4px; max-width: 580px;
  font: italic 20px/1.55 "IM Fell English"; color: #e2d5b8;
  text-shadow: 0 2px 16px #000;
}
.gate-quote span { opacity: 0; display: inline-block; animation: word-in .9s ease forwards; }
.gate-enter {
  margin-top: 22px; padding: 16px 38px;
  background: #04060a88; backdrop-filter: blur(3px);
  border: 1px solid color-mix(in srgb, var(--gold) 73%, transparent); color: var(--gold-bright);
  font: 12px "DM Mono"; letter-spacing: .3em;
  transition: .35s;
  animation: rise-in 1.4s 1.2s ease both, enter-pulse 2.8s 2.6s ease-in-out infinite;
}
.gate-enter:hover { color: var(--ink); background: var(--gold); box-shadow: 0 0 60px color-mix(in srgb, var(--gold) 60%, transparent); animation-play-state: running, paused; }
.gate-hint { margin-top: 16px; font: 9px "DM Mono"; letter-spacing: .2em; color: #6b6254; animation: rise-in 1.4s 1.5s ease both; }

/* ---------- player shell ---------- */
.player {
  position: fixed; inset: 0;
  /* The caption row sizes to its text (chapter 4 captions run 2-3 lines) and
     the stage takes the rest; minmax(0,...) lets the panel actually shrink. */
  display: grid; grid-template-rows: 54px minmax(0, 1fr) auto auto;
  isolation: isolate; overflow: hidden;
}
.backdrop { position: absolute; inset: 0; z-index: -3; overflow: hidden; background: #06080d; }
.backdrop img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: blur(46px) brightness(.22) saturate(1.15);
  transform: scale(1.18);
  opacity: 0; transition: opacity 1.6s ease;
  animation: backdrop-breathe 16s ease-in-out infinite;
}
.backdrop img.is-visible { opacity: 1; }
.backdrop:after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, transparent, rgba(4, 6, 10, .92) 84%);
}
#embers { position: fixed; inset: 0; z-index: 45; width: 100%; height: 100%; pointer-events: none; mix-blend-mode: screen; }
.noise {
  position: absolute; inset: 0; z-index: 4; pointer-events: none; opacity: .08;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- hud ---------- */
.hud {
  z-index: 6; display: flex; justify-content: space-between; align-items: center;
  padding: 0 clamp(16px, 3vw, 44px);
  border-color: #e9dfcb1f; border-style: solid;
}
.top-hud { border-width: 0 0 1px; }
.bottom-hud {
  border-width: 1px 0 0;
  /* Grows with the caption. The tick rail now occupies its own grid row below,
     so this no longer has to reserve overlay clearance in its own padding. */
  min-height: 96px; align-items: center; gap: 24px;
  padding-top: 12px; padding-bottom: 14px;
}
.wordmark { border: 0; background: none; color: var(--paper); text-align: left; font: 600 14px Cinzel, serif; letter-spacing: .18em; }
.wordmark span { display: block; color: var(--blue); font: 9px "DM Mono"; letter-spacing: .25em; margin-top: 3px; }
.top-hud p { font: 10px "DM Mono"; letter-spacing: .16em; color: var(--gold); }
.hud-actions, .controls { display: flex; gap: 10px; align-items: center; }
.hud-actions button, .controls button, .finale-actions button {
  background: #080b0eaa; border: 1px solid #e9dfcb33; color: var(--paper);
  padding: 9px 12px; font: 10px "DM Mono"; letter-spacing: .08em; transition: .25s;
}
.hud-actions button:hover, .controls button:hover, .finale-actions button:hover { color: var(--blue); border-color: var(--blue); box-shadow: 0 0 18px #39c6ff2e; }
.hud-actions b { color: var(--gold); }

/* ---------- stage ---------- */
.comic-stage { position: relative; width: 100%; min-width: 0; min-height: 0; display: grid; place-items: center; overflow: hidden; padding: 18px 34px; }
.comic-stage.shake { animation: shake .5s ease-out; }
.panel-frame {
  width: var(--panel-width, 100%); height: var(--panel-height, 100%); min-width: 0; max-width: 100%;
  position: relative; overflow: hidden;
  filter: drop-shadow(0 25px 55px #000c);
  transition: width .45s ease, height .45s ease, transform .14s ease-out;
}
.panel-frame:before {
  content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  border: 1px solid var(--gold-soft);
  clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));
}
.panel-frame:after {
  content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  box-shadow: inset 0 0 70px 12px #000a;
}
.pane { position: absolute; inset: 0; opacity: 0; transition: opacity .5s ease; }
.pane.is-visible { opacity: 1; z-index: 1; }
.pane.slow-fade { transition-duration: 1.5s; }
/* Undrawn panel in ?draft preview: a plate in the chapter's own palette instead
   of a broken-image glyph, so an in-production chapter still reads as the
   archive. The scene number comes from data-pending. */
.pane.is-pending { display: grid; place-items: center; background:
  repeating-linear-gradient(135deg, rgba(255,255,255,.014) 0 22px, transparent 22px 44px),
  radial-gradient(120% 90% at 50% 40%, color-mix(in srgb, var(--gold) 9%, transparent), transparent 70%),
  var(--ink);
  border: 1px solid var(--gold-soft); }
.pane.is-pending .panel-image { display: none; }
.pane.is-pending::after {
  content: attr(data-pending);
  font: 11px "DM Mono"; letter-spacing: .22em; color: var(--gold);
  opacity: .72; text-align: center; padding: 0 8%;
}
.panel-image { width: 100%; height: 100%; object-fit: cover; display: block; }

.flash { position: absolute; inset: 0; z-index: 2; opacity: 0; pointer-events: none; }
.flash.fire-blue { background: radial-gradient(circle, rgba(57, 198, 255, .38), transparent 62%); animation: flash .6s; }
.flash.fire-gold { background: radial-gradient(circle, rgba(255, 214, 140, .5), transparent 68%); animation: flash .5s; }
.sweep {
  position: absolute; inset: 0; z-index: 2; pointer-events: none; opacity: 0;
  background: linear-gradient(105deg, transparent 30%, #ffe9bf3d 50%, transparent 70%);
  transform: translateX(-100%);
}
.sweep.run { animation: sweep .8s ease-out; }

.stage-meta { position: absolute; left: 22px; top: 12px; z-index: 5; display: flex; gap: 9px; align-items: center; font: 11px "DM Mono"; }
.stage-meta i { width: 28px; height: 1px; background: var(--gold); }

.edge {
  position: absolute; z-index: 5; top: 50%; translate: 0 -50%;
  height: 130px; width: 52px; border: 0;
  background: linear-gradient(90deg, #04060a88, transparent);
  color: #e9dfcb99; font: 34px Cinzel, serif;
  opacity: .55; transition: .3s;
}
.edge:hover { opacity: 1; color: var(--blue); text-shadow: 0 0 18px var(--blue); }
.edge-back { left: 0; }
.edge-next { right: 0; background: linear-gradient(-90deg, #04060a88, transparent); }
.edge[disabled] { opacity: .1; pointer-events: none; }

/* ---------- dialogue overlay ---------- */
/* The full script's dialogue, staged over the panel while each line is being
   read, then gone. Consistent placement by kind: speakers hold the left and
   right edges (a speaker keeps their side), machine display text drops in
   from the top centre, asides rise from the bottom centre. At most two lines
   share the frame. Never intercepts clicks. */
.dialogue { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.dialogue-line {
  position: absolute; display: none;
  max-width: min(400px, 38%);
  padding: 9px 16px;
  background: #04060a; border: 1px solid #e9dfcb55; border-left: 3px solid var(--gold);
  box-shadow: 0 6px 26px #000d;
  transition: opacity .45s ease, transform .45s ease;
}
.dialogue-line.shown { display: block; }
.dialogue-line.slot-left  { left: 22px; top: 34%; animation: slide-left .5s ease both; }
.dialogue-line.slot-right { right: 22px; top: 52%; text-align: right; animation: slide-right .5s ease both; }
.dialogue-line.slot-top    { left: 50%; top: 20px; translate: -50% 0; max-width: min(560px, 64%); animation: slide-top .5s ease both; }
.dialogue-line.slot-bottom { left: 50%; bottom: 18px; translate: -50% 0; max-width: min(560px, 64%); animation: slide-bottom .5s ease both; }
/* animation:none releases the slide-in keyframe hold so the exit transition
   can take the transform/opacity back over. */
.dialogue-line.leaving { animation: none; opacity: 0; }
.dialogue-line.slot-left.leaving  { transform: translateX(-26px); }
.dialogue-line.slot-right.leaving { transform: translateX(26px); }
.dialogue-line.slot-top.leaving    { transform: translateY(-22px); }
.dialogue-line.slot-bottom.leaving { transform: translateY(22px); }
.dialogue-line small {
  display: block; margin-bottom: 1px;
  color: var(--gold); font: 9px "DM Mono"; letter-spacing: .18em;
}
.dialogue-line span { font: 18px/1.3 "IM Fell English"; color: #f4ecda; text-shadow: 0 1px 8px #000; }
.dialogue-line.is-display { border-left-color: var(--blue); background: #04121b; }
.dialogue-line.is-display span { font: 12px/1.5 "DM Mono"; letter-spacing: .08em; color: #b8e6ff; }
.dialogue-line.is-aside { border-left-color: #e9dfcb55; }
.dialogue-line.is-aside span { font-style: italic; font-size: 16px; color: #ded3bf; }
@keyframes slide-left   { from { opacity: 0; transform: translateX(-36px); } to { opacity: 1; } }
@keyframes slide-right  { from { opacity: 0; transform: translateX(36px); } to { opacity: 1; } }
@keyframes slide-top    { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; } }
@keyframes slide-bottom { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; } }

/* ---------- hotspot ---------- */
.hotspot {
  position: absolute; z-index: 5; border: 0; background: none;
  color: var(--blue); font: 10px "DM Mono"; letter-spacing: .1em;
  text-shadow: 0 0 14px #001827; translate: -50% -50%;
}
.hotspot span {
  display: block; width: 34px; height: 34px; margin: auto auto 7px;
  border: 1px solid var(--blue); border-radius: 50%;
  box-shadow: 0 0 0 8px #39c6ff1c, 0 0 26px var(--blue);
  animation: ping 1.8s infinite;
}
.hotspot.claimed { color: var(--gold); text-shadow: 0 0 14px #241500; }
.hotspot.claimed span { border-color: var(--gold-soft); box-shadow: 0 0 0 4px color-mix(in srgb, var(--gold) 8%, transparent), 0 0 14px color-mix(in srgb, var(--gold) 33%, transparent); animation: none; opacity: .75; }
.hotspot.bursting span { animation: burst .7s ease-out; }

/* ---------- captions ---------- */
.scene-copy { flex: 1 1 auto; min-width: 0; max-width: 72%; }
.scene-copy p { margin: 0 0 5px; color: var(--gold); font: 10px "DM Mono"; letter-spacing: .14em; }
.scene-copy h1 { margin: 0; font: clamp(17px, 1.8vw, 26px)/1.3 "IM Fell English"; text-shadow: 0 2px 18px #000; }
.scene-copy h1 span { opacity: 0; display: inline-block; animation: word-in .7s ease forwards; }
.controls { flex: 0 0 auto; flex-direction: column; align-items: end; }
.controls p { margin: 0; color: var(--muted); font: 9px "DM Mono"; }
kbd { color: var(--gold); font-family: inherit; }

/* ---------- scene ticks ---------- */
/* Own grid row, not an absolute overlay. As an overlay it sat on top of the
   footer, so any caption long enough to fill .bottom-hud ran straight through
   the rail (chapter 7's prose captions are 5-6 lines). In flow it can never
   collide, whatever the caption length. */
.ticks {
  position: relative; z-index: 8; width: 100%;
  display: flex; justify-content: center; gap: 7px; padding: 2px 12px 9px;
}
.tick {
  width: 14px; height: 4px; padding: 0; border: 0;
  background: #e9dfcb2a; transition: .3s;
}
.tick.has-lore { height: 7px; translate: 0 1px; }
.tick.done { background: color-mix(in srgb, var(--gold) 60%, transparent); }
.tick.current { background: var(--gold); box-shadow: 0 0 10px color-mix(in srgb, var(--gold) 67%, transparent); }
.tick.has-lore.found { background: var(--blue); box-shadow: 0 0 10px #39c6ff88; }
.tick:hover { background: var(--blue); }

/* ---------- finale ---------- */
.finale {
  position: fixed; inset: 0; z-index: 32;
  display: grid; place-content: center; text-align: center; gap: 14px;
  padding: 24px;
  background: radial-gradient(ellipse at 50% 110%, #100d07, #04060af2 62%);
  animation: finale-in 1.4s ease both;
}
.finale[hidden] { display: none; }
.finale .drawer-close { position: fixed; right: 22px; top: 14px; }
.finale h2 { margin: 4px 0; max-width: 640px; padding: 0 24px; font: italic 500 clamp(28px, 4.4vw, 48px)/1.25 "IM Fell English"; }
.finale h2 span { opacity: 0; display: inline-block; animation: word-in 1.1s ease forwards; }
.finale-stats { margin: 0; color: var(--muted); font: 11px "DM Mono"; letter-spacing: .12em; }
.finale-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-top: 16px; }
.finale-actions button { padding: 13px 18px; }

/* ---------- archive drawer & dialog ---------- */
.archive-drawer {
  position: fixed; z-index: 20; inset: 0 0 0 auto; width: min(460px, 100%);
  padding: 75px 38px; background: #0b1019ee; backdrop-filter: blur(6px);
  transform: translateX(105%); transition: transform .45s; overflow: auto;
  border-left: 1px solid #39c6ff77;
}
.archive-drawer.open { transform: none; }
.drawer-close, #dialog-close { position: absolute; right: 16px; top: 9px; border: 0; background: none; color: var(--paper); font: 31px sans-serif; }
.archive-drawer h2 { font: 38px/1 Cinzel; margin: 12px 0 34px; }
.lore-entry { width: 100%; text-align: left; padding: 15px 0; border: 0; border-top: 1px solid #fff2; background: none; color: var(--paper); transition: .25s; }
.lore-entry:hover:not(.locked) { padding-left: 8px; }
.lore-entry small { color: var(--blue); font: 10px "DM Mono"; }
.lore-entry h3 { margin: 7px 0; font: 21px Cinzel; }
.lore-entry p { margin: 0; color: #b9ae9d; font-size: 17px; }
.locked { opacity: .35; }
dialog { width: min(520px, calc(100% - 30px)); padding: 42px; border: 1px solid var(--gold); background: #0d1119; color: var(--paper); }
dialog::backdrop { background: #000c; }
dialog h2 { font: 36px Cinzel; }
dialog p { font-size: 20px; line-height: 1.4; color: #c8bba8; }

#toast {
  position: fixed; z-index: 34; bottom: 25px; left: 50%; translate: -50% 25px; opacity: 0;
  background: #102031; border: 1px solid var(--blue); padding: 12px 16px;
  color: #d9eeff; font: 10px "DM Mono"; letter-spacing: .1em; transition: .3s;
  pointer-events: none;   /* never block the tick rail / controls beneath it */
}
#toast.visible { opacity: 1; translate: -50% 0; }

/* ---------- keyframes ---------- */
@keyframes rise-in { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes word-in { from { opacity: 0; transform: translateY(9px); filter: blur(4px); } to { opacity: 1; transform: none; filter: none; } }
@keyframes gate-breathe { 50% { box-shadow: 0 0 60px color-mix(in srgb, var(--gold) 8%, transparent), inset 0 0 60px color-mix(in srgb, var(--gold) 5%, transparent); } }
@keyframes gate-zoom { from { transform: scale(1.14); } to { transform: scale(1); } }
@keyframes enter-pulse { 50% { box-shadow: 0 0 34px color-mix(in srgb, var(--gold) 33%, transparent); border-color: var(--gold); } }
@keyframes backdrop-breathe { 50% { transform: scale(1.24); } }
@keyframes flash { 20% { opacity: 1; } 100% { opacity: 0; } }
@keyframes sweep { from { opacity: 1; transform: translateX(-100%); } to { opacity: 1; transform: translateX(100%); } }
@keyframes shake {
  10% { transform: translate(-5px, 2px); } 30% { transform: translate(4px, -3px); }
  50% { transform: translate(-3px, 1px); } 70% { transform: translate(2px, -1px); } 90% { transform: translate(-1px, 0); }
}
@keyframes ping { 50% { scale: 1.2; opacity: .5; } }
@keyframes burst { to { scale: 3.4; opacity: 0; } }
@keyframes finale-in { from { opacity: 0; } }

/* ---------- chapter navigation ---------- */
/* Entrance gate: a slim list of chapters under ENTER, newest first. */
.gate-chapters { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 9px; margin: 20px auto 0; width: min(1180px, calc(100vw - 64px)); animation: rise-in 1.4s 1.7s ease both; }
.chapter-card { position: relative; isolation: isolate; overflow: hidden; min-height: 104px; padding: 0; text-align: left; cursor: pointer; color: var(--paper); border: 1px solid #e9dfcb35; background: #080b0e; transition: transform .28s ease, border-color .28s, box-shadow .28s; }
.chapter-card-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .62; transition: transform .55s ease, opacity .3s; }
.chapter-card-shade { position: absolute; inset: 0; background: linear-gradient(110deg, #04070bea 5%, #07101a9c 58%, #04070b40); }
.chapter-card-copy { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: flex-start; justify-content: end; min-height: 104px; padding: 11px; gap: 3px; }
.chapter-card:hover { transform: translateY(-4px); border-color: var(--gold); box-shadow: 0 14px 38px #000a, 0 0 22px color-mix(in srgb, var(--gold) 25%, transparent); }
.chapter-card:hover .chapter-card-art { transform: scale(1.07); opacity: .8; }
.chapter-card.is-active { border-color: var(--gold); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 40%, transparent); }
.gate-chapters .chapter-card.is-latest { grid-column: auto; min-height: 104px; border-color: #7fd6ff88; }
.gate-chapters .chapter-card.is-latest .chapter-card-copy { min-height: 104px; }
.chapter-menu-list .chapter-card.is-latest { grid-column: span 2; min-height: 188px; }
.chapter-menu-list .chapter-card.is-latest .chapter-card-copy { min-height: 188px; }
.chapter-card-meta { color: #c9d8df; font: 9px "DM Mono"; letter-spacing: .16em; margin-top: 5px; }

/* In-reader chapter menu overlay. */
.chapter-menu { position: fixed; inset: 0; z-index: 33; display: grid; place-items: center; background: #04060ae0; backdrop-filter: blur(6px); animation: finale-in .5s ease both; }
.chapter-menu[hidden] { display: none; }
.chapter-menu-panel { position: relative; width: min(920px, calc(100% - 40px)); max-height: 82vh; overflow: auto; padding: 46px 40px; background: #0b1019; border: 1px solid var(--gold-soft); }
.chapter-menu-panel h2 { font: 34px/1 Cinzel; margin: 10px 0 26px; }
.chapter-menu-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.chapter-menu-list .chapter-card { min-height: 188px; }
.chapter-menu-list .chapter-card-copy { min-height: 188px; }

.chapter-item-no { color: var(--gold); font: 10px "DM Mono"; letter-spacing: .18em; }
.chapter-item-title { font: 18px Cinzel; }
.chapter-latest { margin-left: 9px; color: var(--blue); font: italic 10px "DM Mono"; letter-spacing: .16em; }
.chapter-draft { margin-left: 9px; padding: 1px 6px; border: 1px solid var(--ember);
  color: var(--ember); font: 10px "DM Mono"; letter-spacing: .16em; font-style: normal; }

/* ---------- responsive ---------- */
/* iPad-portrait and below: the top hud holds five items — drop the chapter
   label and keyboard hints before anything wraps or overlaps. */
@media (max-width: 900px) {
  .top-hud > p, .controls p { display: none; }
  .hud-actions { gap: 6px; }
  .hud-actions button { padding: 9px 9px; }
}
@media (max-width: 1100px) and (min-width: 561px) {
  .gate-chapters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .chapter-card, .gate-chapters .chapter-card.is-latest { min-height: 82px; }
  .chapter-card-copy, .gate-chapters .chapter-card.is-latest .chapter-card-copy { min-height: 82px; padding: 9px; }
  .chapter-item-title { font-size: 14px; }
  .chapter-card-meta { display: none; }
}
@media (max-width: 560px) {
  .gate-chapters, .chapter-menu-list { grid-template-columns: 1fr; }
  .chapter-card.is-latest { grid-column: auto; }
  .chapter-menu-panel { width: calc(100% - 24px); padding: 40px 18px; }
}

@media (max-width: 700px) {
  .player { grid-template-rows: 60px minmax(0, 1fr) auto; }
  .bottom-hud { min-height: 150px; }
  .top-hud { padding: 0 14px; }
  .top-hud > p, .controls p { display: none; }
  .comic-stage { padding: 10px 30px 20px; }
  .edge { width: 36px; height: 80px; }
  .bottom-hud { padding: 15px; align-items: start; flex-direction: column; gap: 10px; }
  .controls { align-items: start; }
  .scene-copy { max-width: 100%; }
  .stage-meta { top: 5px; left: 10px; }
  .archive-drawer { padding: 65px 25px; }
  .ticks { gap: 4px; }
  .tick { width: 8px; }
  .dialogue-line { max-width: 72%; }
  .dialogue-line.slot-left { left: 8px; }
  .dialogue-line.slot-right { right: 8px; }
  .dialogue-line span { font-size: 13px; }
  .dialogue-line.is-display span { font-size: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *:before, *:after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .scene-copy h1 span, .gate-quote span, .finale h2 span { opacity: 1; }
}

/* ---------- iOS full-bleed safe areas (viewport-fit=cover) ----------
   Keep the HUD, page rail and edge arrows clear of the notch / Dynamic
   Island / home indicator on iPhone and iPad in every orientation. */
.hud {
  padding-left: max(clamp(16px, 3vw, 44px), env(safe-area-inset-left));
  padding-right: max(clamp(16px, 3vw, 44px), env(safe-area-inset-right));
}
.top-hud { padding-top: max(0px, env(safe-area-inset-top)); }
/* Keep the footer's own breathing room; the tick rail below carries the inset. */
.bottom-hud { padding-bottom: max(14px, env(safe-area-inset-bottom)); }
.ticks { padding-bottom: max(9px, calc(env(safe-area-inset-bottom) + 5px)); }
.edge-back { left: env(safe-area-inset-left); }
.edge-next { right: env(safe-area-inset-right); }

/* ---------- cinematic panel settle ----------
   Each art beat eases in with a slow Ken-Burns-style scale so the reader
   breathes on the big iPad screen instead of a flat cross-fade. Honours
   reduced-motion via the rule above (transition-duration collapses). */
.pane { transition: opacity .5s ease, transform 1.1s ease; transform: scale(1.04); transform-origin: 50% 47%; }
.pane.is-visible { transform: scale(1); }
.pane.slow-fade { transition-duration: 1.5s, 1.9s; }

/* ---------- archive reader refinement ---------- */
.skip-link {
  position: fixed; z-index: 100; left: 16px; top: 12px; translate: 0 -160%;
  padding: 10px 14px; color: var(--ink); background: var(--gold);
  font: 600 11px "DM Mono"; letter-spacing: .12em; text-decoration: none;
}
.skip-link:focus { translate: 0; }

.reading-progress {
  position: fixed; z-index: 24; inset: 0 0 auto; height: 2px;
  background: #ffffff0a; pointer-events: none;
}
.reading-progress i {
  display: block; width: 0; height: 100%;
  background: linear-gradient(90deg, #8a6331, var(--gold), var(--blue));
  box-shadow: 0 0 16px color-mix(in srgb, var(--gold) 53%, transparent);
  transition: width .55s cubic-bezier(.22, .72, .22, 1);
}

.top-hud {
  background: linear-gradient(180deg, #05080ff2 0%, #05080fc7 72%, #05080f00);
  backdrop-filter: blur(14px) saturate(1.15);
}
.bottom-hud {
  background: linear-gradient(0deg, #05080ff5 0%, #05080fda 72%, #05080f00);
  backdrop-filter: blur(14px) saturate(1.15);
}
.chapter-context { min-width: 0; text-align: center; }
.chapter-context p { margin: 0; }
.chapter-context small {
  display: block; max-width: 38vw; margin-top: 4px; overflow: hidden;
  color: #8d98a3; font: 8px "DM Mono"; letter-spacing: .15em;
  text-overflow: ellipsis; text-transform: uppercase; white-space: nowrap;
}
.hud-actions button,
.controls button {
  border: 1px solid #ffffff17; border-radius: 2px;
  background: #0a1019a8;
}
.hud-actions button:hover,
.hud-actions button[aria-expanded="true"],
.controls button:hover {
  border-color: color-mix(in srgb, var(--gold) 47%, transparent); background: #15130fdb;
}
button:focus-visible, .tick:focus-visible, .hotspot:focus-visible {
  outline: 2px solid var(--blue); outline-offset: 3px;
}

.comic-stage { padding-block: 24px; }
.panel-frame {
  border-color: color-mix(in srgb, var(--gold) 32%, transparent);
  box-shadow: 0 32px 100px #000d, 0 0 0 1px #ffffff0b, 0 0 80px #12344b24;
}
.panel-frame:before { inset: -7px; border-color: color-mix(in srgb, var(--gold) 15%, transparent); }
.panel-frame:after {
  content: ""; position: absolute; z-index: 3; inset: 0; pointer-events: none;
  border: 1px solid #ffffff0a;
  background: radial-gradient(ellipse at center, transparent 52%, #02040973 100%);
}
.stage-meta {
  padding: 6px 9px; border: 1px solid #ffffff12; background: #05080db8;
  backdrop-filter: blur(8px);
}
.scene-copy { padding-left: 2px; }
/* Prose captions (chapters that ship a narration[] stream) can run several
   hundred characters. Cap the block and let it scroll rather than let the footer
   grow without bound and eat the artwork; the full text is also in the SCRIPT
   drawer and is spoken in full by the narration. */
.scene-copy h1 {
  max-width: 72ch; color: #f2eadb; text-wrap: balance;
  max-height: min(21vh, 8.4em); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--gold-soft) transparent;
}
.next-up {
  display: block; max-width: 260px; overflow: hidden; color: #7f8d98 !important;
  font-size: 8px !important; letter-spacing: .16em !important;
  text-overflow: ellipsis; white-space: nowrap;
}

.transcript-drawer {
  position: fixed; z-index: 32; inset: 0 0 0 auto; width: min(520px, 92vw);
  padding: 72px 42px 40px; overflow: hidden;
  color: var(--paper); background: #080c13f5; border-left: 1px solid color-mix(in srgb, var(--gold) 33%, transparent);
  box-shadow: -30px 0 90px #000c; backdrop-filter: blur(18px);
  transform: translateX(105%); transition: transform .42s cubic-bezier(.22, .72, .22, 1);
}
.transcript-drawer.open { transform: none; }
.transcript-drawer .drawer-close { position: absolute; right: 20px; top: 15px; }
.transcript-drawer h2 { margin: 10px 0 24px; font: 600 30px/1.1 Cinzel, serif; }
.transcript-scroll {
  height: calc(100vh - 150px); padding-right: 12px; overflow: auto;
  scrollbar-color: color-mix(in srgb, var(--gold) 33%, transparent) transparent;
}
.transcript-line { padding: 13px 0 15px; border-top: 1px solid #ffffff10; }
.transcript-line small,
.transcript-system > small {
  color: var(--gold); font: 9px "DM Mono"; letter-spacing: .17em;
}
.transcript-line.is-spoken small { color: var(--blue); }
.transcript-line p {
  margin: 7px 0 0; color: #dcd5c8; font: 17px/1.52 "IM Fell English", serif;
}
.transcript-line.is-spoken p { color: #f4ecda; font-size: 18px; }
.transcript-system {
  margin-top: 18px; padding: 16px; border: 1px solid #39c6ff2c; background: #06121a;
}
.transcript-system p {
  margin: 10px 0 0; color: #a8dbf5; font: 11px/1.55 "DM Mono"; letter-spacing: .06em;
}

@media (max-width: 1120px) {
  #fullscreen-toggle { display: none; }
  .chapter-context small { display: none; }
}
@media (max-width: 900px) {
  .chapter-context { display: none; }
  #autoplay-toggle { display: none; }
}
@media (max-width: 700px) {
  .reading-progress { top: env(safe-area-inset-top); }
  .top-hud { gap: 8px; }
  .hud-actions { overflow-x: auto; scrollbar-width: none; }
  .hud-actions button { flex: 0 0 auto; }
  .comic-stage { padding-block: 14px 24px; }
  .bottom-hud { min-height: 142px; }
  .next-up { display: block !important; }
  .transcript-drawer { width: 100%; padding: 68px 24px 32px; }
  .transcript-scroll { height: calc(100vh - 132px); }
}
