
:root {
  /* Body + UI: chains fall through CJK fallbacks per glyph so the same
     stylesheet works for Latin, Korean, Japanese, and Simplified Chinese. */
  --serif: 'Lora', 'Noto Serif KR', 'Noto Serif JP', 'Noto Serif SC', Georgia, 'Times New Roman', serif;
  --sans:  'Inter', 'Noto Sans KR', 'Noto Sans JP', 'Noto Sans SC', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --text-primary: #1a1a1a;
  --text-secondary: #6b6b6b;
  --text-tertiary: #9a9a9a;
  --bg: #fafaf7;
  --border: #ececec;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }


.book {
  max-width: 580px;
  margin: 0 auto;
  padding: 24px 16px 64px;
}

.book-header {
  text-align: center;
  padding: 18px 0 32px;
  margin-bottom: 36px;
  border-bottom: 1px solid var(--border);
  position: relative;
}
.book-header::before {
  /* small decorative sparkle above the title */
  content: '✦';
  display: block;
  font-size: 22px;
  color: rgba(140,100,40,0.42);
  margin-bottom: 6px;
}
.book-header .question {
  font-family: 'Caveat', 'Nanum Pen Script', 'Yomogi', 'Liu Jian Mao Cao', cursive;
  font-size: 38px;
  font-weight: 700;
  line-height: 1.18;
  margin: 0;
  letter-spacing: 0;
  color: #3d2918;
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.book-header .subtitle {
  margin-top: 8px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
  word-break: break-word;
}
.book-header .question-full {
  font-size: 17px;
  line-height: 1.35;
  max-width: 38ch;
  margin: 10px auto 0;
  color: #5a4231;
  font-style: italic;
  overflow-wrap: anywhere;
  word-break: break-word;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
@media (max-width: 480px) {
  .book-header .question { font-size: 32px; }
}

.scene { margin-bottom: 44px; }






/* === BOOK MODE — two-page spread flipbook (Storybook.html design) ======== */
/* Decorative palette for the printed-book aesthetic. Per-scene themes
   (.bm-image-page[data-theme=...] / .bm-prose-page[data-theme=...]) override
   --accent / --theme-bg / --bubble-bg via _theme_css() below. */
.flipbook {
  --paper:        #f5ecd1;
  --paper-warm:   #f0e3b9;
  --paper-edge:   #d9c891;
  --ink:          #281c10;
  --ink-soft:     #5a4327;
  --ink-faint:    #927853;
  --gold:         #b78a3a;
  --gold-2:       #e3c178;
  --cloth:        #1c2540;
  --cloth-2:      #283557;
  --cloth-3:      #14192d;
}

/* Top-left corner of the iframe. The parent chat shell pins its
   .book-toolbar (share / fav / delete / close) to TOP-right and its
   help button to BOTTOM-right — both were obscuring this toggle in
   earlier positions. Top-left is empty in the chat shell. */
.view-toggle {
  position: fixed;
  top: 14px;
  left: 14px;
  z-index: 100;
  background: rgba(255,255,255,0.92);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.06);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  user-select: none;
}
.view-toggle:hover { color: var(--text-primary); }
/* When in book mode, give the toggle a parchment-warm look so it doesn't
   clash with the dark tabletop backdrop. */
body.mode-book .view-toggle {
  background: rgba(245, 236, 209, 0.92);
  color: #3b2c14;
  border-color: rgba(80,55,20,0.25);
}

/* Cover-jump button — sits next to the view-toggle in the top-left of
   the iframe. Click jumps straight back to the cover (state 0) without
   manually un-flipping every spread. */
.cover-jump {
  position: fixed;
  top: 14px;
  left: 134px;     /* directly right of .view-toggle (which is ~110px wide + 10px gap) */
  z-index: 100;
  background: rgba(255,255,255,0.92);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.06);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  user-select: none;
  display: none;        /* only show in book mode (book-open or book-end) */
}
.cover-jump:hover { color: var(--text-primary); }
body.mode-book.book-open .cover-jump,
body.mode-book.book-end .cover-jump {
  display: block;
  background: rgba(245, 236, 209, 0.92);
  color: #3b2c14;
  border-color: rgba(80,55,20,0.25);
}
@media (max-width: 760px) {
  .cover-jump { display: none !important; }
}
body[data-preview="1"] .cover-jump { display: none !important; }

.scroll-top {
  position: fixed;
  right: calc(14px + env(safe-area-inset-right,0px));
  bottom: calc(16px + env(safe-area-inset-bottom,0px));
  z-index: 90;
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(255,255,255,0.92);
  color: var(--text-secondary);
  font-family: var(--sans);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.06);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity 0.18s, transform 0.18s;
  user-select: none;
}
.scroll-top:hover { color: var(--text-primary); }
.scroll-top.visible {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
@media (max-width: 1000px) {
  /* #199: invited-tester / random-anon claim panels are parent-shell overlays
     pinned to the bottom edge; lift the iframe-owned back-to-top button above
     them so it isn't occluded.
     #235: scope the lift to when that panel is actually present — the parent
     passes ?claim=1 (read by reader.js -> body.has-tester-claim). Normal readers
     keep the bottom-right corner from the base rule above. */
  body.has-tester-claim .scroll-top { bottom: calc(152px + env(safe-area-inset-bottom,0px)); }
}

/* #92 read-aloud control. INJECTED by reader.js (not baked into renderer.py) so
   every already-published book gets it without a re-render. Bottom-left, mirrors
   the .view-toggle look and the .scroll-top safe-area treatment. A single stable
   button node (data-state drives the icon/spinner); progress + upsell are siblings.
   Read-aloud is a Plus/Family feature: free/anon see the pill as an UPGRADE
   affordance (PLUS badge + upsell), never a dead button. */
.wl-ra {
  position: fixed;
  left: calc(14px + env(safe-area-inset-left,0px));
  bottom: calc(16px + env(safe-area-inset-bottom,0px));
  z-index: 100;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: rgba(255,255,255,0.92);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  min-height: 29px;
  box-sizing: border-box;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.06);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  user-select: none;
}
.wl-ra:hover { color: var(--text-primary); }
.wl-ra[hidden] { display: none !important; }
body.mode-book .wl-ra {
  background: rgba(245,236,209,0.92);
  color: #3b2c14;
  border-color: rgba(80,55,20,0.25);
}
.wl-ra .wl-ra-ico { font-size: 13px; line-height: 1; display: inline-flex; }
.wl-ra .wl-ra-spin {
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid rgba(120,90,40,0.25);
  border-top-color: rgba(120,90,40,0.85);
  animation: wlRaSpin 0.8s linear infinite;
  display: none;
}
.wl-ra[data-state="loading"] .wl-ra-spin { display: inline-block; }
.wl-ra[data-state="loading"] .wl-ra-ico { display: none; }
@keyframes wlRaSpin { to { transform: rotate(360deg); } }
.wl-ra-plus {
  font-size: 10px; font-weight: 600; color: #78561c;
  background: rgba(120,86,28,0.14);
  border-radius: 999px; padding: 1px 6px; margin-left: 1px;
}
.wl-ra-prog {
  position: fixed;
  left: calc(14px + env(safe-area-inset-left,0px));
  bottom: calc(51px + env(safe-area-inset-bottom,0px));
  z-index: 100;
  width: 160px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-soft);
  pointer-events: none;
}
body.mode-book .wl-ra-prog { color: rgba(245,236,209,0.92); }
.wl-ra-prog[hidden] { display: none !important; }
.wl-ra-prog .bar {
  height: 3px; border-radius: 999px;
  background: rgba(150,130,90,0.28); margin-top: 4px; overflow: hidden;
}
body.mode-book .wl-ra-prog .bar { background: rgba(245,236,209,0.22); }
.wl-ra-prog .bar i {
  display: block; height: 100%; border-radius: 999px;
  background: var(--gold); transition: width 0.3s ease;
}
body.mode-book .wl-ra-prog .bar i { background: rgba(245,236,209,0.85); }
.wl-ra-upsell {
  position: fixed;
  left: calc(14px + env(safe-area-inset-left,0px));
  bottom: calc(53px + env(safe-area-inset-bottom,0px));
  z-index: 102;
  width: 236px;
  background: #fffdf6;
  border: 1px solid rgba(80,55,20,0.18);
  border-radius: 14px;
  box-shadow: 0 12px 34px rgba(0,0,0,0.28);
  padding: 14px;
  font-family: var(--sans);
  font-size: 12.5px;
  line-height: 1.45;
  color: #3b2c14;
}
.wl-ra-upsell[hidden] { display: none !important; }
.wl-ra-upsell b { font-weight: 650; font-size: 13px; display: block; margin-bottom: 3px; }
.wl-ra-upsell .wl-ra-sub { color: #6b5836; margin-bottom: 11px; }
.wl-ra-upsell .wl-ra-go {
  display: block; text-align: center;
  background: #2f6b4f; color: #fff; font-weight: 600;
  border: none; width: 100%;
  border-radius: 999px; padding: 9px 0; font-size: 12.5px;
  text-decoration: none; cursor: pointer;
}
.wl-ra-upsell .wl-ra-go:hover { background: #285c43; }
.wl-ra-sr {
  position: absolute !important; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (min-width: 761px) {
  body.mode-book .scroll-top { display: none !important; }
}

.page-indicator {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  background: rgba(245, 236, 209, 0.85);
  border-radius: 999px;
  padding: 5px 14px;
  font-family: 'Caveat', cursive;
  font-size: 17px;
  font-weight: 600;
  color: #5d4022;
  letter-spacing: 0.02em;
  box-shadow: 0 2px 6px rgba(0,0,0,0.18);
  display: none;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
body.mode-book .page-indicator { display: block; }

.nav-zone {
  position: fixed;
  top: 0; bottom: 0;
  width: 22%;
  z-index: 50;
  cursor: pointer;
  display: none;
  align-items: center;
  background: transparent;
  user-select: none;
}
body.mode-book .nav-zone { display: flex; }
/* Push the arrow toward the OUTER edge of the viewport so it doesn't
   overlap the book page content. The click zone is still the full 22%
   so users can click anywhere on the left/right edge of the screen,
   but the visible button sits clear of the spread. */
.nav-zone.prev { left: 0; justify-content: flex-start; padding-left: 14px; }
.nav-zone.next { right: 0; justify-content: flex-end;   padding-right: 14px; }
.nav-zone .arrow {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: rgba(245, 236, 209, 0.85);
  color: #5d4022;
  font-family: var(--sans);
  font-size: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0,0,0,0.32);
  opacity: 0;
  transition: opacity 0.2s;
}
.nav-zone:hover .arrow { opacity: 1; }


body.mode-book::before {
  content: '';
  position: fixed;
  left: 50%; bottom: -10vh;
  width: 90vw; height: 60vh;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(255,210,150,0.06) 0%, transparent 70%);
  pointer-events: none;
  z-index: 0;
}
body.mode-book .book.scroll-layout { display: none !important; }
body:not(.mode-book) .flipbook { display: none !important; }
/* Preview-mode (anon truncated payload) pins to scroll layout so the
   signup paywall stays visible; toggle hidden since there's no book
   payload to switch into. */
body[data-preview="1"] .view-toggle { display: none !important; }
body[data-preview="1"] .flipbook,
body[data-preview="1"] .nav-zone,
body[data-preview="1"] .page-indicator { display: none !important; }

@media (max-width: 760px), (max-height: 500px) {
  /* Two-page spread doesn't fit phone-width viewports; founder choice:
     mobile users see the scroll layout while web is paginated. The
     `max-height: 500px` arm also catches LANDSCAPE phones (wide enough to
     dodge max-width:760px but too SHORT — ~390px — to fit the 459px book,
     which clipped top+bottom). Desktops are tall (>=600px) so never match. */
  body.mode-book .flipbook { display: none !important; }
  body.mode-book .book.scroll-layout { display: block !important; }
  body.mode-book .nav-zone, body.mode-book .page-indicator { display: none !important; }
  body.mode-book {
    background: var(--bg);
    display: block;
    height: auto;
    overflow: auto;
  }
  body.mode-book::before { display: none; }
  body.mode-book .view-toggle { display: none !important; }
}

/* When the rendered HTML is printed or exported to PDF, force scroll
   layout (founder locked: 'PDF export should ONLY do scroll view'). The
   PDF export route additionally appends ?view=scroll to be belt-and-
   braces; this rule covers print stylesheets and user File→Print. */
@media print {
  body.mode-book { background: var(--bg); height: auto; overflow: visible; display: block; }
  body.mode-book::before { display: none; }
  body.mode-book .flipbook,
  body.mode-book .view-toggle,
  body.mode-book .nav-zone,
  body.mode-book .page-indicator { display: none !important; }
  body.mode-book .book.scroll-layout { display: block !important; }
}
@media print {
  /* All fixed reader chrome must never print, in EITHER view mode — PDFs render
     in scroll mode where the mode-book rule above doesn't apply (#97 follow-up:
     the Book/Scroll-view toggle was leaking into exported PDFs). */
  .view-toggle, .cover-jump, .scroll-top, .nav-zone, .page-indicator,
  .shared-home-bar, .wl-ra, .wl-ra-prog, .wl-ra-upsell { display: none !important; }
}

/* The flipbook container — two-page-spread book on a table. Closed state
   (book-closed) shifts it left so only the right-half (cover) sits
   centered, and hides the binding + paper-stack edges. Book-end is the
   mirror state for the back cover after the final flip. */
.flipbook {
  position: relative;
  width: min(96vw, 1100px);
  height: var(--book-height, min(72vh, 740px));
  margin: 0 auto;
  perspective: 2800px;
  filter: drop-shadow(0 30px 50px rgba(0,0,0,0.55));
  transform: rotateX(3deg) translateX(0);
  transform-style: preserve-3d;
  transition: transform 0.95s cubic-bezier(0.42, 0.04, 0.58, 0.96),
              opacity 0.4s ease;
  z-index: 1;
}
body.mode-book.book-closed .flipbook {
  transform: rotateX(3deg) translateX(-25%);
}
body.mode-book.book-end .flipbook {
  transform: rotateX(3deg) translateX(25%);
}

/* Paper-stack edges — the thin striped band peeking out behind the
   open pages, suggesting many pages stacked under the visible spread. */
.page-stack {
  position: absolute;
  top: 5px; bottom: -3px;
  width: 6px;
  z-index: -1;
  background:
    repeating-linear-gradient(to bottom,
      var(--paper) 0 1px,
      var(--paper-edge) 1px 2px,
      var(--paper-warm) 2px 3px,
      #c8b272 3px 4px);
  box-shadow:
    0 4px 8px rgba(0,0,0,0.3),
    inset 0 0 0 1px rgba(120,90,40,0.25);
  transition: opacity 0.4s ease 0.2s;
}
.page-stack.right { left: calc(50% - 3px); border-radius: 0 2px 2px 0; }
.page-stack.left  { right: calc(50% - 3px); border-radius: 2px 0 0 2px; opacity: 0; }
body.mode-book.book-closed .page-stack { opacity: 0; transition-delay: 0s; }
body.mode-book.book-end .page-stack.right { opacity: 0; }
body.mode-book.book-end .page-stack.left  { opacity: 1; }

/* Sheets — stacked on the RIGHT half of the flipbook. Each sheet flips
   around its LEFT edge (the binding). After flipping, the sheet visually
   occupies the LEFT half (showing its back face). */
.bm-sheets {
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 50%;
  transform-style: preserve-3d;
}
.bm-sheet {
  position: absolute;
  inset: 0;
  transform-origin: 0 50%;
  transform-style: preserve-3d;
  transform: rotateY(0);
  transition: transform 1.1s cubic-bezier(0.34, 0.05, 0.55, 0.99);
  cursor: pointer;
}
.bm-sheet.flipped {
  transform: rotateY(-180deg);
  /* Flipped sheets are still clickable: clicking the most-recently-flipped
     sheet's back face calls flip(-1), going back one spread. Keep
     cursor: pointer so the affordance is honest. */
}
/* Used by jumpToCover() and restoreSpread() to snap state changes
   without animating — also kills the visibility transition so the back
   face doesn't briefly show during a restore. */
.flipbook.no-animate .bm-sheet,
.flipbook.no-animate .bm-face,
.flipbook.no-animate.flipbook,
.flipbook.no-animate {
  transition: none !important;
}

/* Each face is split into outer (transform + backface-visibility, NO border-
   radius / overflow) and inner (border-radius + overflow:hidden + content).
   Putting border-radius+overflow on the SAME element as backface-visibility
   triggers a long-standing WebKit bug where the back face leaks through. */
.bm-face {
  position: absolute;
  inset: 0;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  background: transparent;
  /* Belt-and-braces: explicitly hide whichever face is facing away. The
     delay snaps the swap at the midpoint of the 1.1s flip, so during the
     animation both faces are visible and the rotation reads correctly;
     at rest only the front-facing one paints. */
  transition: visibility 0s linear 0.55s;
}
.bm-face-back { transform: rotateY(180deg); }
.bm-sheet:not(.flipped) .bm-face-back { visibility: hidden; }
.bm-sheet.flipped .bm-face-front { visibility: hidden; }
.bm-face-inner {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--paper);
}
.bm-face-front .bm-face-inner {
  border-radius: 0 5px 5px 0;
  box-shadow:
    inset 22px 0 28px -24px rgba(50,30,8,0.55),
    inset -1px 0 0 var(--paper-edge),
    1px 0 1px rgba(0,0,0,0.08);
}
.bm-face-back .bm-face-inner {
  border-radius: 5px 0 0 5px;
  box-shadow:
    inset -22px 0 28px -24px rgba(50,30,8,0.55),
    inset 1px 0 0 var(--paper-edge),
    -1px 0 1px rgba(0,0,0,0.08);
}

/* Spine binding shadow visible when book is open */
.bm-binding {
  position: absolute;
  left: 50%;
  top: -2px; bottom: -2px;
  width: 14px;
  transform: translateX(-7px);
  z-index: 200;
  pointer-events: none;
  background:
    linear-gradient(to right,
      rgba(0,0,0,0) 0%,
      rgba(0,0,0,0.10) 18%,
      rgba(0,0,0,0.42) 50%,
      rgba(0,0,0,0.10) 82%,
      rgba(0,0,0,0) 100%);
  opacity: 1;
  transition: opacity 0.45s ease 0.3s;
}
body.mode-book.book-closed .bm-binding { opacity: 0; transition-delay: 0s; }
body.mode-book.book-end .bm-binding { opacity: 0; transition-delay: 0s; }

/* Generic page styling shared by image / prose / title / endpaper pages. */
.bm-page {
  position: absolute;
  inset: 0;
  background: var(--paper);
  background-image:
    radial-gradient(ellipse 70% 45% at 50% 8%, rgba(255,255,255,0.35) 0%, transparent 65%),
    radial-gradient(ellipse 90% 65% at 50% 100%, rgba(140,100,40,0.07) 0%, transparent 70%);
  overflow: hidden;
}
/* Subtle paper grain on every page (multiply blend with the page bg). */
.bm-page::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(circle at 17% 23%, rgba(120,80,30,0.05) 0 1px, transparent 1.5px),
    radial-gradient(circle at 73% 41%, rgba(120,80,30,0.04) 0 1px, transparent 1.5px),
    radial-gradient(circle at 41% 78%, rgba(120,80,30,0.05) 0 1px, transparent 1.5px),
    radial-gradient(circle at 89% 86%, rgba(120,80,30,0.04) 0 1px, transparent 1.5px);
  background-size: 31px 29px, 38px 41px, 27px 33px, 44px 37px;
  pointer-events: none;
  mix-blend-mode: multiply;
}

/* === COVER (cloth-bound, gold-foil frame, no image) ===================== */
.bm-cover, .bm-back-cover {
  background:
    radial-gradient(ellipse 80% 60% at 25% 20%, rgba(255,255,255,0.06) 0%, transparent 60%),
    linear-gradient(135deg, var(--cloth-2) 0%, var(--cloth) 50%, var(--cloth-3) 100%);
  overflow: hidden;
}
.bm-cover::before, .bm-back-cover::before {
  /* Replace the paper grain with a faint linen weave on the cloth. */
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(0deg,   rgba(255,255,255,0.025) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg,  rgba(0,0,0,0.06)        0 1px, transparent 1px 3px);
  pointer-events: none;
  opacity: 0.8;
  mix-blend-mode: normal;
}
.bm-cover-spine {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 18px;
  background:
    linear-gradient(to right,
      rgba(0,0,0,0.55) 0%,
      rgba(0,0,0,0.35) 35%,
      rgba(0,0,0,0.10) 75%,
      rgba(0,0,0,0) 100%);
  z-index: 5;
}
.bm-back-cover-spine {
  position: absolute;
  right: 0; top: 0; bottom: 0;
  width: 18px;
  background:
    linear-gradient(to left,
      rgba(0,0,0,0.55) 0%,
      rgba(0,0,0,0.35) 35%,
      rgba(0,0,0,0.10) 75%,
      rgba(0,0,0,0) 100%);
  z-index: 5;
}
.bm-cover-inner {
  position: absolute;
  inset: 38px 44px 38px 56px;
  border: 1px solid rgba(227,193,120,0.55);
  border-radius: 1px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px 32px;
  z-index: 2;
}
.bm-cover-inner::after {
  /* inner double rule */
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px solid rgba(227,193,120,0.30);
  border-radius: 1px;
  pointer-events: none;
}
.cover-eyebrow {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--gold-2);
  opacity: 0.85;
  margin-bottom: 18px;
}
/* Cover/title/finis ornaments — dot · ✦ · dot. Flexbox layout puts the
   star DEAD CENTER between the two dots regardless of the unicode glyph's
   own bounding-box quirks. The bare ✦ character isn't horizontally
   centered in its glyph box, so margin-based layouts always looked
   left-leaning. flex + gap fixes it. */
.cover-ornament,
.title-ornament {
  margin-bottom: 28px;
  user-select: none;
}
.title-ornament { margin-bottom: 0; }
.ornament-row {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  line-height: 1;
}
.ornament-row .dot {
  display: inline-block;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--gold-2);
}
.ornament-row .ornament-star {
  font-family: var(--serif);
  font-style: normal;
  font-size: 22px;
  line-height: 1;
  color: var(--gold-2);
  opacity: 0.9;
}
.ornament-row .ornament-logo {
  width: 22px;
  height: 22px;
  object-fit: contain;
  display: block;
}
/* Title page is on warm paper, not cloth — recolor the ornament. */
.bm-title-page .ornament-row .dot { background: var(--ink-faint); }
.bm-title-page .ornament-row .ornament-star { color: var(--ink-faint); opacity: 1; }
.cover-title {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(34px, 5.4vw, 56px);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--gold-2);
  margin: 0 0 18px;
  text-shadow:
    0 1px 0 rgba(0,0,0,0.3),
    0 0 12px rgba(227,193,120,0.18);
  max-width: 90%;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.cover-question {
  font-family: var(--serif);
  font-style: italic;
  font-size: 16px;
  letter-spacing: 0.05em;
  color: rgba(227,193,120,0.78);
  margin: 0;
  max-width: 90%;
  overflow-wrap: anywhere;
}
.cover-imprint {
  position: absolute;
  bottom: 60px;
  left: 0; right: 0;
  text-align: center;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(227,193,120,0.55);
  z-index: 2;
}

/* Back cover — minimal device */
.bm-back-cover-inner {
  position: absolute;
  inset: 38px 56px 38px 44px;
  border: 1px solid rgba(227,193,120,0.40);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.back-mark {
  text-align: center;
  line-height: 1.7;
  max-width: 60%;
  /* Container stays neutral so the upright sparkle reads as upright; the
     italic styling applies only to the text. */
}
.back-mark .sparkle {
  display: block;
  font-family: var(--serif);
  font-style: normal;        /* upright ✦ — italic was making it lean left */
  font-size: 18px;
  margin-bottom: 12px;
  letter-spacing: 0;
  color: var(--gold-2);
  opacity: 0.7;
}
.back-mark .back-mark-text {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  letter-spacing: 0.04em;
  color: rgba(227,193,120,0.55);
}

/* === ENDPAPER (inside front + inside back) ============================== */
.bm-endpaper {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 90% 70% at 50% 50%, #1f2a4a 0%, #131830 100%);
  overflow: hidden;
}
/* Endpaper has NO ::before dots — the Storybook design's constellation
   pattern read as 'dirty' against the smooth navy background once it was
   rendered at production size, so it was removed 2026-05-18. The bm-page
   inherits a paper-grain ::before from .bm-page, but we override that to
   `display: none` so the endpapers stay clean dark navy. */
.bm-endpaper::before { display: none; }
.bm-endpaper-mark {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: rgba(227,193,120,0.45);
  letter-spacing: 0.16em;
}

/* === TITLE PAGE ========================================================= */
.bm-title-page .title-block {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px 50px;
}
.bm-title-page .title-eyebrow {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 32px;
}
.bm-title-page .title-rule {
  width: 56px;
  height: 1px;
  background: var(--ink-faint);
  opacity: 0.5;
  margin: 0 auto 32px;
}
.bm-title-page .title-main {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(30px, 4.8vw, 52px);
  line-height: 1.05;
  color: var(--ink);
  letter-spacing: -0.01em;
  margin: 0 0 24px;
  max-width: 100%;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.bm-title-page .title-question {
  font-family: var(--serif);
  font-style: italic;
  font-size: 17px;
  color: var(--ink-soft);
  margin: 0 0 48px;
  letter-spacing: 0.02em;
  max-width: 90%;
  overflow-wrap: anywhere;
}
.bm-title-page .title-ornament {
  font-size: 22px;
  color: var(--ink-faint);
  letter-spacing: 0.4em;
  user-select: none;
}
.bm-title-page .title-imprint {
  position: absolute;
  bottom: 56px;
  left: 0; right: 0;
  text-align: center;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
  opacity: 0.7;
}

/* === IMAGE PAGE (LEFT page of a scene spread) =========================== */
/* Pure flexbox layout — NOT absolute positioning. Earlier attempts at
   absolute positioning had a containing-block walk-up bug in some
   browsers (the .illo's `position: absolute` could resolve to the
   .flipbook ancestor instead of .bm-image-page, dragging the image to
   the webpage center instead of the left-page center). Flex layout
   sidesteps all containing-block / 3D-transform quirks. */
.bm-image-page {
  display: flex;
  flex-direction: column;
  padding: 28px;
}
.bm-image-page .scene-num {
  flex: 0 0 auto;
  margin-bottom: 14px;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.bm-image-page .illo {
  /* flex: 1 1 0 with min-height:0 lets the flex item shrink to the
     real available space (default min-content would hold it open to the
     image's natural height). Image inside is then centered via flex. */
  flex: 1 1 0;
  min-height: 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.bm-image-page .illo .illustration-container {
  /* Container fills .illo. Image inside fits via object-fit:contain so
     the full painting always shows, centered, with letterboxing only
     where the page aspect doesn't match. */
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0;
  border-radius: 3px;
  /* Override scroll-mode rules that would clobber book layout: */
  aspect-ratio: auto;        /* scroll mode sets 3/2 */
  background: transparent;   /* scroll mode sets #eee / var(--bg) */
  overflow: hidden;
  /* No box-shadow in book mode — founder direction: clean, no shadow. */
}
.bm-image-page .illo .illustration {
  display: block;
  width: 100%;
  height: 100%;
  /* CRITICAL: explicit contain (not cover). The scroll-mode .illustration
     rule sets `object-fit: cover` which would crop/zoom the image to fill
     the container. Without this override the image bleeds across the
     page (founder reported 2026-05-18: "image left edge starts at center,
     cuts off at right edge, repeats from left edge" — that was cover
     scaling the 3:2 painting to fill the taller-than-wide page box). */
  object-fit: contain;
  object-position: center center;
  /* Multiply blends watercolor whites into the cream paper so the plate
     reads like printed art rather than a pasted PNG. Matches scroll mode. */
  mix-blend-mode: multiply;
}
.bm-image-page .illo .illustration-placeholder {
  /* Placeholder during generation — keep simple 3:2 aspect. */
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  max-height: 100%;
}

/* === PROSE PAGE (RIGHT page of a scene spread) ========================== */
/* Pure flex layout — see .bm-image-page above for rationale. */
.bm-prose-page {
  display: flex;
  flex-direction: column;
  padding: 28px;
}
.bm-prose-page .scene-head {
  flex: 0 0 auto;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.bm-prose-page .scene-head .swatch {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent, var(--ink-faint));
  margin-right: 10px;
  vertical-align: middle;
}
.bm-prose-page .prose {
  /* Flex 1 to fill the available space between scene-head and page-foot.
     min-height:0 to allow shrink. justify-content:center vertically
     centers the text within the available column. */
  flex: 1 1 0;
  min-height: 0;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink);
  padding: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(140,100,40,0.35) transparent;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.bm-prose-page .prose::-webkit-scrollbar { width: 4px; }
.bm-prose-page .prose::-webkit-scrollbar-thumb {
  background: rgba(140,100,40,0.35);
  border-radius: 2px;
}
.bm-prose-page .prose p {
  margin: 0 0 14px;
  text-align: justify;
  hyphens: auto;
}
.bm-prose-page .prose p:last-child { margin-bottom: 0; }
.bm-prose-page .prose p:first-child::first-letter {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 44px;
  line-height: 0.9;
  float: left;
  padding: 2px 8px 0 0;
  color: var(--accent, var(--ink-soft));
}
.bm-prose-page .bubble {
  font-size: 13.5px;
  padding: 8px 14px;
  margin: 12px auto;
}

/* Folio + running title. In flex-layout context (image-page, prose-page)
   this sits at the bottom of the flex column. Image-page reverses the
   inline order so the folio sits on the outer (left) edge of the left
   page; prose-page keeps natural order so its folio sits on the outer
   (right) edge of the right page. */
.bm-image-page .page-foot,
.bm-prose-page .page-foot {
  flex: 0 0 auto;
  margin-top: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--sans);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  opacity: 0.78;
}
.bm-image-page .page-foot { flex-direction: row-reverse; }
.bm-page .page-foot .page-foot-title {
  max-width: 60%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* === FINIS / colophon page (LEFT page of the closing spread) ============ */
.bm-end {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 60px 50px;
}
.bm-end .finis {
  font-family: var(--serif);
  font-style: italic;
  font-size: 30px;
  letter-spacing: 0.18em;
  color: var(--ink-soft);
  margin: 0 0 24px;
}
.bm-end .ornament {
  margin-bottom: 32px;
  user-select: none;
}
/* Finis-page recolor: dots match warm-paper ink, logo sized to match
   the ornament-row height. */
.bm-end .ornament-row .dot { background: var(--ink-faint); }
.bm-end .ornament-row .ornament-logo {
  width: 26px;
  height: 26px;
  opacity: 0.9;
}
.bm-end .colophon {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-faint);
  max-width: 36ch;
  margin: 0;
}

/* Scroll-mode .illustration-container styles further down still target
   scroll mode only; book-mode versions are scoped under .bm-image-page
   above so they don't conflict. */


.book-footer {
  margin-top: 24px;
  padding: 18px 8px 0;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-tertiary);
  text-align: center;
}

.illustration-container {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  margin-bottom: 18px;
  background: #eee;
}
.illustration {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Blend solid-white image backgrounds into the warm paper page. White
     pixels multiply with cream (#fafaf7) → match the page exactly; subjects
     stay legible (watercolor colors get a soft warm tint, not muddy). */
  mix-blend-mode: multiply;
}
.illustration-container {
  /* multiply needs SOMETHING under it — keep the page color visible. */
  background: var(--bg);
}

/* === Inline alt-versions picker — replaces .illustration when a scene
   has 2+ viable attempts and the user hasn't picked yet. ChatGPT-style:
   two images side by side, click one to choose. No labels, no rationale. */
.illustration-container.alt-pick {
  aspect-ratio: auto;
  background: transparent;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  overflow: visible;
}
.alt-pick-card {
  position: relative;
  display: block;
  padding: 0;
  margin: 0;
  border: 2px solid transparent;
  border-radius: 14px;
  overflow: hidden;
  background: var(--bg);
  cursor: pointer;
  aspect-ratio: 3 / 2;
  transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
  font: inherit;
  color: inherit;
}
.alt-pick-card:hover {
  border-color: rgba(61, 41, 24, 0.55);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.10);
}
.alt-pick-card:focus-visible {
  outline: none;
  border-color: #3d2918;
  box-shadow: 0 0 0 3px rgba(61, 41, 24, 0.18);
}
.alt-pick-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  mix-blend-mode: multiply;
}
/* Visual lock when a card is mid-request: chosen card grows, others fade. */
.alt-pick.alt-pick-busy { pointer-events: none; }
.alt-pick.alt-pick-busy .alt-pick-card { opacity: 0.45; }
.alt-pick.alt-pick-busy .alt-pick-card.alt-pick-chosen {
  opacity: 1;
  border-color: #3d2918;
  transform: scale(1.01);
}
.alt-pick-caption {
  margin: -8px 0 14px;
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}

/* Preview-mode text page: scene_2 has no illustration slot because the
   signup fade only exposes the first paragraph. */
.scene-preview-text {
  padding: 36px 0 28px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.scene-preview-text:last-child { border-bottom: 0; }
.scene-preview-text .prose {
  font-size: 17px;
  line-height: 1.65;
  max-width: 100%;
}
.scene-preview-text .prose p {
  margin: 0 0 18px;
}
.scene-preview-text .prose p:last-child { margin-bottom: 0; }

/* Brand-defense caption — shown under the image when a scene was redacted
   (blurred to avoid a recognizable trademark) or marked unrenderable
   (no safe image could be produced). Kept low-key so it doesn't break
   the picture-book rhythm; honest about what happened. */
.brand-defense-caption {
  margin: -8px 0 14px;
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 12px;
  color: var(--text-tertiary);
  letter-spacing: 0.01em;
}

/* === Per-scene feedback (hover-only thumbs) ====================== */
/* #144 hotfix (2026-05-19, founder feedback): hide both the per-scene
   thumbs (.scene-feedback) and the end-of-book "How was this book?"
   card (.book-feedback). The CSS-side hide covers existing static
   books too, since reader.css is shared across all of them. The
   server-side renderer.py can stop emitting the markup later in a
   separate PR. */
.scene-feedback,
.book-feedback {
  display: none !important;
}

/* Buttons live absolutely-positioned over the scene; invisible until
   .scene is hovered. One vote per scene per user; click toggles the
   button to a "selected" state and POSTs to /api/book/{slug}/feedback. */
.scene { position: relative; }
.scene-feedback {
  position: absolute;
  top: 12px;
  right: 12px;
  display: flex;
  gap: 6px;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.18s, transform 0.18s;
  z-index: 4;
  pointer-events: none;
}
.scene:hover .scene-feedback,
.scene-feedback:focus-within,
.scene-feedback.sf-locked {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.scene-feedback .sfb {
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(60, 40, 18, 0.20);
  border-radius: 999px;
  width: 32px; height: 32px;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.10);
  transition: background 0.15s, transform 0.10s, border-color 0.15s;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.scene-feedback .sfb:hover { background: #fff; transform: scale(1.06); }
.scene-feedback .sfb.sfb-selected {
  background: #fbf6e9;
  border-color: var(--text-primary, #1a1a1a);
  border-width: 1.5px;
}
.scene-feedback .sfb-up.sfb-selected { background: #e8f5d8; border-color: #4a7c2a; }
.scene-feedback .sfb-down.sfb-selected { background: #fbe5e0; border-color: #a04a3a; }

/* === End-of-book feedback card =================================== */
.book-feedback {
  margin: 56px 0 32px;
  padding: 0 16px;
}
.book-feedback-card {
  max-width: 480px;
  margin: 0 auto;
  background: linear-gradient(180deg, #fdf6e0 0%, #f6ecca 100%);
  border: 1px solid rgba(80, 50, 15, 0.22);
  border-radius: 14px;
  padding: 22px 24px 20px;
  text-align: center;
  font-family: var(--sans);
}
.book-feedback-title {
  margin: 0 0 14px;
  font-family: 'Caveat', 'Nanum Pen Script', cursive;
  font-weight: 700;
  font-size: 26px;
  color: #3d2918;
}
.book-feedback-buttons {
  display: flex;
  gap: 14px;
  justify-content: center;
  margin-bottom: 12px;
}
.book-feedback-buttons .bfb {
  background: #fff;
  border: 1.5px solid rgba(60, 40, 18, 0.30);
  border-radius: 999px;
  width: 52px; height: 52px;
  font-size: 22px;
  cursor: pointer;
  padding: 0;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.10s, background 0.15s, border-color 0.15s;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.book-feedback-buttons .bfb:hover { transform: scale(1.08); background: #fbf6e9; }
.book-feedback-buttons .bfb-up.bfb-selected { background: #e8f5d8; border-color: #4a7c2a; }
.book-feedback-buttons .bfb-down.bfb-selected { background: #fbe5e0; border-color: #a04a3a; }
.book-feedback-comment {
  width: 100%;
  background: #fff;
  border: 1px solid rgba(80, 50, 15, 0.18);
  border-radius: 8px;
  padding: 10px 12px;
  font-family: var(--sans);
  font-size: 13px;
  color: #3d2918;
  resize: vertical;
  min-height: 50px;
  box-sizing: border-box;
}
.book-feedback-comment:focus {
  outline: none;
  border-color: var(--text-primary, #1a1a1a);
}
.book-feedback-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 6px;
}
.book-feedback-save {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 8px;
  border: 1px solid rgba(80, 50, 15, 0.25);
  background: #fbf6e9;
  color: #3d2918;
  cursor: pointer;
  transition: background 120ms, opacity 120ms;
}
.book-feedback-save:hover:not(:disabled) { background: #f5ecd2; }
.book-feedback-save:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.book-feedback-status {
  margin-top: 8px;
  min-height: 18px;
  font-size: 12px;
  color: #4a7c2a;
  font-style: italic;
}

/* Book colophon — soft wonderleaf signoff at the end of the story.
   Always rendered. The 140px bottom margin doubles as scroll buffer so
   bottom-pinned UI (trial-mode claim banner, any future floating
   affordance) cannot obscure the last line of story text. */
.book-colophon {
  margin: 72px auto 140px;
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  opacity: 0.42;
  transition: opacity 240ms ease-out;
  user-select: none;
}
.book-colophon:hover { opacity: 0.65; }
.colophon-mark {
  width: 38px;
  height: 38px;
  display: block;
}
.colophon-text {
  font-family: var(--serif);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
}
.colophon-slogan {
  font-family: var(--serif);
  font-size: 11px;
  font-style: italic;
  color: var(--ink);
  text-align: center;
  max-width: 32ch;
  line-height: 1.5;
}
.book-colophon.has-recommendations {
  margin-bottom: 42px;
}

.reader-recommendations {
  width: min(820px, calc(100vw - 32px));
  margin: 0 0 132px 50%;
  transform: translateX(-50%);
  padding: 24px;
  border: 1px solid #ded3bd;
  border-radius: 8px;
  background: #fffdf7;
  box-shadow: 0 10px 28px rgba(64, 42, 24, 0.08);
  font-family: var(--sans);
}
.reader-recommendations[hidden] { display: none !important; }
@media (min-width: 761px) {
  body.mode-book #readerRecommendations:not([hidden]) {
    display: none;
  }
  /* The back cover shows ALONE — recs do NOT overlay it. They live on their
     own navigable page (`book-recs`), reached by clicking "next" once more at
     the back cover, and returned-from with "previous". This avoids
     repositioning the 3D flipbook (unreliable across widths — the side-by-side
     split was reverted 2026-06-01); the card is a normal centered fixed element. */
  body.mode-book.book-recs .flipbook {
    opacity: 0;
    pointer-events: none;
  }
  body.mode-book.book-recs #readerRecommendations:not([hidden]) {
    display: block;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    /* Leave the outer-edge prev/next nav-zone arrows (~58px) un-occluded even
       at the narrowest book-mode width (761px) — the card sits z-index above
       them, so it must not cover the primary "previous" escape (#161). */
    width: min(760px, calc(100vw - 200px));
    max-height: calc(100vh - 96px);
    overflow-y: auto;
    z-index: 95;
    border-radius: 8px;
    box-shadow: 0 18px 52px rgba(20, 14, 10, 0.26);
    animation: readerRecsIn 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  }
  @keyframes readerRecsIn {
    from { opacity: 0; transform: translate(calc(-50% + 56px), -50%); }
    to   { opacity: 1; transform: translate(-50%, -50%); }
  }
  body.mode-book.book-recs .reader-rec-back { display: inline-flex; }
}
.reader-rec-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 20px;
}
.reader-rec-back {
  flex: none;
  align-self: center;
  /* Hidden everywhere by default — only the desktop book-recs page (the
     extra page after the back cover) reveals it. In scroll view / on phones
     the recs are normal scroll content with no book to return to, so this
     control must not appear there. Revealed in the @media block below. */
  display: none;
  align-items: center;
  min-height: 38px;
  padding: 0 16px;
  border: 1px solid #ded3bd;
  border-radius: 999px;
  background: #fffdf7;
  color: #5d4022;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
}
.reader-rec-back:hover { background: #f5ecd1; }
.reader-rec-kicker {
  margin: 0 0 4px;
  color: #6f8d5d;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.reader-rec-head h2 {
  margin: 0;
  font-family: var(--serif);
  font-size: 28px;
  line-height: 1.1;
  color: #3d2918;
  letter-spacing: 0;
}
.reader-rec-group + .reader-rec-group {
  margin-top: 22px;
}
.reader-rec-group h3 {
  margin: 0 0 10px;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.2;
  color: #4b3320;
  letter-spacing: 0;
}
.reader-rec-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.reader-rec-card {
  background: #fffaf0;
  border: 1px solid #d8c39a;
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  color: inherit;
  text-align: left;
  font: inherit;
  padding: 0;
  transition: transform 160ms ease, box-shadow 160ms ease,
              border-color 160ms ease;
  box-shadow: 0 1px 2px rgba(90, 56, 37, 0.05);
}
.reader-rec-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(90, 56, 37, 0.14);
  border-color: #8a6f3a;
}
.reader-rec-card:focus-visible {
  outline: none;
  border-color: #6f8d5d;
  box-shadow: 0 0 0 3px rgba(111, 141, 93, 0.25),
              0 6px 18px rgba(90, 56, 37, 0.14);
}
.reader-recommendations .pick-image {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
  background: #f4ead4;
  overflow: hidden;
}
.reader-recommendations .pick-image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.reader-recommendations .pick-image.no-image {
  background:
    radial-gradient(ellipse 60% 60% at 30% 40%,
      rgba(122, 82, 56, 0.18) 0%, transparent 70%),
    radial-gradient(ellipse 50% 60% at 70% 65%,
      rgba(45, 108, 179, 0.14) 0%, transparent 70%),
    #f4ead4;
}
.reader-recommendations .pick-chip,
.reader-recommendations .pick-lang-badge {
  position: absolute;
  top: 8px;
  background: rgba(26, 29, 36, 0.72);
  color: #fffdf7;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 4px 7px;
  border-radius: 4px;
}
.reader-recommendations .pick-chip {
  left: 8px;
  max-width: calc(100% - 16px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-transform: uppercase;
}
.reader-recommendations .pick-lang-badge {
  right: 8px;
}
.reader-recommendations .pick-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reader-recommendations .pick-title {
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  color: #3d2918;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.reader-recommendations .pick-question {
  font-size: 12px;
  font-style: italic;
  color: var(--text-secondary);
  line-height: 1.35;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

@media (max-width: 620px) {
  .reader-recommendations {
    width: calc(100vw - 24px);
    padding: 18px;
  }
  .reader-rec-head {
    display: block;
  }
  .reader-rec-row {
    grid-template-columns: 1fr;
  }
}
/* PDF/print export: keep the colophon (it's part of the book), but tighten
   the bottom margin since there's no floating UI on paper. */
@media print {
  .book-colophon { margin: 56px auto 24px; opacity: 0.55; }
  .reader-recommendations { display: none !important; }
}

/* Phase 3 Preview-Mode paywall — fade overlay + sticky CTA card.
   The .preview-truncated-wrap holds scene_2 (the chopped-to-first-paragraph
   one); .preview-fade is the gradient that visually implies "more content
   below" — server-side truncation means there's literally nothing below
   that point in the HTML besides the paywall card itself. */
.preview-truncated-wrap {
  position: relative;
  overflow: hidden;
  /* Limit the visible height of scene_2 so we get a clean fade boundary.
     The first paragraph's lower half disappears into the gradient. */
}
.preview-truncated-wrap .scene {
  /* Cap the visible portion of the scene below the title/image —
     enough to see the start of the prose, not enough to read all of it.
     Tuned to the typical first-paragraph layout. */
  max-height: 320px;
}
.preview-fade {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 240px;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(253, 251, 243, 0) 0%,
    rgba(253, 251, 243, 0.85) 55%,
    rgba(253, 251, 243, 1) 100%
  );
  z-index: 2;
}
.preview-paywall {
  display: block;
  max-width: 640px;
  margin: 8px auto 120px;
  padding: 0 16px;
}
.preview-paywall-card {
  background: #ffffff;
  border: 1px solid #d4c39a;
  border-radius: 18px;
  padding: 28px 28px 24px;
  box-shadow: 0 8px 28px rgba(80,50,15,0.10);
  text-align: center;
}
.preview-paywall-headline {
  margin: 0 0 8px;
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 600;
  color: #3d2918;
  letter-spacing: -0.01em;
}
.preview-paywall-sub {
  margin: 0 0 18px;
  font-size: 14px;
  color: #5d4022;
  line-height: 1.5;
}
.preview-paywall-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.preview-paywall-btn {
  font-family: 'Quicksand', sans-serif;
  font-size: 14px;
  font-weight: 600;
  padding: 11px 22px;
  border-radius: 10px;
  border: 1px solid rgba(80,50,15,0.25);
  background: #ffffff;
  color: #3d2918;
  cursor: pointer;
  transition: background 120ms, transform 80ms;
}
.preview-paywall-btn:hover { background: #fbf6e9; transform: translateY(-1px); }
.preview-paywall-btn.primary {
  background: #7a9568;
  color: #ffffff;
  border-color: #7a9568;
}
.preview-paywall-btn.primary:hover { background: #6b8757; }
.preview-paywall-card.completing {
  background: linear-gradient(135deg, #fff8df 0%, #f7eccb 100%);
  border-color: #d4c39a;
}
.preview-paywall-spinner {
  width: 28px; height: 28px;
  margin: 6px auto 0;
  border: 3px solid rgba(122,149,104,0.25);
  border-top-color: #7a9568;
  border-radius: 50%;
  animation: preview-spin 0.9s linear infinite;
}
@keyframes preview-spin {
  to { transform: rotate(360deg); }
}
@media print {
  /* PDF export of a preview-mode book is nonsensical, but defensively
     hide the paywall + fade if it ever happens. */
  .preview-paywall, .preview-fade { display: none !important; }
  .preview-truncated-wrap .scene { max-height: none; }
}

/* Dev affordance: per-illustration <details> showing the image_prompt.
   Hidden by default; revealed when body has .dev-mode (URL ?dev=1, local
   hostname, or 'D' keypress). Production users never see it.
   Always hidden on print/PDF export — even when Playwright loads the
   file via file:// (which trips the local-host auto-dev-mode), the PDF
   stays clean and only contains the picture book itself. */
.image-prompt-dev { display: none; }
@media print {
  .image-prompt-dev,
  body.dev-mode .image-prompt-dev { display: none !important; }
}
body.dev-mode .image-prompt-dev {
  display: block;
  margin: -10px 0 14px;
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 11px;
  color: #6b6b6b;
  background: rgba(0,0,0,0.04);
  border: 1px dashed rgba(0,0,0,0.18);
  border-radius: 4px;
  padding: 6px 10px;
}
body.dev-mode .bm-scene-page .image-prompt-dev {
  margin: 0 0 6px;
  font-size: 10px;
  padding: 4px 8px;
  flex: 0 0 auto;
}
body.dev-mode .image-prompt-dev summary {
  cursor: pointer;
  user-select: none;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 10px;
  color: #888;
  display: flex;
  align-items: center;
  gap: 10px;
}
body.dev-mode .image-prompt-dev .ip-copy {
  margin-left: auto;
  font-family: inherit;
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #4a4d54;
  background: #fff;
  border: 1px solid rgba(0,0,0,0.25);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
  line-height: 1.3;
}
body.dev-mode .image-prompt-dev .ip-copy:hover {
  background: #1a1a1a;
  color: #fff;
  border-color: #1a1a1a;
}
body.dev-mode .image-prompt-dev .ip-copy.is-copied {
  background: #1a8556;
  color: #fff;
  border-color: #1a8556;
}
body.dev-mode .image-prompt-dev pre {
  margin: 6px 0 0;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.45;
  color: #333;
}
body.dev-mode .image-prompt-dev .ip-beat {
  margin: 6px 0 0;
  font-size: 13px;
  color: #5a4525;
  font-style: italic;
}
body.dev-mode .image-prompt-dev .ip-beat-label,
body.dev-mode .image-prompt-dev .ip-state-label {
  display: inline-block;
  font-style: normal;
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #888;
  margin-right: 6px;
}
body.dev-mode .image-prompt-dev .ip-state {
  margin: 6px 0 0;
  font-size: 12.5px;
  color: #5a4525;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
body.dev-mode .image-prompt-dev .ip-state-row {
  display: flex;
  gap: 8px;
  align-items: baseline;
}
body.dev-mode .image-prompt-dev .ip-state-id {
  font-weight: 600;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 11.5px;
  color: #444;
  min-width: 60px;
}
body.dev-mode .image-prompt-dev .ip-state-phrase {
  font-style: italic;
  color: #5a4525;
}
.illustration-placeholder {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Theme bg is set per-scene via .scene[data-theme="..."] rules below;
     the gradient layers ride on top of it. */
}
/* Three radial watercolor blooms, each in the theme's accent color,
   layered + offset to feel hand-painted. The opacity pulse cycles
   subtly so the page reads as "in progress, but settled" rather than a
   spinner. --accent inherits from the scene's color_theme. */
.illustration-placeholder .ph-bloom {
  position: absolute;
  border-radius: 50%;
  filter: blur(28px);
  opacity: 0.55;
  mix-blend-mode: multiply;
  animation: ph-pulse 3.6s ease-in-out infinite;
}
.illustration-placeholder .ph-bloom-a {
  top: -12%; left: -8%;
  width: 70%; height: 70%;
  background: var(--accent, #888);
  opacity: 0.30;
  animation-delay: 0s;
}
.illustration-placeholder .ph-bloom-b {
  bottom: -18%; right: -12%;
  width: 80%; height: 80%;
  background: var(--accent, #888);
  opacity: 0.22;
  animation-delay: 0.9s;
}
.illustration-placeholder .ph-bloom-c {
  top: 30%; left: 35%;
  width: 45%; height: 45%;
  background: var(--accent, #888);
  opacity: 0.18;
  animation-delay: 1.8s;
}
/* Subtle paper-grain crosshatch overlay so the swatch feels textured
   like watercolor paper, not flat CSS gradients. */
.illustration-placeholder .ph-grain {
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(45deg,  transparent 0 11px, rgba(0,0,0,0.025) 11px 12px),
    repeating-linear-gradient(-45deg, transparent 0 17px, rgba(255,255,255,0.05) 17px 18px);
  pointer-events: none;
  mix-blend-mode: overlay;
}
.illustration-placeholder .ph-page-number {
  position: relative;
  z-index: 2;
  font-family: var(--serif);
  font-size: 64px;
  font-weight: 600;
  color: var(--accent, #555);
  opacity: 0.35;
  text-shadow: 0 1px 0 rgba(255,255,255,0.6);
  user-select: none;
}
@keyframes ph-pulse {
  0%, 100% { transform: scale(1) translate(0, 0); }
  50%      { transform: scale(1.06) translate(2%, -1%); }
}
/* Horizontal brush-wash that drifts L->R across the placeholder. Pairs
   with the soft radial blooms above so the swatch reads as "wet paper
   with paint settling in" — calmer than a spinner, still visibly alive. */
.illustration-placeholder .ph-wash {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.16) 38%,
    rgba(255, 255, 255, 0.30) 50%,
    rgba(255, 255, 255, 0.16) 62%,
    transparent 100%
  );
  transform: translateX(-100%);
  animation: ph-wash 3.4s ease-in-out infinite;
  mix-blend-mode: soft-light;
  pointer-events: none;
}
@keyframes ph-wash {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .illustration-placeholder .ph-bloom,
  .illustration-placeholder .ph-wash { animation: none; }
}

/* Caption strip — sits BELOW the image, never on top. Picture-book legend
   aesthetic: tiny color dots + handwritten label text, comma-separated. */
.caption-strip {
  font-family: 'Caveat', 'Nanum Pen Script', 'Yomogi', 'Liu Jian Mao Cao', cursive;
  font-size: 18px;
  font-weight: 500;
  line-height: 1.35;
  color: #5a5a5a;
  text-align: center;
  margin: -6px 0 16px;
  padding: 0 14px;
}
.caption-strip .caption-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  margin: 0 4px;
}
.caption-strip .caption-item::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dot, #888);
  flex-shrink: 0;
}
.caption-strip .caption-sep {
  color: #c4c4c4;
  margin: 0 4px;
  font-weight: 400;
}

.prose {
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.72;
  color: var(--text-primary);
  padding: 0 6px;
}
.prose p { margin: 0 0 14px; }
.prose p:last-child { margin-bottom: 0; }

.bubble {
  display: block;
  padding: 11px 18px;
  margin: 16px auto;
  border-radius: 18px;
  max-width: 360px;
  text-align: center;
  font-family: var(--sans);
  font-size: 15.5px;
  font-weight: 400;
  line-height: 1.45;
}
.bubble[data-speaker]::before {
  content: attr(data-speaker);
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 3px;
}
.bubble-emote {
  display: inline-block;
  font-size: 1.3em;
  margin-left: 5px;
  vertical-align: -2px;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.1));
}

/* === Inline emphasis effects ============================================= */
/* Locked vocabulary (Rikku 2026-05-05): pop, marker, handnote, shout, stamp,
   rainbow. Static-first so PDF capture works; entry animations resolve TO the
   static state, never away from it.                                          */

.fx { display: inline-block; }

.fx-pop {
  color: var(--accent, #c93a1f);
  font-weight: 600;
  transform: rotate(-1.5deg);
  text-shadow: 0 1px 0 rgba(0,0,0,0.04);
  padding: 0 1px;
  animation: fx-pop-in 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

/* Yellow-highlighter underline — restored 2026-05-19. #94 had pulled
   the background gradient because long phrases couldn't wrap (one
   inline-block span = one unbreakable layout unit, so a long highlight
   either fit on one line or left a giant gap). With the per-word
   fx-span splitting from #120, each highlighted word is now its OWN
   inline-block atom; the background paints per word, the inter-word
   whitespace stays unhighlighted, and the line wraps naturally at
   those spaces. The phrase reads as a row of highlighter strokes —
   one per word — which is the "marker" feel without the broken wrap.
   Text color stays default ink so the yellow reads as overlay, not
   recoloring. */
.fx-marker {
  background-image: linear-gradient(180deg,
    transparent 0%, transparent 58%,
    rgba(254, 240, 138, 0.85) 58%,
    rgba(254, 224, 110, 0.78) 92%,
    transparent 96%);
  padding: 0 2px;
  border-radius: 1px;
  font-weight: 500;
  animation: fx-marker-draw 0.45s ease-out backwards;
}

.fx-handnote {
  /* Latin: Caveat / Korean: Nanum Pen Script / JP: Yomogi / Simplified CN: Liu Jian Mao Cao
     Browser falls through chain per glyph — single class, multilingual handwritten feel. */
  font-family: 'Caveat', 'Nanum Pen Script', 'Yomogi', 'Liu Jian Mao Cao', cursive;
  font-weight: 600;
  font-size: 1.18em;
  line-height: 1;
  color: #555;
  margin: 0 1px;
  animation: fx-handnote-in 0.5s ease-out backwards;
}

.fx-shout {
  /* Latin: Bangers / KR: Black Han Sans / JP: Hachi Maru Pop / SC: ZCOOL KuaiLe */
  font-family: 'Bangers', 'Black Han Sans', 'Hachi Maru Pop', 'ZCOOL KuaiLe', 'Inter', sans-serif;
  font-weight: 400;
  font-size: 1.22em;
  letter-spacing: 0.04em;
  color: #c93a1f;
  transform: rotate(-2deg);
  margin: 0 2px;
  animation: fx-shout-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

.fx-stamp {
  /* Latin: Permanent Marker / KR: Bagel Fat One / JP: RocknRoll One / SC: Ma Shan Zheng */
  font-family: 'Permanent Marker', 'Bagel Fat One', 'RocknRoll One', 'Ma Shan Zheng', 'Inter', sans-serif;
  font-weight: 400;
  font-size: 0.96em;
  color: var(--accent, #444);
  transform: rotate(-2.5deg);
  margin: 0 2px;
  animation: fx-stamp-in 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

/* Founder iteration 2026-05-19: fx-rainbow (per-word gradient color)
   was visually dizzying on multi-word phrases like "red, orange,
   yellow, purple" because the per-word fx-span split from #120 made
   every word repeat the full gradient. Neutralized to plain
   inheritable text — keeps the emphasis weight without the
   confusing color cycling. The animation is also disabled. */
.fx-rainbow {
  color: inherit;
  background-image: none;
  -webkit-background-clip: initial;
  background-clip: initial;
  -webkit-text-fill-color: inherit;
  font-weight: 600;
  animation: none;
}

@keyframes fx-pop-in {
  0%   { transform: scale(0.6) rotate(-12deg); opacity: 0; }
  60%  { transform: scale(1.12) rotate(2deg); opacity: 1; }
  100% { transform: scale(1) rotate(-1.5deg); opacity: 1; }
}
@keyframes fx-marker-draw {
  /* Gentle opacity fade-in per word. The highlighter background is back
     (2026-05-19, paired with per-word fx-span splitting from #120) but
     the entry stays a soft fade rather than a sweep so multi-word
     highlights don't feel busy — each word's yellow band fades in at
     the same beat as the rest of the prose. */
  from { opacity: 0.4; }
  to   { opacity: 1; }
}
@keyframes fx-handnote-in {
  0%   { opacity: 0; transform: translateY(-3px) rotate(-3deg); }
  100% { opacity: 1; transform: translateY(0) rotate(0); }
}
@keyframes fx-shout-in {
  0%   { transform: scale(0.5) rotate(8deg); opacity: 0; }
  60%  { transform: scale(1.18) rotate(-4deg); opacity: 1; }
  100% { transform: scale(1) rotate(-2deg); opacity: 1; }
}
@keyframes fx-stamp-in {
  0%   { transform: scale(2) rotate(-15deg); opacity: 0; }
  70%  { transform: scale(0.92) rotate(-1deg); opacity: 1; }
  100% { transform: scale(1) rotate(-2.5deg); opacity: 1; }
}
@keyframes fx-rainbow-in {
  0%   { background-position: 100% 0; opacity: 0; }
  100% { background-position: 0 0; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .fx, .fx-pop, .fx-marker, .fx-handnote, .fx-shout, .fx-stamp, .fx-rainbow {
    animation: none !important;
  }
}

/* Inside dialogue bubbles, accent colors should sit against the bubble bg
   without fighting it. The bubble already provides contrast — let pop/shout
   inherit the bubble's text color rather than the scene accent.            */
.bubble .fx-pop, .bubble .fx-shout { color: inherit; }

/* WS-A (#129/#149): neutral "Preparing next page…" affordance shown only
   while a page-turn decode gate is in flight (rare — scene images are
   eager + small WebP). Centered, soft, non-alarming. Never implies the
   book is broken. */
.page-turn-pending {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 40;
  padding: 8px 16px;
  border-radius: 999px;
  background: rgba(40, 28, 12, 0.82);
  color: #fff;
  font-family: "Fredoka", system-ui, sans-serif;
  font-size: 13px;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  pointer-events: none;
  animation: page-turn-pending-pulse 1.3s ease-in-out infinite;
}
.page-turn-pending[hidden] { display: none; }
@keyframes page-turn-pending-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .page-turn-pending { animation: none; opacity: 0.9; }
}

/* Shared-link "home" affordance — a centered top button-group. Per the founder's
   logic the group is centered, so with only the ✕ it lands dead-center (the
   button IS the whole group). Only shown on a directly-opened shared reader
   (window.top === self, wired in reader.js); in-app the shell's own ✕ is used.
   Matches the .view-toggle / .cover-jump pill aesthetic. */
.shared-home-bar {
  position: fixed;
  top: 12px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  z-index: 200;
  pointer-events: none;
}
.shared-home-bar > * { pointer-events: auto; }
.shared-home-x {
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.92);
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.shared-home-x:hover { color: var(--text-primary); }
