/* ChronoShell stage layer — see CLAUDE_CODE_BRIEF.md
   Loaded AFTER style.css. Everything here is scoped to .hero-stage so the
   modern era face and the rest of the site are untouched. */

/* ---------- §2 shared 16:9 stage ---------- */
.hero-stage {
  position: relative;
  /* Smallest 16:9 box that COVERS the viewport — no bars on normal screens.
     Scaling the whole stage (not just the video) is what keeps every overlay
     locked to the frame: hotspots are % of this box, so they crop with it and
     the terminal stays welded to the glass panel at any size.

     On a 16:9 screen all three terms are equal, so it lands on an exact fit and
     nothing is cropped — identical to before.

     Capped at 1.22x: at most 22% cropped, split evenly 11% top / 11% bottom.
     That budget is set by the two things that must survive — the glass panel
     starts at 24.54% (so 11% off the top is safe) and the journal ends at
     98.15% (so 11% off the bottom clips only the front desk edge). Covers every
     ratio up to ~2.17:1; past that thin bars return rather than eat the frame.

     Nested min/max, NOT clamp(): in clamp() the lower bound wins outright when
     it exceeds the upper, so `clamp(100vw, …, cap)` would blow straight past
     the cap on an ultrawide screen. */
  width: min(max(100vw, calc(100vh * 16 / 9)), calc(100vh * 16 / 9 * 1.22));
  aspect-ratio: 16 / 9;
  flex: 0 0 auto;
  overflow: hidden;
  background: #0a0806;
}

/* §2 letterbox against a dark background on non-16:9 screens — never crop */
body:has(.hero-stage) {
  margin: 0;
  /* height, NOT min-height: the body must stay exactly one viewport tall or it
     grows to fit an over-tall stage, and the whole overflow ends up at the
     bottom instead of splitting evenly. */
  height: 100vh;
  display: flex;
  /* Symmetric: overflow splits evenly, a little off the ceiling machines and a
     little off the front desk edge. The 1.18 cap above is what keeps both the
     glass panel and the journal inside the visible band. */
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #0a0806;
}

.hero-stage { z-index: 1; }

/* override style.css's fixed/viewport video — stage is already 16:9 */
.hero-stage .banner-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}

.hero-stage .bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  z-index: 1;
  transition: filter .45s ease;
}

.hero-stage .bg-video.dimmed {
  filter: brightness(.4) saturate(.9);
}

/* Brief §2 puts this at z-index 3, under the DOM overlays. In practice a bubble
   that renders under the terminal is a bubble nobody sees, and bubbles are
   transient, so this layer sits on top. Children re-enable pointer events. */
#label-layer {
  position: absolute;
  inset: 0;
  z-index: 9;
  pointer-events: none;
}

/* Stacking order inside the stage:
   1 video · 4 hotspots · 7 parchment panels · 8 terminal · 9 bubbles
   The terminal must outrank the hotspots — when expanded it covers 88% of the
   stage, and anything above it swallows every click meant for the shell. */
.hero-stage .container { z-index: 8; }
.hero-stage .steampunk-floating-panels { z-index: 7; }

/* belt and braces: no hotspot is live while the shell has the stage */
.hero-stage.cs-expanded .hotspot {
  pointer-events: none;
  opacity: 0;
}

/* ---------- §3 terminal in the measured glass rect ---------- */
/* glass panel: x 567→1331, y 265→688 of 1920×1080 */
.hero-stage .container {
  position: absolute;
  inset: 0;
  display: block;
  height: auto;
  z-index: 4;
  pointer-events: none;
}

/* `.hero-stage .container #terminal` on purpose, not `.hero-stage #terminal`.
   main.js is obfuscated and adds its own state classes (fullscreen, flip, AI
   mode); a rule like `#terminal.whatever` (1,1,0) would outrank the shorter
   form and drag the panel off the glass. This is (1,3,0) and cannot lose. */
.hero-stage .container #terminal {
  position: absolute;
  left: 29.53%;
  top: 24.54%;
  width: 39.79%;
  height: 39.17%;
  aspect-ratio: auto;
  margin: 0;
  padding: 0;
  /* style.css animates #terminal with a transform; it would displace the
     measured rect. Flip/minimize states re-enable it below. */
  transform: none;
  pointer-events: auto;
  border: 1px solid rgba(184, 134, 59, .35);
  /* elliptical: more curve on the left/right edges, to sit with the brass
     machine face's rounded glass rather than reading as a flat web rectangle */
  border-radius: 3.2% / 5.6%;
  overflow: auto;
  background: rgba(0, 0, 0, .55);
  box-shadow: inset 0 0 50px rgba(0, 150, 255, .18);
  font-size: clamp(6px, .55vw, 11px);
  color: #d8e6f0;
  cursor: pointer;
  transition: left .45s cubic-bezier(.4, 0, .2, 1), top .45s cubic-bezier(.4, 0, .2, 1),
    width .45s cubic-bezier(.4, 0, .2, 1), height .45s cubic-bezier(.4, 0, .2, 1),
    font-size .45s ease, background .45s ease;
}

.hero-stage .container #terminal.expanded {
  left: 6%;
  top: 6%;
  width: 88%;
  height: 88%;
  border-radius: 12px;
  background: rgba(6, 5, 4, .94);
  box-shadow: 0 0 0 3px #b8863b, 0 0 80px rgba(0, 0, 0, .8);
  font-size: clamp(11px, 1.05vw, 17px);
  cursor: auto;
}

/* two-state banner: ASCII art only fits when expanded (§3 typography) */
.hero-stage #terminal .ascii-art-container { display: none; }
.hero-stage #terminal.expanded .ascii-art-container { display: block; }
.hero-stage #terminal .ascii-art { white-space: pre; overflow-x: auto; }

.hero-stage #terminal.expanded .cs-compact-id { display: none; }

.cs-compact-id {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  color: #d8b06a;
  letter-spacing: .08em;
  line-height: 1.5;
}

.cs-compact-id .cs-tag { color: #8fd3ff; }

.cs-caret {
  display: inline-block;
  width: .55em;
  height: 1em;
  background: #64f105;
  vertical-align: text-bottom;
  animation: cs-blink 1.1s steps(1) infinite;
}

@keyframes cs-blink { 50% { opacity: 0; } }

.cs-affordance {
  position: absolute;
  right: .5em;
  bottom: .35em;
  z-index: 6;
  font: inherit;
  font-size: 1.05em;
  color: #ffc45c;
  background: rgba(0, 0, 0, .45);
  border: 1px solid rgba(255, 196, 92, .45);
  border-radius: 4px;
  padding: .1em .45em;
  cursor: pointer;
}

.cs-affordance:focus-visible { outline: 2px solid #ffc45c; }

/* ---------- §4 desk hotspots ---------- */
.hotspot {
  position: absolute;
  z-index: 4;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  border-radius: 8px;
  transition: box-shadow .3s ease, background .3s ease;
}

.hotspot:hover,
.hotspot:focus-visible {
  background: radial-gradient(ellipse at center,
      rgba(255, 196, 92, .20) 0%, rgba(255, 196, 92, 0) 70%);
  box-shadow: 0 0 28px rgba(255, 196, 92, .35);
  outline: none;
}

.hotspot:focus-visible {
  box-shadow: 0 0 0 2px #ffc45c, 0 0 28px rgba(255, 196, 92, .35);
}

/* --- the click "feel" ---
   Adventure games sell an object by answering the pointer twice: a warm lift on
   hover, and a ring that leaves the object on click. Both are cheap; without
   them an invisible rect reads as dead scenery. */
.hotspot:hover {
  background: radial-gradient(ellipse at center,
      rgba(255, 210, 130, .30) 0%, rgba(255, 196, 92, .08) 55%, rgba(255, 196, 92, 0) 72%);
  box-shadow: 0 0 34px rgba(255, 200, 110, .45);
  transform: scale(1.035);
}

.hotspot:active { transform: scale(.985); }

/* expanding ring, spawned on click */
.cs-ping {
  position: absolute;
  z-index: 8;
  border: 2px solid rgba(255, 210, 130, .85);
  border-radius: 50%;
  pointer-events: none;
  animation: cs-ping .55s cubic-bezier(.2, .7, .3, 1) forwards;
}

@keyframes cs-ping {
  from { transform: translate(-50%, -50%) scale(.25); opacity: .9; }
  to   { transform: translate(-50%, -50%) scale(1);   opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hotspot:hover, .hotspot:active { transform: none; }
  .cs-ping { display: none; }
}

/* measured — CLAUDE_CODE_BRIEF.md §4, do not recalculate */
.hotspot[data-panel="trivia"]  { left: 15.62%; top: 83.80%; width: 30.21%; height: 14.35%; }
.hotspot[data-panel="joke"]    { left: 56.77%; top: 79.63%; width: 9.64%;  height: 11.57%; }
.hotspot[data-panel="weather"] { left: 75.78%; top: 66.20%; width: 11.46%; height: 22.69%; }
.hotspot[data-cmd="help"]      { left: 48.70%; top: 85.19%; width: 7.29%;  height: 6.94%; }
.hotspot[data-cmd="sound"]     { left: 0.52%;  top: 59.72%; width: 13.54%; height: 18.98%; }

/* ---------- §5 hint bubbles ---------- */
.cs-bubble {
  position: absolute;
  z-index: 5;
  max-width: 22ch;
  padding: .7em .9em;
  transform: translate(-50%, -100%) scale(.85);
  opacity: 0;
  pointer-events: auto;
  background: #f6e6c4;
  color: #4a3418;
  border: 2px solid #8a6534;
  border-radius: 14px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .55);
  font-family: "Dancing Script", Georgia, serif;
  font-size: clamp(11px, 1.1vw, 19px);
  line-height: 1.25;
  text-align: center;
  transition: opacity .3s ease, transform .3s cubic-bezier(.34, 1.3, .64, 1);
}

.cs-bubble.show { opacity: 1; transform: translate(-50%, -100%) scale(1); }

/* tail pointing down at the object */
.cs-bubble::after,
.cs-bubble::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  border: 11px solid transparent;
  border-top-color: #8a6534;
  transform: translateX(-50%);
}

.cs-bubble::after {
  border-width: 9px;
  border-top-color: #f6e6c4;
  margin-top: -2px;
}

/* click-to-read object bubbles: same component, a touch wider for prose */
.cs-bubble--info { max-width: 30ch; }

/* flipped: object sits near the top of the frame, so the bubble hangs below it
   and the tail points up */
.cs-bubble--below { transform: translate(-50%, 0) scale(.85); }
.cs-bubble--below.show { transform: translate(-50%, 0) scale(1); }

.cs-bubble--below::before,
.cs-bubble--below::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
}

.cs-bubble--below::before { border-bottom-color: #8a6534; }

.cs-bubble--below::after {
  border-bottom-color: #f6e6c4;
  margin-top: 0;
  margin-bottom: -2px;
}

/* `hotspots` terminal command draws every box so the rects can be nudged
   against the real frame instead of guessed at */
.hero-stage.cs-calibrate .hotspot {
  outline: 1px dashed rgba(255, 196, 92, .9);
  background: rgba(255, 196, 92, .10);
}

/* calibration must win over the expanded-terminal stand-down rule, or the boxes
   vanish the moment the shell is open */
.hero-stage.cs-calibrate .hotspot {
  opacity: 1;
  pointer-events: auto;
}

.cs-calibrate-banner {
  position: fixed;
  left: 50%;
  bottom: 16px;
  z-index: 999;
  transform: translateX(-50%);
  padding: 7px 14px;
  font: 12px/1.3 ui-monospace, monospace;
  color: #ffe6b0;
  background: rgba(20, 12, 4, .92);
  border: 1px solid #b8863b;
  border-radius: 6px;
  pointer-events: none;
}

.hero-stage.cs-calibrate .hotspot::after {
  content: attr(data-label);
  position: absolute;
  left: 0;
  top: 0;
  font: 10px/1.2 ui-monospace, monospace;
  color: #ffc45c;
  background: rgba(0, 0, 0, .7);
  padding: 1px 3px;
  white-space: nowrap;
}

/* ---------- §7 portrait frame (covers the sparkle watermark) ---------- */
.portrait-frame {
  position: absolute;
  left: 86.5%;
  top: 74.5%;
  width: 9.5%;
  height: 16%;
  z-index: 4;
  border-radius: 3px;
  border: .35vw solid transparent;
  background:
    linear-gradient(#0000, #0000) padding-box,
    linear-gradient(150deg, #f0cf8a 0%, #b8863b 35%, #6b4a1c 60%, #e2bd76 100%) border-box;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .75), inset 0 0 12px rgba(0, 0, 0, .5);
  overflow: hidden;
  pointer-events: none;
}

.portrait-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(.88) sepia(.10) contrast(1.03);
}

/* until the photograph is supplied, the frame stays hidden but still masks
   the watermark with a plate that reads as a covered picture back */
.portrait-frame:not(:has(img)) {
  background:
    linear-gradient(160deg, #2a1f14, #14100b) padding-box,
    linear-gradient(150deg, #f0cf8a 0%, #b8863b 35%, #6b4a1c 60%, #e2bd76 100%) border-box;
}

/* ---------- §8 parchment panels: closed by default, beside their object ---------- */
.hero-stage .steampunk-floating-panels { z-index: 6; }

.hero-stage .steampunk-floating-panels .info-dropdown,
.hero-stage .steampunk-floating-panels #weather-popup {
  position: absolute;
  transform-origin: var(--cs-origin, 50% 100%);
  animation: cs-unfurl .35s cubic-bezier(.34, 1.3, .64, 1);
}

@keyframes cs-unfurl {
  from { transform: scale(.7); opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}

/* left/right thirds only — never over the glass rect (29.5%→69.3%) */
.hero-stage #trivia-dropdown {
  left: 2%;
  top: 30%;
  right: auto;
  bottom: auto;
  --cs-origin: 40% 100%;
  max-height: 42%;
  overflow-y: auto;
}

.hero-stage #joke-dropdown {
  left: auto;
  right: 2%;
  top: auto;
  bottom: 18%;
  --cs-origin: 20% 100%;
}

.hero-stage #weather-popup.victorian-card.poster {
  left: auto;
  right: 2%;
  top: 12%;
  bottom: auto;
  --cs-origin: 30% 100%;
}

/* brass rail scrollbar (§8) */
.hero-stage #trivia-dropdown::-webkit-scrollbar { width: 8px; }
.hero-stage #trivia-dropdown::-webkit-scrollbar-track { background: rgba(60, 42, 20, .35); border-radius: 4px; }
.hero-stage #trivia-dropdown::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #e2bd76, #8a6534);
  border-radius: 4px;
}

/* ---------- The journal, opening at centre stage ----------
   CSS 3D, not WebGL. Brief §6 reserves WebGL for the coins, where a metallic
   specular is the entire point; parchment has no specular to miss, and §7's
   reasoning applies here too — clean WebGL output sits badly against baked
   lighting and grain. This gets the same trick for no dependency and degrades
   to a plain fade when 3D transforms are unavailable. */
.cs-book-wrap {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  perspective: 1800px;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, .45) 0%, rgba(0, 0, 0, .82) 75%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s ease;
}

.cs-book-wrap.open { opacity: 1; pointer-events: auto; }

.cs-book {
  position: relative;
  width: 58%;
  height: 62%;
  transform-style: preserve-3d;
  /* flies up out of the journal on the desk, then settles facing the viewer */
  transform: translateY(26%) rotateX(52deg) scale(.42);
  transition: transform .62s cubic-bezier(.22, .9, .3, 1.05);
}

.cs-book-wrap.open .cs-book { transform: translateY(0) rotateX(8deg) scale(1); }

.cs-page {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
  /* vintage_paper.jpg is a whole decorative spread with a central ornament, so
     stretch one copy across BOTH pages — each page shows its own half and the
     ornament lands in the gutter where a real binding would put it, instead of
     being duplicated twice under the text. */
  background:
    linear-gradient(180deg, rgba(70, 48, 20, .13), rgba(70, 48, 20, .03) 22%, rgba(70, 48, 20, .16)),
    url("vintage_paper.jpg") no-repeat, #efe0bd;
  background-size: auto, 200% 100%, auto;
  box-shadow: inset 0 0 60px rgba(96, 66, 26, .45);
  backface-visibility: hidden;
}

.cs-page--l { background-position: center, left center, center; }
.cs-page--r { background-position: center, right center, center; }

/* keep the ornament from competing with the writing — the wash sits on the
   page, under the inner, so it lightens the texture without touching the text
   (the trivia HTML arrives as bare text nodes, so it cannot be raised above a
   wash placed inside the same element) */
.cs-page::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  /* papyrus wash — heavy enough that the ornament reads as watermark under the
     text rather than competing with it */
  background:
    radial-gradient(ellipse at 50% 45%, rgba(246, 233, 202, .93) 0%, rgba(243, 226, 189, .82) 55%, rgba(232, 210, 165, .62) 100%);
  pointer-events: none;
}

/* Torn outer edge, Victorian-tome style. Reuses the #v-wavy turbulence filter
   already defined in index.html — the displacement chews an irregular line out
   of a plain strip. It sits on its own layer so the text is never distorted. */
.cs-page::after {
  content: "";
  position: absolute;
  top: -2%;
  height: 104%;
  width: 5%;
  z-index: 2;
  background: #efe0bd;
  filter: url(#v-wavy);
  pointer-events: none;
}

.cs-page--l::after { left: -2.5%; }
.cs-page--r::after { right: -2.5%; }

/* the torn lip catches the candlelight along its ragged line */
.cs-page--l { box-shadow: inset 0 0 60px rgba(96, 66, 26, .45), inset 10px 0 22px -14px rgba(60, 38, 10, .8); }
.cs-page--r { box-shadow: inset 0 0 60px rgba(96, 66, 26, .45), inset -10px 0 22px -14px rgba(60, 38, 10, .8); }

.cs-page-inner { position: relative; z-index: 1; }

.cs-page--l {
  left: 0;
  transform-origin: right center;
  border-radius: 10px 3px 3px 10px;
  transform: rotateY(88deg);
  transition: transform .62s cubic-bezier(.22, .9, .3, 1.05) .12s;
}

.cs-page--r {
  right: 0;
  transform-origin: left center;
  border-radius: 3px 10px 10px 3px;
  transform: rotateY(-88deg);
  transition: transform .62s cubic-bezier(.22, .9, .3, 1.05) .12s;
}

.cs-book-wrap.open .cs-page--l { transform: rotateY(6deg); }
.cs-book-wrap.open .cs-page--r { transform: rotateY(-6deg); }

/* the gutter shadow is what sells it as one bound object rather than two cards */
.cs-book::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 7%;
  height: 100%;
  transform: translateX(-50%);
  background: linear-gradient(90deg,
      rgba(0, 0, 0, 0), rgba(60, 40, 14, .55) 45%, rgba(60, 40, 14, .55) 55%, rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* Readability beats atmosphere. The body was set in Dancing Script — a
   handwriting face — over a busy texture, which is unreadable at any size for
   prose. Script is now reserved for the title block; the entries are set in a
   plain old-style serif, larger, looser and in darker ink. */
.cs-page-inner {
  padding: 8% 9%;
  height: 100%;
  overflow-y: auto;
  color: #2b1a08;
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
  font-size: clamp(13px, 1.15vw, 19px);
  line-height: 1.72;
  letter-spacing: .005em;
}

.cs-page--l .cs-page-inner { text-align: center; }

/* the script survives here, where it is three words and decorative */
.cs-book-title,
.cs-book-sub,
.cs-book-date {
  font-family: "Dancing Script", "Tangerine", Georgia, serif;
}

.cs-book-title {
  font-size: 2.1em;
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: .35em;
  color: #4a2c0c;
}

.cs-book-sub { font-size: 1.25em; opacity: .8; }

/* --- the old-mystical dressing: gilt edges, candlelight, ink --- */
.cs-book-rule {
  width: 62%;
  height: 2px;
  margin: .9em auto;
  background: linear-gradient(90deg, transparent, #a3762f 18%, #e0bb74 50%, #a3762f 82%, transparent);
}

.cs-book-rule::after {
  content: "❦";
  display: block;
  margin-top: -.75em;
  color: #8a6534;
  font-size: 1.1em;
  background: transparent;
}

.cs-book-date {
  margin-top: 1.4em;
  font-size: 1.15em;
  color: #6b4a1c;
  letter-spacing: .04em;
}

/* gilded page edges — the thin bright line that says "this book is old and cared for" */
.cs-page--l { box-shadow: inset 0 0 60px rgba(96, 66, 26, .45), inset 6px 0 0 -3px #c9a259; }
.cs-page--r { box-shadow: inset 0 0 60px rgba(96, 66, 26, .45), inset -6px 0 0 -3px #c9a259; }

/* candle throw from the desk below, plus the burn at the page corners */
.cs-page-inner {
  background:
    radial-gradient(ellipse at 50% 120%, rgba(255, 196, 92, .28), transparent 60%),
    radial-gradient(ellipse at 15% 8%, rgba(90, 58, 20, .22), transparent 45%),
    radial-gradient(ellipse at 88% 92%, rgba(90, 58, 20, .26), transparent 50%);
}

/* entries are the thing people actually read — give them room and separation */
.cs-book-body { text-align: left; }
.cs-book-body br + br { line-height: 2.4; }

/* the book throws its own light into the room while it is open */
.cs-book {
  filter: drop-shadow(0 26px 46px rgba(0, 0, 0, .8))
          drop-shadow(0 0 60px rgba(255, 190, 100, .22));
}

/* motes drifting in the candlelight — one element, no JS */
.cs-book-wrap.open::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(2px 2px at 20% 30%, rgba(255, 226, 170, .55), transparent 60%),
    radial-gradient(2px 2px at 72% 22%, rgba(255, 226, 170, .40), transparent 60%),
    radial-gradient(1.5px 1.5px at 38% 74%, rgba(255, 226, 170, .45), transparent 60%),
    radial-gradient(1.5px 1.5px at 84% 66%, rgba(255, 226, 170, .35), transparent 60%),
    radial-gradient(2px 2px at 56% 48%, rgba(255, 226, 170, .30), transparent 60%);
  animation: cs-motes 14s ease-in-out infinite alternate;
}

@keyframes cs-motes {
  from { transform: translate3d(0, 0, 0); opacity: .5; }
  to   { transform: translate3d(-18px, -26px, 0); opacity: .95; }
}

.cs-page-inner::-webkit-scrollbar { width: 7px; }
.cs-page-inner::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #b08a4a, #6b4a1c);
  border-radius: 4px;
}

/* --- turning leaf ---
   One leaf does the whole job: it carries the current right-hand page on its
   front and the next left-hand page on its back, swings on the spine, and when
   it lands the two static pages are swapped to the new spread. A real stack of
   leaves would be prettier and about ten times the code. */
.cs-leaf {
  position: absolute;
  top: 0;
  left: 50%;
  width: 50%;
  height: 100%;
  z-index: 3;
  transform-origin: left center;
  transform-style: preserve-3d;
  transition: transform .78s cubic-bezier(.42, .02, .34, 1);
  pointer-events: none;
  display: none;
}

.cs-leaf.turning { display: block; transform: rotateY(-176deg); }
.cs-leaf.turning-back { display: block; transform: rotateY(0deg); }
.cs-leaf.at-left { transform: rotateY(-176deg); }

.cs-leaf-face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  background:
    linear-gradient(180deg, rgba(70, 48, 20, .13), rgba(70, 48, 20, .03) 22%, rgba(70, 48, 20, .16)),
    url("vintage_paper.jpg") no-repeat, #efe0bd;
  background-size: auto, 200% 100%, auto;
  box-shadow: inset 0 0 60px rgba(96, 66, 26, .45);
}

.cs-leaf-front { background-position: center, right center, center; border-radius: 3px 10px 10px 3px; }

.cs-leaf-back {
  transform: rotateY(180deg);
  background-position: center, left center, center;
  border-radius: 10px 3px 3px 10px;
}

.cs-leaf-face::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 45%, rgba(246, 233, 202, .93) 0%, rgba(243, 226, 189, .82) 55%, rgba(232, 210, 165, .62) 100%);
}

/* the shading that sells paper bending rather than a flat card rotating */
.cs-leaf-face::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(60, 40, 14, .38), rgba(60, 40, 14, 0) 32%);
  opacity: 0;
  transition: opacity .78s ease;
}

.cs-leaf.turning .cs-leaf-face::after,
.cs-leaf.turning-back .cs-leaf-face::after { opacity: 1; }

/* corner tabs — the affordance that says the pages move */
.cs-turn {
  position: absolute;
  bottom: 3%;
  z-index: 4;
  padding: .35em .8em;
  font: 1em/1 "Dancing Script", Georgia, serif;
  color: #f6e6c4;
  background: linear-gradient(180deg, rgba(107, 74, 28, .92), rgba(51, 34, 13, .92));
  border: 1px solid #c9a259;
  border-radius: 6px;
  cursor: pointer;
  opacity: .85;
}

.cs-turn:hover { opacity: 1; }
.cs-turn[disabled] { opacity: .25; cursor: default; }
.cs-turn--prev { left: 4%; }
.cs-turn--next { right: 4%; }

.cs-book-folio {
  position: absolute;
  bottom: 3.2%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  font: .8em/1 Georgia, serif;
  color: #6b4a1c;
  opacity: .8;
}

@media (prefers-reduced-motion: reduce) {
  .cs-leaf { transition: none; }
}

.cs-book-close {
  position: absolute;
  top: -4%;
  right: -3%;
  z-index: 2;
  width: 2.2em;
  height: 2.2em;
  font: 700 1em/1 ui-monospace, monospace;
  color: #f6e6c4;
  background: linear-gradient(180deg, #6b4a1c, #33220d);
  border: 1px solid #e2bd76;
  border-radius: 50%;
  cursor: pointer;
}

@media (max-width: 768px) {
  .cs-book { width: 92%; height: 70%; }
  .cs-page-inner { font-size: 15px; padding: 10% 8%; }
}

@media (prefers-reduced-motion: reduce) {
  /* The opening is the point of the book, so it survives here — same call as
     the background video. Only the ambient drift is dropped. */
  .cs-book-wrap.open::after { animation: none; }
}

/* ---------- §9 mobile ---------- */
.cs-dock {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 6;
  gap: 10px;
  justify-content: center;
  padding: 8px;
  background: linear-gradient(0deg, rgba(10, 8, 6, .92), rgba(10, 8, 6, 0));
}

.cs-dock button {
  font-size: 20px;
  line-height: 1;
  padding: 10px 14px;
  color: #f6e6c4;
  background: linear-gradient(180deg, #6b4a1c, #3a2711);
  border: 1px solid #e2bd76;
  border-radius: 10px;
  cursor: pointer;
}

@media (max-width: 768px) {
  .hero-stage { aspect-ratio: auto; height: 100vh; height: 100dvh; }
  .hero-stage .bg-video { object-fit: cover; }
  /* must match the desktop rule's specificity or it will never apply */
  .hero-stage .container #terminal,
  .hero-stage .container #terminal.expanded {
    left: 4%;
    top: 4%;
    width: 92%;
    height: 74%;
    font-size: clamp(11px, 3.2vw, 15px);
  }
  .hero-stage #terminal .ascii-art-container { display: block; }
  .hero-stage .cs-compact-id,
  .cs-affordance,
  .hotspot,
  .portrait-frame { display: none; }
  .cs-dock { display: flex; }
  .hero-stage .steampunk-floating-panels .info-dropdown,
  .hero-stage .steampunk-floating-panels #weather-popup {
    left: 4%;
    right: 4%;
    top: 10%;
    bottom: auto;
    max-height: 60%;
    overflow-y: auto;
  }
}

/* ---------- §10 reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  /* DELIBERATE DEVIATION from brief §10: the brief says swap the video for the
     poster here. Rishab wants the background video playing regardless — it is a
     slow, deliberate loop (§11), not a motion hazard. The poster still covers
     first paint, and every *interface* animation below is still disabled. */
  /* Only the UNPROMPTED hint bubbles are suppressed (brief §5). A bubble the
     visitor opened by clicking an object is a response, not motion — killing
     those made every object look broken. */
  .cs-bubble:not(.cs-bubble--info) { display: none !important; }
  .cs-bubble--info { transition: none; }
  .hero-stage #terminal,
  .hero-stage .steampunk-floating-panels .info-dropdown,
  .hero-stage .steampunk-floating-panels #weather-popup {
    transition: none;
    animation: none;
  }
}
