/*
  ============================================================
  DODO STORYLAND — KIDS STORYBOOKS (Phase 3)
  Shared styles for the public Storybook Hub (/stories/) and the
  Storybook Reader (/stories/<slug>/).
  ============================================================
  Deliberately its own small stylesheet, loaded ONLY on these pages
  (see pageShell()'s extraStyles option in scripts/build-content.js)
  rather than folded into the sitewide styles.css -- same reasoning
  as games/<name>/<name>.css: this is a fair amount of CSS that no
  other page on the site needs to download.

  Every color/spacing value below reuses the EXISTING design tokens
  from styles.css (--ink, --ink-soft, --surface, --surface-alt,
  --line, --shadow, --coral, --green, --purple, --gold, --blue) --
  nothing new is defined here, so light/dark mode and any future
  palette tweak in styles.css apply automatically, with zero
  duplication.

  Phase 3 rewrites the book itself: page turning is now driven by
  the vendored page-flip library (assets/vendor/page-flip.browser.js
  -- see reader.js), which owns each page's position/size/transform
  via its own inline styles. Phase 2's hand-rolled flip animation
  (.reader-flip, .reader-spine, the old mobile single-page-per-view
  media-query overrides) is gone -- the library now provides all of
  that (the physical curl AND the mobile single-page/portrait mode,
  via its own `usePortrait` setting) more realistically than CSS
  transforms could. The Hub section below is unchanged.
  ============================================================
*/

/* ---------- Storybook Hub ---------- */
/* `auto-fit` with a `1fr` upper bound stretches a lone card to fill the
   entire row -- fine with a full shelf of books, but with only one or
   two published it blew the cover up to near full-page-width. Capping
   the track at 280px keeps every card a fixed, book-sized card no
   matter how many are published, and `justify-content:center` keeps a
   short row centered instead of jammed to the left. */
.storybook-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 280px)); gap:24px;
  justify-content:center;
}
.storybook-card{
  display:flex; flex-direction:column; background:var(--surface); border:1px solid var(--line);
  border-radius:24px; overflow:hidden; box-shadow:var(--shadow); position:relative;
  transition:transform .2s ease, box-shadow .2s ease;
}
.storybook-card:hover{ transform:translateY(-4px); }
.storybook-cover-link{ display:block; position:relative; }
/* A4 portrait ratio (210:297) -- reads as an actual book cover instead
   of a cropped landscape photo, and the whole cover stays visible
   rather than being cropped down to fit a wide box. */
.storybook-cover{
  width:100%; aspect-ratio:210/297; object-fit:cover; display:block; background:var(--surface-alt);
}
.storybook-cover-placeholder{
  width:100%; aspect-ratio:210/297; display:flex; align-items:center; justify-content:center; gap:-10px;
  background:linear-gradient(135deg, color-mix(in srgb, var(--gold) 55%, var(--surface-alt)), color-mix(in srgb, var(--blue) 40%, var(--surface-alt)));
}
.storybook-cover-placeholder .storybook-placeholder-avatar{
  width:64px; height:64px; border-radius:50%; overflow:hidden; border:3px solid var(--surface);
  box-shadow:0 4px 14px rgba(0,0,0,0.18); margin:0 -10px; background:#fff;
}
.storybook-cover-placeholder .storybook-placeholder-avatar img{ width:100%; height:100%; object-fit:cover; display:block; }
.storybook-badge{
  position:absolute; top:12px; left:12px; font-size:0.72rem; font-weight:800; letter-spacing:0.02em;
  padding:5px 12px; border-radius:999px; background:var(--surface); color:var(--ink); box-shadow:var(--shadow);
}
.storybook-badge.is-free{ background:var(--green); color:#fff; }
.storybook-badge.is-paid{ background:var(--gold); color:var(--ink); }
/* Deliberately minimal (see renderStorybookCard()'s comment in
   build-content.js): cover, title, badge and CTA only -- no description,
   no Ages/page-count pills. Those live on the Storybook Details page
   instead (.storybook-detail-cover-frame below). */
.storybook-card-body{ padding:18px 18px 20px; display:flex; flex-direction:column; gap:8px; flex:1; }
.storybook-card-body h3{ font-size:1.15rem; margin:0; }
.storybook-cta-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:0 18px 18px; }
.storybook-price{ font-weight:800; color:var(--ink); }

/* ---------- Storybook Details page (/stories/<slug>/) ---------- */
/* The big cover on the Details page, sitting in .content-hero-art (see
   styles.css) exactly where a character page puts its circular
   .char-portrait-frame -- same idea, rectangular A4 shape instead of a
   circle, since a book cover isn't a face. */
.storybook-detail-cover-frame{
  width:100%; max-width:320px; aspect-ratio:210/297; border-radius:20px; overflow:hidden; display:block;
  background:#fff; box-shadow:0 0 0 5px var(--surface), 0 0 0 9px var(--line), var(--shadow);
}
.storybook-detail-cover{ width:100%; height:100%; display:block; object-fit:cover; }
.storybook-detail-cover.storybook-cover-placeholder{ display:flex; align-items:center; justify-content:center; }

/* ---------- Storybook Reader shell ---------- */
/* Wide, not just tall: the book below is now sized off the VIEWPORT
   HEIGHT (see .reader-stage), so this shell needs enough max-width that
   a tall stage doesn't get squeezed narrower than its own A4-shaped
   pages want to be on a typical laptop/desktop screen. */
.reader-shell{
  max-width:1440px; margin:0 auto; padding:0 20px 40px; display:flex; flex-direction:column; gap:16px;
}
/* The public reader (/stories/<slug>/read/) has no site header/nav/
   footer around it at all (see renderStorybookReaderPage() in
   build-content.js) -- this modifier turns it into a true fullscreen
   digital storybook: the stage is the entire viewport edge to edge, and
   everything else (the back button, the tiny Draft/Preview badges, the
   controls bar, the corner nav arrows) floats ON TOP of it as small
   glass overlays with only a hair of margin, never claiming a
   permanent row of their own -- see .reader-shell--bare .reader-stage
   below. That's the brand owner's own direction: the book should be
   "essentially the entire screen", not a webpage with a book embedded
   in the middle. Admin preview shares this exact same fullscreen
   treatment now (see renderStorybookReaderPage()'s isPreview branch and
   renderPreviewBodyHtml() in storybook-shared.js) -- the only visual
   difference is the small Draft/Preview badges next to the back button.
   `100dvh` (falling back to plain `100vh` in browsers that don't know
   it -- an unsupported value is ignored, leaving the earlier
   declaration in place) tracks a mobile browser's real visible height
   as its address bar shows/hides, instead of `100vh`'s fixed layout
   viewport, which can leave a gap or a scrollbar-triggering overflow. */
.reader-shell--bare{
  height:100vh; height:100dvh; min-height:100vh; min-height:100dvh;
  max-width:none; width:100%; margin:0; padding:0;
  position:relative; gap:0; overflow:hidden;
}
/* Full-bleed, minimal margin: the book is meant to fill essentially the
   whole viewport, so this is just a hair of breathing room (plus real
   device safe-area insets, for a phone's notch/home-indicator/rounded
   corners) -- NOT space reserved to keep the floating bars clear of the
   artwork the way earlier versions of this reader did. The back button,
   badges, controls bar and corner arrows now deliberately sit on top of
   the book's own edges instead, exactly like a video player's overlay
   controls sit on top of the video -- see reader.js's auto-hide-on-
   inactivity behavior, which is what keeps that from feeling busy. */
.reader-shell--bare .reader-stage{
  /* `inset:0` alone only PINS all four edges to the shell's box -- it
     does not by itself override an explicit `height` from the plain
     (non-bare) .reader-stage rule above (min(78vh,920px)), since that's
     a different property than top/right/bottom/left and a real value
     always beats `auto`'s stretch-to-fill behavior regardless of which
     rule has higher specificity. Explicit 100% here is what actually
     makes the stage -- and so the book inside it, in page-flip's
     "stretch" size mode -- fill the entire fullscreen shell instead of
     quietly capping out at the old boxed reader's much smaller budget. */
  position:absolute; inset:0; height:100%; width:100%; border-radius:0;
  padding:
    max(14px, env(safe-area-inset-top))
    max(14px, env(safe-area-inset-right))
    max(14px, env(safe-area-inset-bottom))
    max(14px, env(safe-area-inset-left));
}
/* The back button + status badges: a small floating cluster in the
   top-left corner, NOT a title bar -- see renderStorybookReaderPage()'s
   comment in build-content.js for why the story title and page-progress
   text still exist in the DOM (as .sr-only) instead of being deleted
   outright. Fades with the rest of the chrome on inactivity (below). */
.reader-shell--bare .reader-topbar{
  position:absolute; top:max(14px, env(safe-area-inset-top)); left:max(14px, env(safe-area-inset-left));
  z-index:20; margin:0; display:flex; align-items:center; gap:8px;
}
/* The floating control dock: positioned in the unused margin beside the
   book (right's actual pixel offset is set by reader.js from the book's
   own measured edge -- see its dock-positioning code) so it can NEVER
   sit on top of the artwork. `right` here is only the fallback for the
   instant before that first measurement runs. See further down for the
   full .reader-dock rule set and its compact-fallback mode. */
.reader-shell--bare .reader-dock{
  position:absolute; top:50%; transform:translateY(-50%); z-index:22;
  right:max(14px, env(safe-area-inset-right));
}
/* ---------- Clean reading mode: fade the floating chrome on inactivity ----------
   Added by reader.js (a simple activity listener -- mousemove, a tap,
   a key press, or focusing any control -- toggles this class on the
   shell), NOT a fixed timer in CSS alone, so a screen-reader/keyboard
   user tabbing through controls or someone mid-interaction never loses
   them. Faded controls stay at real opacity and full size, not
   `visibility:hidden`/`display:none` and not shrunk down -- every one
   of them is always a real, clickable/tappable target, exactly the
   size a WCAG target should be; only their opacity drops, so a single
   tap/click both wakes the chrome AND performs the action, and a
   keyboard user is never punished for not moving a mouse in a while. */
.reader-shell--bare .reader-topbar,
.reader-shell--bare .reader-dock,
.reader-shell--bare .reader-corner-btn{
  transition:opacity .4s ease;
}
.reader-shell--bare.reader-chrome-idle .reader-topbar,
.reader-shell--bare.reader-chrome-idle .reader-dock,
.reader-shell--bare.reader-chrome-idle .reader-corner-btn{
  opacity:0.32;
}
.reader-shell--bare.reader-chrome-idle .reader-topbar:hover,
.reader-shell--bare.reader-chrome-idle .reader-dock:hover,
.reader-shell--bare.reader-chrome-idle .reader-corner-btn:hover,
.reader-shell--bare .reader-topbar:focus-within,
.reader-shell--bare .reader-dock:focus-within,
.reader-shell--bare .reader-corner-btn:focus-visible{
  opacity:1;
}
/* prefers-reduced-motion still gets the fade (it's just an opacity dip,
   not motion), but instantly rather than eased, matching the spirit of
   that setting without giving up the feature entirely. */
@media (prefers-reduced-motion: reduce){
  .reader-shell--bare .reader-topbar,
  .reader-shell--bare .reader-dock,
  .reader-shell--bare .reader-corner-btn{ transition:none; }
}

.reader-topbar{ display:flex; align-items:center; gap:8px; }
/* Small pill badges next to the back button -- Draft appears on any
   unpublished book (public or preview), Preview only in Admin Preview.
   Purely a status cue, never a title/page-count bar. */
.reader-status-badge, .reader-preview-tag{
  flex:none; font-size:0.68rem; font-weight:800; letter-spacing:0.02em; text-transform:uppercase;
  padding:4px 10px; border-radius:999px; white-space:nowrap;
  background:color-mix(in srgb, var(--surface) 88%, transparent); color:var(--ink-soft);
  border:1px solid var(--line); box-shadow:var(--shadow);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
}
.reader-status-badge--draft{ background:color-mix(in srgb, var(--gold) 55%, var(--surface)); color:var(--ink); }
/* The only way back to the story's Details page (public reader) or the
   Storybook Factory (admin preview) once the book has taken over the
   screen -- a small floating glass circle, matching the corner nav
   arrows' "subtle and premium" treatment rather than a solid button. */
.reader-back-btn{
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  width:38px; height:38px; border-radius:50%; color:var(--ink);
  background:color-mix(in srgb, var(--surface) 82%, transparent);
  border:1px solid var(--line); box-shadow:var(--shadow);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  transition:background .15s ease, transform .15s ease;
}
.reader-back-btn:hover{ background:var(--surface); transform:translateX(-2px); }
.reader-back-btn:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; }
.reader-back-btn svg{ display:block; width:17px; height:17px; }
/* Still real, semantic elements for a screen reader (a heading + an
   aria-live page announcement on every page turn) -- just never shown
   on screen. See the sitewide .sr-only utility in styles.css. */
.reader-title{ margin:0; }
.reader-progress-wrap{ display:flex; flex-direction:column; gap:6px; }
.reader-progress{ font-variant-numeric:tabular-nums; }

/* ---------- The book stage ---------- */
/* Sized off the viewport, not a fixed pixel box: the stage claims most
   of the screen's height on any device (up to a cap so it doesn't turn
   into an absurd wall of a book on a 4K monitor). #readerBook (below)
   fills this box completely and page-flip's own "stretch" size mode
   reads ITS real pixel dimensions every frame to size each page --
   see reader.js's PageFlip settings -- so the book is always as big as
   the screen reasonably allows while staying true to its A4 shape,
   phone, tablet or desktop alike. No `perspective` is set here: the
   page-flip library computes its own realistic bend/curl geometry
   (clip-path + rotation math, not a flat CSS 3D rotate), so an ancestor
   perspective isn't part of how the effect works and would just be
   dead weight. */
.reader-stage{
  position:relative;
  background:
    radial-gradient(120% 140% at 50% -10%, color-mix(in srgb, var(--gold) 14%, transparent), transparent 60%),
    linear-gradient(180deg, var(--surface-alt), var(--surface));
  border-radius:32px; box-shadow:var(--shadow); padding:clamp(16px, 3vw, 40px);
  height:min(78vh, 920px); min-height:420px; display:flex; align-items:center; justify-content:center;
  overflow:hidden;
}

/* Subtle and premium: a small glass circle rather than a solid button,
   matching the back button's treatment -- it reads as floating over the
   book rather than a chunky webpage control, and fades with the rest of
   the chrome on inactivity (see .reader-chrome-idle above). Still a
   real 44px touch target either way (WCAG), just visually quiet. */
.reader-corner-btn{
  position:absolute; top:50%; transform:translateY(-50%); width:46px; height:46px; border-radius:50%;
  border:1px solid var(--line); background:color-mix(in srgb, var(--surface) 78%, transparent);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  box-shadow:0 4px 14px rgba(0,0,0,0.16); font-size:1.3rem;
  cursor:pointer; display:flex; align-items:center; justify-content:center; color:var(--ink);
  min-width:44px; min-height:44px; z-index:7;
}
/* Standard left-to-right book: Previous sits on the left, Next on the
   right, matching reader.js's RTL=false. See reader.js if a future book
   ever needs right-to-left reading -- both files mirror together.
   Insets respect a landscape phone's safe area too, alongside the
   existing responsive clamp(). */
.reader-corner-btn--prev{ left:max(clamp(2px, 1.5vw, 14px), env(safe-area-inset-left)); }
.reader-corner-btn--next{ right:max(clamp(2px, 1.5vw, 14px), env(safe-area-inset-right)); }
.reader-corner-btn:disabled{ opacity:0.3; cursor:default; }
.reader-corner-btn:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; }
.reader-corner-btn:not(:disabled):hover{ transform:translateY(-50%) scale(1.06); }

/* The open book itself. page-flip (assets/vendor/page-flip.browser.js)
   adds its own "stf__parent" class to THIS element and takes full
   ownership of its sizing via inline styles set every frame (width,
   min/max-width, min/max-height, display) -- see reader.js. CSS here
   is deliberately limited to properties the library never touches:
   layout keeps it centered and filling the stage, and a single
   `filter:drop-shadow` gives the whole open book one soft ambient
   shadow underneath it, layered independently of the library's own
   per-page turn shadows (which it draws on separate nested elements). */
.reader-book{
  width:100%; height:100%; margin:0 auto;
  filter:drop-shadow(0 22px 34px rgba(0,0,0,0.22));
}

/* Each of these becomes one of page-flip's own "stf__item" pages once
   the engine initializes: position, size and transform are all owned
   by the library's per-frame inline styles, so nothing below ever sets
   those properties on `.reader-leaf` itself (a class rule for them
   would just be silently overridden every frame anyway, since inline
   styles win). Only the inner content -- the art, the page-number
   badge -- is styled here, sized to fill whatever box the library
   gives its outer page element. */
.reader-leaf{ background:var(--surface); }

.reader-leaf-page{
  width:100%; height:100%; background:var(--surface); padding:clamp(8px, 1.4vw, 16px);
  display:flex; align-items:center; justify-content:center; min-width:0;
  border-radius:14px; box-shadow:0 1px 0 rgba(0,0,0,0.04) inset;
}

/* The art fills the page -- no caption text below it, ever (each page's
   story text and character art are already baked into the uploaded
   image itself). A4 portrait ratio (210:297) so every page reads as an
   actual book page even though its surrounding padded box isn't
   perfectly A4-shaped itself, and object-fit:cover so the art fills
   that A4 frame completely edge-to-edge rather than leaving letterboxed
   gaps if a source image isn't exactly A4-shaped. The page number is a
   small overlay badge in the corner of the art, not a separate line
   underneath it. */
.reader-leaf-art{
  height:100%; width:auto; max-width:100%; aspect-ratio:210/297; border-radius:14px; overflow:hidden;
  background:var(--surface-alt); display:flex; align-items:center; justify-content:center; position:relative;
}
.reader-leaf-art img.reader-page-image{ width:100%; height:100%; object-fit:cover; display:block; }
.reader-page-placeholder{
  width:100%; height:100%; display:flex; align-items:center; justify-content:center; color:var(--ink-soft);
  background:
    radial-gradient(60% 60% at 50% 45%, color-mix(in srgb, var(--gold) 18%, transparent), transparent),
    var(--surface-alt);
}
.reader-page-placeholder svg{ opacity:0.55; }

.reader-leaf-number{
  position:absolute; right:10px; bottom:10px; font-size:0.72rem; font-weight:700; color:#fff;
  background:rgba(0,0,0,0.45); padding:2px 9px; border-radius:999px; font-variant-numeric:tabular-nums;
}

/* ---------- Controls: a floating vertical dock beside the book ----------
   Replaces the old full-width pill bar that used to sit along the
   bottom of the book -- the brand owner's direction was explicit that
   NOTHING should ever visually cover a story page, so this now lives in
   the unused margin to the book's side instead. Collapsed to icon-only
   buttons by default; each opens a small popover with its real controls
   on demand (see .reader-popover below) rather than showing everything
   permanently. reader.js measures the book's own rendered box and moves
   this dock (and decides whether it fits beside the book at all) --
   nothing here hardcodes a side or an offset for the dock's own position
   (only its INTERNAL layout, which doesn't depend on where it ends up). */
.reader-dock{
  /* Purely a positioning host now -- reader.js moves THIS element (see
     its layout()/positionCompactDock()), and its actual visible content
     is either .reader-dock-panel alone (margin mode, full stack, exactly
     as before) or .reader-dock-toggle alone (compact mode's collapsed
     single button -- see further down). */
  display:flex; flex-direction:column; align-items:center;
}
/* Two classes (not one) so this beats .reader-dock-btn's own `display:flex`
   despite coming earlier in the file -- equal-specificity rules resolve
   by source order otherwise, and that rule is defined below this one. */
.reader-dock-btn.reader-dock-toggle{ display:none; } /* shown only in .reader-dock--compact, below */
.reader-dock-panel{ display:flex; flex-direction:column; align-items:center; gap:10px; }
.reader-dock-item{ position:relative; display:flex; }
.reader-dock-btn{
  flex:none; width:46px; height:46px; min-width:44px; min-height:44px; border-radius:50%; padding:0;
  border:1px solid var(--line); background:color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  box-shadow:0 4px 14px rgba(0,0,0,0.16);
  display:flex; align-items:center; justify-content:center; color:var(--ink); cursor:pointer;
  transition:background .15s ease, box-shadow .15s ease, transform .12s ease;
}
.reader-dock-btn:not(:disabled):hover{ background:var(--surface); transform:scale(1.05); }
.reader-dock-btn:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; }
.reader-dock-btn .reader-icon{ width:17px; height:17px; }
/* Auto Play is the one dock button that's a direct toggle, not a popover
   trigger -- it gets the same pressed/playing treatment the old pill-bar
   buttons had (see .reader-control-btn[aria-pressed] further down,
   mirrored here since this button now lives outside that markup). */
.reader-dock-btn[aria-pressed="true"]{
  background:linear-gradient(135deg, var(--coral), color-mix(in srgb, var(--coral) 65%, var(--gold)));
  color:#fff; box-shadow:0 6px 16px color-mix(in srgb, var(--coral) 45%, transparent);
}
.reader-dock-btn[aria-pressed="true"] .reader-icon-play{ display:none; }
.reader-dock-btn[aria-pressed="true"] .reader-icon-pause{ display:inline-flex; }
/* A popover's own trigger button gets a quieter "currently open" cue
   instead -- it isn't a toggle with an on/off state of its own. */
.reader-dock-btn[aria-expanded="true"]{ background:var(--surface); box-shadow:0 6px 18px rgba(0,0,0,0.22); }

/* The popover itself: ALWAYS `position:fixed` and placed by reader.js's
   positionPopover() from real, measured numbers (the trigger icon's
   rect, the popover's own natural size, and the viewport) -- never CSS
   anchoring like `left:100%`. That function prefers opening to the
   right of the icon, falls back to the left when the right doesn't
   fit, and finally falls back to a centered/clamped placement when
   neither side has room, then clamps the result so it can never land
   outside the viewport regardless of which branch ran (see that
   function for the exact algorithm). Being `fixed` (not relative to
   the dock item, as before) also means opening/closing one can never
   affect the book's own layout, size, or position -- it paints in its
   own layer, independent of everything else on the page. `top`/`left`
   are set inline per open; nothing here hardcodes a side or offset.
   `max-width` is what keeps it inside the viewport WIDTH-wise even
   before reader.js measures it (that measurement happens after this
   CSS has already applied, so a narrow phone screen never lets the
   popover claim more width than fits in the first place). */
.reader-popover{
  position:fixed; top:0; left:0; /* placeholder -- reader.js overwrites both inline on every open */
  width:220px;
  max-width:calc(100vw - 24px - env(safe-area-inset-left) - env(safe-area-inset-right));
  display:flex; flex-direction:column; gap:10px; padding:14px;
  background:color-mix(in srgb, var(--surface) 94%, transparent);
  border:1px solid var(--line); border-radius:18px;
  box-shadow:var(--shadow), inset 0 1px 0 rgba(255,255,255,0.35);
  backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px);
  z-index:24;
  /* Closed state: invisible and non-interactive, but still a real laid-
     out box (not display:none) the instant before it opens -- reader.js
     needs to measure its real width/height via getBoundingClientRect()
     to position it, which only works while it's actually in the flow.
     See showPopover()/hidePopover() in reader.js for the two-phase
     hidden-attribute + class dance that keeps this both measurable AND
     properly removed from the accessibility tree while closed. */
  opacity:0; transform:translateY(-4px) scale(0.98); pointer-events:none;
  transition:opacity .16s ease, transform .16s ease;
}
@media (prefers-reduced-motion: reduce){
  .reader-popover{ transition:opacity .16s ease; transform:none; }
}
.reader-popover.is-open{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }
.reader-popover[hidden]{ display:none; }
.reader-popover-title{
  margin:0; font-size:0.7rem; font-weight:800; letter-spacing:0.05em; text-transform:uppercase; color:var(--ink-soft);
}
.reader-popover .reader-control-btn,
.reader-popover .reader-toggle-btn{ width:100%; justify-content:center; }
.reader-popover .reader-volume-control{ justify-content:space-between; gap:12px; }

/* ---------- Compact fallback: no usable margin beside the book ----------
   A narrow desktop window, most tablets, and phones all leave little or
   no side margin once the book is sized to fill the screen -- forcing
   the full four-button stack into that non-existent space would mean
   either shrinking the book (explicitly ruled out) or covering real
   artwork with 200+px of stacked circles (the one thing this whole
   redesign exists to avoid -- confirmed the hard way: an earlier version
   of this compact mode kept the full stack and it landed squarely on a
   book's bottom-right illustration on a portrait tablet). reader.js
   measures this at runtime and collapses the dock down to ONE small
   toggle button instead (.reader-dock-toggle, hidden in margin mode --
   see above), positioning it wherever real free space actually exists
   around the book (right/below/above/left, in that order of preference,
   via positionCompactDock() in reader.js). The `bottom`/`right` below are
   only the safety-net position for the instant before that measurement
   runs, and for the rare screen where none of the four sides has room
   for even one 46px circle -- at that point this deliberately matches
   the same corner treatment the nav arrows already use over the book's
   own corner, since there is no remaining alternative that keeps the
   book at its required size. */
.reader-dock.reader-dock--compact{
  position:absolute; top:auto; left:auto; transform:none;
  bottom:max(14px, env(safe-area-inset-bottom));
  right:max(14px, env(safe-area-inset-right));
}
.reader-dock--compact .reader-dock-toggle{ display:flex; }
.reader-dock--compact .reader-dock-panel{ display:none; }
/* Opened on demand only (a viewer tapped the toggle) -- the "small bottom
   control strip" the brand owner's own spec asked for on mobile, laid
   out as a horizontal row so it fits the FULL viewport width available
   below/above the book rather than the narrow side margin a vertical
   stack would need. Still just floating chrome over the safe-area
   margin, exactly like the popovers below already behave in this mode --
   never a permanent fixture, and closes on outside click, Escape, its
   own toggle, or the idle-fade timer (see reader.js). */
.reader-dock--compact.reader-dock--open .reader-dock-panel{
  display:flex; flex-direction:row; align-items:center; gap:8px;
  position:fixed; left:50%; right:auto; bottom:max(14px, env(safe-area-inset-bottom));
  transform:translateX(-50%);
  padding:8px; border-radius:999px;
  background:color-mix(in srgb, var(--surface) 94%, transparent);
  border:1px solid var(--line);
  box-shadow:var(--shadow), inset 0 1px 0 rgba(255,255,255,0.35);
  backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px);
  max-width:calc(100vw - 28px); overflow-x:auto; z-index:23;
}
/* No compact-mode-specific popover rule anymore -- .reader-popover above
   is already `position:fixed` and JS-positioned from real measurements
   in every mode, so the same logic that keeps it clear of the viewport
   edges in margin mode does the same job here too (see positionPopover()
   in reader.js). */

.reader-icon{ display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; flex:none; }
.reader-icon svg{ display:block; width:100%; height:100%; }
.reader-icon-pause{ display:none; }

.reader-control-btn, .reader-toggle-btn{
  display:inline-flex; align-items:center; gap:7px; border:1px solid transparent; background:var(--surface-alt);
  border-radius:999px; padding:9px 16px; font-weight:700; font-size:0.83rem; color:var(--ink); cursor:pointer;
  min-height:40px; transition:background .18s ease, box-shadow .18s ease, transform .12s ease, color .18s ease;
}
.reader-control-btn:not(:disabled):hover, .reader-toggle-btn:not(:disabled):hover{
  transform:translateY(-1px); box-shadow:0 6px 14px rgba(0,0,0,0.14);
}
.reader-control-btn:disabled, .reader-toggle-btn:disabled{ opacity:0.4; cursor:default; transform:none; box-shadow:none; }
.reader-control-btn:focus-visible, .reader-toggle-btn:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; }
.reader-control-btn.is-unavailable{ font-weight:600; }

.reader-control-btn[aria-pressed="true"], .reader-toggle-btn[aria-pressed="true"]{
  background:linear-gradient(135deg, var(--coral), color-mix(in srgb, var(--coral) 65%, var(--gold)));
  color:#fff; box-shadow:0 6px 16px color-mix(in srgb, var(--coral) 45%, transparent);
}
.reader-control-btn[aria-pressed="true"] .reader-icon-play{ display:none; }
.reader-control-btn[aria-pressed="true"] .reader-icon-pause{ display:inline-flex; }

.reader-volume-control{ display:flex; align-items:center; gap:8px; font-size:0.76rem; color:var(--ink-soft); font-weight:600; }
.reader-volume-label{ white-space:nowrap; }

/* Custom range slider -- the OS-default look this replaces was the main
   thing reading as "very basic" (a flat unstyled bar with a plain
   square-ish thumb). --val is kept current by reader.js on load and on
   every "input" event, so the filled portion always matches the value,
   not just a static gradient. */
.reader-volume-control input[type="range"]{
  --val: 50%;
  -webkit-appearance:none; appearance:none; width:88px; height:5px; border-radius:999px;
  background:linear-gradient(to right, var(--coral) var(--val), var(--surface-alt) var(--val));
  cursor:pointer;
}
.reader-volume-control input[type="range"]::-webkit-slider-runnable-track{
  -webkit-appearance:none; height:5px; border-radius:999px; background:transparent;
}
.reader-volume-control input[type="range"]::-webkit-slider-thumb{
  -webkit-appearance:none; width:15px; height:15px; margin-top:-5px; border-radius:50%;
  background:#fff; border:2px solid var(--coral); box-shadow:0 1px 4px rgba(0,0,0,0.35); cursor:pointer;
}
.reader-volume-control input[type="range"]::-moz-range-track{
  height:5px; border-radius:999px; background:var(--surface-alt);
}
.reader-volume-control input[type="range"]::-moz-range-progress{
  height:5px; border-radius:999px; background:var(--coral);
}
.reader-volume-control input[type="range"]::-moz-range-thumb{
  width:15px; height:15px; border-radius:50%; background:#fff; border:2px solid var(--coral);
  box-shadow:0 1px 4px rgba(0,0,0,0.35); cursor:pointer;
}
.reader-volume-control input[type="range"]:focus-visible{ outline:3px solid var(--blue); outline-offset:3px; }

@media (max-width: 760px){
  /* page-flip's own `usePortrait`/`changeOrientation` (a JS setting, not
     a CSS media query -- see reader.js) is what actually switches the
     book to one page at a time on a narrow screen; this block only
     tightens a few purely cosmetic dimensions for a small screen. */
  .reader-stage{ padding:12px 14px 18px; }
  .reader-corner-btn{ width:44px; height:44px; }
  .reader-dock-btn{ width:44px; height:44px; }
}

/* ============================================================
   ADVENTURE SYSTEM — one-time storybook completion celebration
   ============================================================
   Local-only (see adventure.js); this is purely presentational. Mirrors
   games/shared/game-shell.css's .completion-overlay/.completion-card
   almost exactly (same look everywhere a "you finished!" moment
   appears on the site) with two reader-specific additions: a full-bleed
   confetti canvas sized to the reader stage instead of a game canvas,
   and a small close button so a visitor can dismiss it and keep
   reading/re-reading without it ever trapping them.
*/
.reader-confetti-canvas{
  position:absolute; inset:0; z-index:25; pointer-events:none; width:100%; height:100%;
}
.completion-overlay{
  position:absolute; inset:0; z-index:30; display:flex; align-items:center; justify-content:center;
  padding:20px; background:color-mix(in srgb, var(--ink) 55%, transparent);
  opacity:0; pointer-events:none; transition:opacity .25s ease;
}
.completion-overlay.is-visible{ opacity:1; pointer-events:auto; }
.completion-card{
  position:relative; background:var(--surface); border-radius:28px; padding:36px 30px; text-align:center;
  max-width:400px; box-shadow:0 24px 50px -12px rgba(0,0,0,0.5); transform:translateY(12px); transition:transform .25s ease;
}
.completion-overlay.is-visible .completion-card{ transform:translateY(0); }
.completion-close{
  position:absolute; top:10px; right:12px; width:34px; height:34px; border-radius:50%;
  border:none; background:var(--surface-alt); color:var(--ink); font-size:1.3rem; line-height:1;
  cursor:pointer; display:flex; align-items:center; justify-content:center;
}
.completion-close:hover{ background:var(--line); }
.completion-close:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; }
.completion-title{ font-size:1.5rem; font-weight:800; margin:0 0 10px; }
.completion-subtitle{ color:var(--ink-soft); margin:0 0 22px; font-size:1rem; line-height:1.4; }
.completion-actions{ display:flex; gap:10px; justify-content:center; flex-wrap:wrap; }
.completion-passport-link{
  display:block; margin-top:16px; font-size:0.92rem; font-weight:700; color:var(--purple, var(--blue));
  text-decoration:none;
}
.completion-passport-link:hover{ text-decoration:underline; }
.completion-passport-link:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; border-radius:4px; }
@media (prefers-reduced-motion:reduce){
  .completion-overlay, .completion-card{ transition:none; }
}
@media (max-width:480px){
  .completion-card{ padding:28px 20px; }
  .completion-actions{ flex-direction:column; }
  .completion-actions .btn{ width:100%; }
}
