/* Reader mode — folded into ebook.html as a hidden #reader-root
   container. Per-chapter styling lives inside each chapter's shadow root
   (not here) so book CSS can't leak into the reader UI. Body-scoped
   rules sit under `body.reader-mode` so they only kick in once the host
   has flipped that flag — otherwise reader.css can't be loaded alongside
   the dashboard without clobbering its chrome. */

/* --reader-backdrop lives on :root (not .reader-root) because its
   consumer is `body.reader-mode`, and CSS custom properties cascade
   from ancestor to descendant — body cannot read a var defined on
   one of its own descendants. Light value here, dark overrides below. */
:root {
  --reader-backdrop: #ececef;
  /* Mirrored on :root (not only .reader-root) so the body-level annotation
     palette swatches — which sit outside .reader-root — resolve the colours.
     The reader page highlights read these from .reader-root (with dark
     overrides); these :root values are the light defaults for the palette. */
  --ann-hl-y: rgba(255, 214, 64, 0.42);
  --ann-hl-g: rgba(118, 214, 132, 0.42);
  --ann-hl-p: rgba(208, 150, 240, 0.42);
  --ann-hl-b: rgba(120, 190, 250, 0.42);
}
:root[data-theme="dark"] {
  --ann-hl-y: rgba(212, 168, 36, 0.5);
  --ann-hl-g: rgba(72, 168, 92, 0.5);
  --ann-hl-p: rgba(170, 110, 210, 0.5);
  --ann-hl-b: rgba(78, 148, 220, 0.5);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ann-hl-y: rgba(212, 168, 36, 0.5);
    --ann-hl-g: rgba(72, 168, 92, 0.5);
    --ann-hl-p: rgba(170, 110, 210, 0.5);
    --ann-hl-b: rgba(78, 148, 220, 0.5);
  }
}

.reader-root {
  --reader-bg: #f8f5ef;
  --reader-ink: #1c1c1c;
  --reader-muted: #6b6b6b;
  --reader-page-bg: #ffffff;
  --reader-page-ink: #1c1c1c;
  /* Links track the PAGE surface, like the highlights below: the page is
     white by default even in dark mode, where the themed --link would be
     a 2:1 smear. Only the darkened page swaps them. */
  --link: #3730a3;
  --link-visited: #6d28d9;
  --reader-page-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 8px 24px rgba(0, 0, 0, 0.06);
  --reader-popover-bg: rgba(255, 255, 255, 0.97);
  --reader-popover-ink: #333;
  --reader-popover-border: rgba(0, 0, 0, 0.1);
  --reader-popover-hover-bg: rgba(0, 0, 0, 0.04);
  --reader-popover-hover-border: rgba(0, 0, 0, 0.15);
  --reader-popover-sep: rgba(0, 0, 0, 0.12);
  --reader-popover-active-bg: rgba(0, 0, 0, 0.06);
  --reader-chevron-ink: rgba(0, 0, 0, 0.35);
  --reader-chevron-ink-hover: rgba(0, 0, 0, 0.6);
  --reader-page-width: 540px;
  --reader-page-gutter: 58px;
  /* Mirrored from geometry.pageHeight (JS) so the slot-clip matches the
     wrapper's actual outer height, which obeys the height-fit clamp and
     the aspect flex rather than being strictly pageWidth*√2. The √2
     fallback is used only before geometry runs. */
  --reader-page-outer-height: calc(var(--reader-page-width) * 1.4142);
}

/* Dark backdrop sits one step lighter than the dark-mode page (#2c2c2e)
   so the page card stays visually distinct without a glowing hairline.
   Scoped to :root (not .reader-root) so body.reader-mode can read it. */
:root[data-theme="dark"] {
  --reader-backdrop: #3a3a3c;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --reader-backdrop: #3a3a3c;
  }
}

/* Dark theme — the reader's PAGE surface stays paper-white when
   body.paper-keeps-light is set (default); when the user opts OUT of
   paper-keeps-light, the page surface darkens too. The reader CHROME
   (bg-around-pages, chevrons, popovers, bookmark/TOC panels) always
   follows the theme regardless. */
:root[data-theme="dark"] .reader-root {
  --reader-bg: #1c1c1e;
  --reader-ink: #f2f2f7;
  --reader-muted: #8e8e93;
  --reader-popover-bg: rgba(44, 44, 46, 0.97);
  --reader-popover-ink: #f2f2f7;
  --reader-popover-border: rgba(255, 255, 255, 0.12);
  --reader-popover-hover-bg: rgba(255, 255, 255, 0.06);
  --reader-popover-hover-border: rgba(255, 255, 255, 0.2);
  --reader-popover-sep: rgba(255, 255, 255, 0.15);
  --reader-popover-active-bg: rgba(255, 255, 255, 0.08);
  --reader-chevron-ink: rgba(255, 255, 255, 0.4);
  --reader-chevron-ink-hover: rgba(255, 255, 255, 0.7);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .reader-root {
    --reader-bg: #1c1c1e;
    --reader-ink: #f2f2f7;
    --reader-muted: #8e8e93;
    --reader-popover-bg: rgba(44, 44, 46, 0.97);
    --reader-popover-ink: #f2f2f7;
    --reader-popover-border: rgba(255, 255, 255, 0.12);
    --reader-popover-hover-bg: rgba(255, 255, 255, 0.06);
    --reader-popover-hover-border: rgba(255, 255, 255, 0.2);
    --reader-popover-sep: rgba(255, 255, 255, 0.15);
    --reader-popover-active-bg: rgba(255, 255, 255, 0.08);
    --reader-chevron-ink: rgba(255, 255, 255, 0.4);
    --reader-chevron-ink-hover: rgba(255, 255, 255, 0.7);
  }
}

/* Reader page surface darkens with the theme — UNLESS paper-keeps-light
   is set on body, in which case the page stays paper-white. The default
   page-shadow inherits from light mode; the dark page (#2c2c2e) on the
   lighter dark backdrop (#3a3a3c) defines its own edge. */
:root[data-theme="dark"] body:not(.paper-keeps-light) .reader-root {
  --reader-page-bg: #2c2c2e;
  --reader-page-ink: #f2f2f7;
  --link: #a5b4fc;
  --link-visited: #d8b4fe;
  /* Highlights track the PAGE surface, not the global theme: on a paper-
     white page (paper-keeps-light, the default) they keep the light palette;
     only when the page itself darkens do they swap to the higher-saturation
     dark variant. */
  --ann-hl-y: rgba(212, 168, 36, 0.5);
  --ann-hl-g: rgba(72, 168, 92, 0.5);
  --ann-hl-p: rgba(170, 110, 210, 0.5);
  --ann-hl-b: rgba(78, 148, 220, 0.5);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body:not(.paper-keeps-light) .reader-root {
    --reader-page-bg: #2c2c2e;
    --reader-page-ink: #f2f2f7;
    --link: #a5b4fc;
    --link-visited: #d8b4fe;
    --ann-hl-y: rgba(212, 168, 36, 0.5);
    --ann-hl-g: rgba(72, 168, 92, 0.5);
    --ann-hl-p: rgba(170, 110, 210, 0.5);
    --ann-hl-b: rgba(78, 148, 220, 0.5);
  }
}

/* Reader backdrop is theme-conditional — pale grey in light mode, a step
   lighter than the dark page in dark mode (see --reader-backdrop cascade
   above). Page card(s) still follow paper-keeps-light. */
body.reader-mode {
  margin: 0;
  padding: 0;
  background: var(--reader-backdrop, #ececef);
  color: var(--reader-ink, #1c1c1c);
  overflow: hidden;
}

/* Two independent owners collapse the app chrome: the editor's own
   fullscreen toggle sets `body.fullscreen`; the reader always runs
   chrome-collapsed and rides on its own `body.reader-mode`. */
body.fullscreen .toolbar,
body.fullscreen .app-footer,
body.reader-mode .toolbar,
body.reader-mode .app-footer {
  display: none !important;
}
body.reader-mode .reader-root {
  top: 0;
  bottom: 0;
}

body.reader-mode #upload-screen,
body.reader-mode #view-review {
  display: none !important;
}

/* The inset values here only cover the first paint; reader-mode collapses
   them to 0 above.

   The reader-root itself never scrolls — overflow:auto lives one level
   down on .reader-scroller so the chrome layer (a sibling of the
   scroller) stays anchored to the viewport. Browser-native zoom is
   handled by the viewport meta tag; this scroller's overflow:auto lets
   the user pan a zoomed page. */
.reader-root {
  position: fixed;
  top: 48px;
  bottom: 24px;
  left: 0;
  right: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  overflow: hidden;
}

.reader-root .reader-scroller {
  position: absolute;
  inset: 0;
  overflow-x: hidden;
  overflow-y: auto;
  /* Round 111 (#313) — grid + `safe center` gives reliable two-axis
     centring when the page card fits the viewport, AND falls back to
     `start` (top-aligned) when content overflows so native vertical
     scroll reaches the top. Pre-111 used `margin: auto` on the child,
     which doesn't centre vertically inside a block container that has
     `overflow-y: auto` — bug #313/1 (page aligned to top when it
     should be centred). The `safe` keyword is load-bearing: without
     it, grid would centre overflowing content and the user couldn't
     scroll up to see the clipped top. */
  display: grid;
  align-content: safe center;
  justify-content: safe center;
  /* Block the browser's horizontal back-gesture (mac trackpad two-finger
     swipe, edge-swipe on Android) when the user wheels/pans the reader
     horizontally. readerSwipe.js calls preventDefault on those events;
     `overscroll-behavior-x: contain` is belt-and-braces for cases where
     preventDefault arrives too late (non-cancelable wheel ticks, etc). */
  overscroll-behavior-x: contain;
}

/* In reader mode the main toolbar mirrors the editor's open-book toolbar:
   cover, title, author, read-only badge, burger. Editor-only controls
   (progress span, dialect picker) get hidden via these rules. The spread
   toggle and text-zoom controls have moved out of the toolbar entirely
   — iter D will reintroduce them inside a dissolvable popover. */
body.reader-mode #toolbar-progress {
  display: none;
}

/* Burger menu — items that have no meaning while reading (custom dict,
   font picker, settings) get .is-reader-disabled added at enterReader
   time and the class is dropped on exit. The button is also marked
   disabled so click handlers won't fire. */
.burger-menu-item:disabled,
.burger-menu-item.is-reader-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.reader-root[hidden] {
  display: none;
}

.reader-root .reader {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.reader-root .reader-error {
  font-size: 1rem;
  padding: 1rem 1.25rem;
  text-align: center;
}

/* Floating load indicator (#71 follow-up). A card that floats near the
   bottom of the reader, OVER the grey backdrop, while the book streams in
   and the first page paginates+paints. It is absolutely positioned (not the
   flex-centred child it used to be) so the first page can paint underneath
   it. Mobile-first: box-sizing border-box + width capped to the viewport so
   it never overflows its column on a narrow screen. */
.reader-root .reader-loading {
  position: absolute;
  left: 50%;
  bottom: max(24px, env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 5;
  box-sizing: border-box;
  width: 18rem;
  max-width: calc(100% - 32px);
  padding: 0.75rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: var(--reader-page-bg, #fff);
  color: var(--reader-muted, #6b6b6b);
  border-radius: 10px;
  box-shadow: 0 6px 20px -6px rgba(0, 0, 0, 0.35);
  font-size: 0.85rem;
  text-align: center;
}
.reader-root .reader-loading[hidden] { display: none; }
.reader-root .reader-loading-label {
  font-weight: 500;
}
.reader-root .reader-loading-track {
  position: relative;
  width: 100%;
  height: 4px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.12);
  overflow: hidden;
}
.reader-root .reader-loading-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0;
  border-radius: 2px;
  background: var(--reader-accent, #3a6ea5);
  transition: width 180ms ease;
}

.reader-root .reader-error {
  box-sizing: border-box;
  max-width: min(32rem, calc(100% - 32px));
  background: var(--reader-page-bg);
  color: var(--reader-muted);
  border-radius: 10px;
  box-shadow: 0 6px 20px -6px rgba(0, 0, 0, 0.35);
}
.reader-root .reader-error-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* Round 100 / 111 — CSS-literal geometry. `.reader-pages` is sized
   inline from JS to exactly the scaled page-rect (pageWidth × ppS ×
   scale wide, pageHeight × scale tall). Centring lives on the parent
   `.reader-scroller` (grid + `safe center`), so this element just
   sits at its natural size. `flex-shrink: 0` / `min-height: 0`
   defensive: grid items default to `auto` min-size which would let a
   tall page card collapse to fit; explicit `0` keeps the literal
   height. */
.reader-root .reader-pages {
  position: relative;
  /* JS sets `width`/`height` inline. Defaults match the natural slot-clip
     so the first paint before mountPinchZoom runs isn't visually wrong. */
  width: var(--reader-page-width);
  height: var(--reader-page-outer-height);
  min-width: 0;
  min-height: 0;
  /* Outer clip — same role as before: nothing escapes the pages region
     even on rapid flips. The inner .reader-slot-clip is the tight clip
     that defines the Apple Books page rectangle. iter BS2. */
  overflow: hidden;
}
.reader-root.is-spread .reader-pages {
  width: calc(2 * var(--reader-page-width));
}

/* iter BS2 — clip element sized to the page rectangle (single mode) or
   spread rectangle (two-page mode). Wrappers are positioned ABSOLUTELY
   inside this clip and translate within its coordinate space, so a
   `translateX(-115%)` lands the wrapper one wrapper-width-plus-margin
   to the left — off-screen with the right-edge drop-shadow safely
   beyond the clip's left edge (iter BS2-fix2). The clip is what gives
   the Apple-Books-style page-turn its hard edge.

   Spread-mode override (iter BS2-fix): the clip must wrap BOTH pages
   plus the inner gap so the wrapper's natural outer-width fits and
   `translateX(-115%)` slides one full spread off-screen. Trigger class
   `is-spread` is set by reader.js whenever geometry reports twoPage.
   The inner gap (one outer marginX, half-margin on each page's inside
   edge — see paginateChapter) is ALREADY absorbed into the wrapper's
   2*pageWidth outer-box, so the clip width is exactly 2*pageWidth.
   `--reader-page-gutter` is retained as a semantic hint and resolves
   to 0 in single mode for the same reason — keeping the formula
   uniform makes the relationship to wrapper geometry explicit. */
.reader-root .reader-slot-clip {
  position: relative;
  width: var(--reader-page-width);
  /* NOT `calc(var(--reader-page-width) * 1.4142)`: that is the √2 ideal,
     but pageGeometry() may stretch the aspect taller to fit a spread.
     Mirrored from JS via --reader-page-outer-height so the clip matches
     the wrapper's real outer rectangle — otherwise the bottom of the page
     was cropped and the surviving top margin read as an oversized one. */
  height: var(--reader-page-outer-height);
  overflow: hidden;
  /* Round 100 — pinch-zoom writes `transform: scale(<s>)` here. Origin
     at 0,0 so the scaled box sits flush against the parent's top-left;
     `.reader-pages` is sized literally to the scaled rect, so the visible
     result fills it exactly. */
  transform-origin: 0 0;
}
.reader-root.is-spread .reader-slot-clip {
  width: calc(2 * var(--reader-page-width));
}

.reader-root .reader-chapter {
  /* #78-parity Phase 4: chapter wrappers are the hidden PAGINATION
     SUBSTRATE. They stay in the DOM, sized like a page, so their
     column-fill layout can measure and the spread-buffer cards can
     clone them — but they are never shown (visibility:hidden is the
     substrate hider; the stack states live on .reader-spread-buffer). */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: var(--reader-page-bg);
  color: var(--reader-page-ink);
  box-sizing: border-box;
  padding: var(--reader-page-margin-y, 48px) var(--reader-page-margin-x, 36px);
  overflow: hidden;
  visibility: hidden;
  z-index: 0;
}

/* #78-parity Phase 4 — spread-buffer display layer, the reader's
   visible page stack. Two buffer cards (front/back) render the visible
   spread from clones of the (hidden) chapter wrappers; panes and card
   geometry are inline-styled by readerSpreadBuffer.js from live page
   geometry. A card with no stack state is invisible.

   Page-stack model: the lower-numbered page is always on top with the
   right-edge depth shadow, and is the page that moves in BOTH
   directions. Forward turn: the OLD page sits on top at translateX(0)
   with the NEW page underneath; the OLD page slides off to the left,
   uncovering the NEW page. Backward turn: the NEW page (lower-numbered,
   previous) parks off-screen left and slides in to translateX(0) over
   the static OLD page underneath.
   States:
   - .is-bottom                     — translateX(0), z-index 1. Static
                                       under-layer.
   - .is-top-offscreen-left         — translateX(-115%), z-index 2,
                                       right-edge shadow. Parked for the
                                       backward turn's slide-in.
   - .is-top-onscreen-shadow-right  — translateX(0), z-index 2, right-edge
                                       shadow. The OLD page during a
                                       forward queue and the slide-out
                                       start position.
   The slide is driven by an inline transform that animates to the
   destination (translateX(0) for backward, translateX(-115%) for
   forward). .is-sliding only enables the transition. -115% (not -100%)
   keeps the leading-edge drop-shadow clear of the slot-clip's edge
   while parked (iter BS2-fix2). */
.reader-root .reader-spread-buffers {
  position: absolute;
  inset: 0;
}
.reader-root .reader-spread-buffer {
  background: var(--reader-page-bg);
  color: var(--reader-page-ink);
  box-shadow: var(--reader-page-shadow);
  visibility: hidden;
  z-index: 0;
}
.reader-root .reader-spread-buffer.is-bottom,
.reader-root .reader-spread-buffer.is-top-offscreen-left,
.reader-root .reader-spread-buffer.is-top-onscreen-shadow-right {
  visibility: visible;
}
.reader-root .reader-spread-buffer.is-bottom {
  transform: translateX(0);
  z-index: 1;
  box-shadow: var(--reader-page-shadow);
}
.reader-root .reader-spread-buffer.is-top-offscreen-left {
  transform: translateX(-115%);
  z-index: 2;
  box-shadow:
    var(--reader-page-shadow),
    6px 0 14px -4px rgba(0, 0, 0, 0.16);
}
.reader-root .reader-spread-buffer.is-top-onscreen-shadow-right {
  transform: translateX(0);
  z-index: 2;
  box-shadow:
    var(--reader-page-shadow),
    6px 0 14px -4px rgba(0, 0, 0, 0.16);
}
.reader-root .reader-spread-buffer.is-sliding {
  transition: transform 220ms ease;
}
/* Forward uses the time-reverse of `ease` (each control point reflected
   through 0.5,0.5) so forward IS backward played in reverse. */
.reader-root .reader-spread-buffer.is-sliding.is-sliding-forward {
  transition-timing-function: cubic-bezier(0.75, 0, 0.75, 0.9);
}
@media (prefers-reduced-motion: reduce) {
  .reader-root .reader-spread-buffer,
  .reader-root .reader-spread-buffer.is-sliding { transition: none; }
}

/* Buffer-level page chrome — the light-DOM twins of the shadow chrome
   readerRender injects per wrapper (which stays hidden inside clones).
   Rules mirror renderChapter's baseStyle so the two render identically
   during the transition. */
.reader-root .reader-spread-buffer .reader-page-header {
  position: absolute;
  top: 12px;
  left: 0;
  right: 0;
  /* Names the page face rather than inheriting: .reader-root stacks the shell
     on system-ui, which has no Shavian glyphs, and this head prints the book's
     Shavian author/title. The folio below inherits — it renders digits. */
  font-family: var(--font-shavian);
  font-size: calc(0.9em * var(--reader-text-scale, 1));
  line-height: 1.3;
  color: #6b6b6b;
  padding: 0 var(--reader-page-margin-x, 36px);
  pointer-events: none;
  display: grid;
  grid-template-columns: 1fr;
  justify-items: center;
}
.reader-root .reader-spread-buffer .reader-page-header[hidden] { display: none; }
.reader-root .reader-spread-buffer .reader-page-header[data-mode="spread"] {
  grid-template-columns: 1fr 1fr;
}
.reader-root .reader-spread-buffer .reader-page-header[data-mode="single"] .header-right {
  display: none;
}
.reader-root .reader-spread-buffer .reader-page-header .header-left,
.reader-root .reader-spread-buffer .reader-page-header .header-right {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}
.reader-root .reader-spread-buffer .reader-page-header .header-left {
  font-style: italic;
}
.reader-root .reader-spread-buffer .reader-page-folio {
  position: absolute;
  bottom: 16px;
  left: 0;
  right: 0;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: calc(0.9em * var(--reader-text-scale, 1));
  line-height: 1.3;
  color: #6b6b6b;
  padding: 0 var(--reader-page-margin-x, 36px);
  pointer-events: none;
  display: grid;
  grid-template-columns: 1fr;
  justify-items: center;
}
.reader-root .reader-spread-buffer .reader-page-folio[data-mode="spread"] {
  grid-template-columns: 1fr 1fr;
}
.reader-root .reader-spread-buffer .reader-page-folio[data-mode="single"] .folio-right {
  display: none;
}
.reader-root .reader-spread-buffer .reader-page-folio .folio-left,
.reader-root .reader-spread-buffer .reader-page-folio .folio-right {
  display: inline-block;
  min-width: 1em;
  text-align: center;
}
.reader-root .reader-spread-buffer .reader-page-ribbon {
  position: absolute;
  top: 0;
  right: var(--ribbon-right, 18px);
  width: 18px;
  height: 30px;
  background: #c0392b;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 78%, 0 100%);
  cursor: pointer;
  border: 0;
  padding: 0;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 120ms ease;
}
.reader-root .reader-spread-buffer .reader-page-ribbon[hidden] { display: none; }
.reader-root .reader-spread-buffer .reader-page-ribbon:hover {
  transform: translateY(2px);
}

/* Skeleton pane (#154): pulsing dot while a pane's chapter is still
   paginating; the shadow-scoped twin lives in renderChapter's baseStyle. */
.reader-root .reader-spread-pane .parallel-page-placeholder-pulse {
  position: absolute;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #9a9a9a;
  opacity: 0.5;
  animation: reader-buffer-placeholder-pulse 1.4s ease-in-out infinite;
}
@keyframes reader-buffer-placeholder-pulse {
  0%, 100% { opacity: 0.25; transform: translateX(-50%) scale(0.7); }
  50% { opacity: 0.75; transform: translateX(-50%) scale(1); }
}

/* Page-internal header lives INSIDE each chapter's shadow root (see
   readerRender.renderChapter — it injects a `.reader-page-header` div
   styled via a <style> sibling). Shadow encapsulation prevents reader.css
   from reaching in, so the styling is shipped with the element itself. */

/* Tap-zone overlays sit ON TOP of the page. Pointer events pass through
   to the shadow root for click-to-Latin (commit 8) — these elements get
   pointer-events only when a click hits a NON-link, NON-event-span area.
   For v1 we keep them invisible and use a single delegated click on
   the reader-pages container instead. */
.reader-root .reader-nav-zones {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Dissolvable panel chrome — chevrons + settings popover. The whole
   layer fades in on pointer/keyboard activity and fades back out after
   3 s idle (timer managed by reader.js :: mountChromeLayer). The layer
   has pointer-events:none so it doesn't intercept clicks while invisible;
   the per-child opt-in restores pointer events for the actual controls.

   While hidden (no .is-chrome-visible on .reader-root) we ALSO force
   pointer-events:none on every descendant so the invisible chrome can't
   eat clicks that look like they're hitting blank page (round 132 /
   2026-05-27 report). Autoshow-on-hover doesn't help mobile, so the
   fix is: hidden means hidden. The same .is-chrome-visible toggle the
   JS already manages now gates input as well as opacity — no parallel
   handler, no per-button book-keeping. */
.reader-root .reader-chrome-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
  z-index: 2;
}
.reader-root.is-chrome-visible .reader-chrome-layer { opacity: 1; }
.reader-root:not(.is-chrome-visible) .reader-chrome-layer * { pointer-events: none; }

/* Large stretched chevrons on the left/right of the viewport. Click =
   page turn. The invisible tap zones in bindGlobalNav stay live whether
   or not chrome is showing, so users who don't move their mouse can
   still page through by clicking the page-edge areas. */
.reader-root .reader-chevron {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 48px;
  color: var(--reader-chevron-ink);
  background: transparent;
  border: none;
  cursor: pointer;
  pointer-events: auto;
  user-select: none;
  padding: 0;
  font-family: system-ui, -apple-system, sans-serif;
}
.reader-root .reader-chevron.prev { left: 8px; }
.reader-root .reader-chevron.next { right: 8px; }
.reader-root .reader-chevron:hover { color: var(--reader-chevron-ink-hover); }
.reader-root .reader-chevron:disabled { opacity: 0.2; cursor: default; }

/* Settings popover in the top-right corner. Spread toggle, zoom group,
   edit-chapter button. */
.reader-root .reader-popover {
  position: absolute;
  top: 16px;
  right: 16px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  pointer-events: auto;
  font: 13px/1.3 system-ui, -apple-system, sans-serif;
  color: var(--reader-popover-ink);
}
.reader-root .reader-popover-btn {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 4px 8px;
  cursor: pointer;
  font: inherit;
  color: var(--reader-popover-ink);
}
.reader-root .reader-popover-btn:hover {
  border-color: var(--reader-popover-hover-border);
  background: var(--reader-popover-hover-bg);
}
.reader-root .reader-popover-btn:disabled { opacity: 0.35; cursor: default; }
.reader-root .reader-popover-btn:disabled:hover {
  border-color: transparent;
  background: transparent;
}
.reader-root .reader-popover-sep {
  width: 1px;
  height: 18px;
  background: var(--reader-popover-sep);
  margin: 0 4px;
  display: inline-block;
}
.reader-root .reader-popover-readout {
  min-width: 3.5em;
  text-align: center;
}

/* Close-X, top-left of the chrome layer. The reader hides the app
   toolbar, so this is the only leave-the-book surface besides Esc.
   Fades with the rest of the chrome layer via the parent's opacity
   transition (controlled by .is-chrome-visible on .reader-root). */
.reader-root .reader-close {
  position: absolute;
  top: 16px;
  left: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 50%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  color: var(--reader-popover-ink);
  pointer-events: auto;
  cursor: pointer;
  padding: 0;
}
.reader-root .reader-close:hover {
  border-color: var(--reader-popover-hover-border);
  background: var(--reader-popover-hover-bg);
}
.reader-root .reader-close .shave-icon {
  width: 18px;
  height: 18px;
}

/* Two-page mode is unusable on phone-width portrait; hide the toggle to
   avoid a no-op control. The breakpoint matches the spread-fits check in
   pageGeometry() — at viewports narrower than ~720px in portrait, the
   spread layout never satisfies the safe-margin so the button does
   nothing. */
@media (max-width: 720px) and (orientation: portrait) {
  .reader-root [data-role="reader-spread-toggle"] {
    display: none;
  }
}

/* Floating Edit/Download toolbar (iter DV / #289). Visible at ALL
   viewports — Round 91 (#270) originally gated this on
   (max-width: 720px) and kept Edit/Download in the popover for
   desktop; #289 unifies the surface so the line-art icons read at
   every width. Shares click handlers with the prior popover buttons
   (see reader.js). */
.reader-root .reader-floating-toolbar {
  position: absolute;
  /* #119 chrome symmetry swap: floating toolbar moved from bottom-LEFT
     to bottom-RIGHT to match the editor's floating toolbar position.
     The page-info / progress chip moved the opposite way in the same
     change so the two corners' roles are consistent across views. */
  bottom: 16px;
  right: 16px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  pointer-events: auto;
  color: var(--reader-popover-ink);
}
/* Icons inherit color from the button text (currentColor stroke in
   icons.js) so they pick up the light/dark theme automatically — same
   pattern as the spread / bookmark / TOC icons above. */
.reader-root [data-role="reader-floating-edit"] .shave-icon,
.reader-root [data-role="reader-floating-download"] .shave-icon,
.reader-root [data-role="reader-floating-settings"] .shave-icon {
  width: 18px;
  height: 18px;
  display: block;
}
/* Popover Edit + Download are gone from the popover under #289 — the
   floating toolbar owns them at every viewport. Hide the unused DOM
   holders so they don't paint as ghost buttons. The DOM stays so the
   shared click-handler binding in reader.js keeps working without a
   conditional. */
.reader-root .reader-popover [data-role="reader-edit-chapter"],
.reader-root .reader-popover [data-role="reader-download"] {
  display: none;
}
/* sep3 sat between the bookmarks group and Edit/Download in the
   popover; with both gone the last `.reader-popover-sep` is now a
   dangling divider at the end of the row. Hide it. */
.reader-root .reader-popover .reader-popover-sep:last-of-type {
  display: none;
}

/* Bookmarks (iter BB). The toggle + list chevron sit inline in the
   popover; the bookmarks list panel pops down beneath the popover when
   the list button is clicked. */
/* Spread toggle uses [data-role] (no role-class added by makePopoverButton),
   matching the TOC selector below; bookmark buttons use class selectors
   because the JS adds `reader-bookmark-toggle`/`reader-bookmark-list` to
   the button's className explicitly. Don't change one without the other. */
.reader-root .reader-bookmark-toggle .shave-icon,
.reader-root .reader-bookmark-list .shave-icon,
.reader-root [data-role="reader-spread-toggle"] .shave-icon,
.reader-root [data-role="reader-parallel-toggle"] .shave-icon {
  width: 14px;
  height: 14px;
  display: block;
}
.reader-root .reader-bookmark-list {
  padding: 4px 4px;
  min-width: 0;
}
.reader-root .reader-bookmarks-panel {
  position: absolute;
  top: 56px;
  right: 16px;
  min-width: 240px;
  max-width: 360px;
  max-height: 60vh;
  overflow: auto;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  padding: 6px;
  pointer-events: auto;
  font: 13px/1.3 system-ui, -apple-system, sans-serif;
  color: var(--reader-popover-ink);
}
.reader-root .reader-bookmarks-panel[hidden] {
  display: none;
}
.reader-root .reader-bookmarks-section {
  padding: 8px 8px 2px;
  color: var(--reader-muted);
  font-size: 12px;
  font-weight: 600;
}
.reader-root .reader-bookmarks-section:first-child {
  padding-top: 2px;
}
.reader-root .reader-bookmarks-empty {
  padding: 8px 12px;
  color: var(--reader-muted);
  font-style: italic;
}

/* TOC dropdown (iter BT). Mirrors the bookmarks-panel layout but
   anchored to the LEFT of the popover so the chapter list opens
   beneath its toggle button. */
.reader-root [data-role="reader-toc"] .shave-icon {
  width: 14px;
  height: 14px;
  display: block;
}
.reader-root .reader-toc-panel {
  position: absolute;
  top: 56px;
  right: 16px;
  min-width: 260px;
  max-width: 420px;
  max-height: 60vh;
  overflow: auto;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  padding: 6px;
  pointer-events: auto;
  font: 13px/1.3 system-ui, -apple-system, sans-serif;
  color: var(--reader-popover-ink);
}
.reader-root .reader-toc-panel[hidden] {
  display: none;
}
/* Find in the book (readerFind.js): a bar under the popover, as the
   editor's find bar (ebook.css .editor-find). */
.reader-root .reader-find {
  position: absolute;
  top: 56px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 2px;
  box-sizing: border-box;
  width: min(30em, calc(100% - 32px));
  padding: 2px 4px 2px 8px;
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  pointer-events: auto;
  font: 13px/1.3 system-ui, -apple-system, sans-serif;
  color: var(--reader-popover-ink);
}
.reader-root .reader-find[hidden] {
  display: none;
}
/* 16px keeps iOS Safari from zooming the page when the field takes focus. */
.reader-root .reader-find-input {
  flex: 1 1 12em;
  min-width: 0;
  font-size: 16px;
  padding: 4px 6px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: inherit;
}
.reader-root .reader-find-input:focus {
  outline: none;
  border-color: var(--reader-popover-border);
}
.reader-root .reader-find-input.is-invalid {
  border-color: #c0392b;
}
.reader-root .reader-find-count {
  flex: 0 0 auto;
  padding: 0 4px;
  font-size: 12px;
  opacity: 0.7;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.reader-root .reader-find .reader-popover-btn {
  min-width: 32px;
  min-height: 32px;
}
.reader-root .reader-find .shave-icon,
.reader-root [data-role="reader-floating-find"] .shave-icon {
  width: 16px;
  height: 16px;
  display: block;
}
.reader-root .reader-find .reader-popover-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
@media (pointer: coarse) {
  .reader-root .reader-find .reader-popover-btn {
    min-width: 44px;
    min-height: 44px;
  }
}
.reader-root .reader-toc-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 6px 8px;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.reader-root .reader-toc-row:hover {
  background: var(--reader-popover-hover-bg);
  border-color: var(--reader-popover-border);
}
.reader-root .reader-toc-row.is-current {
  background: var(--reader-popover-active-bg);
  font-weight: 600;
}
.reader-root .reader-toc-row[data-depth="1"] { padding-left: 24px; }
.reader-root .reader-toc-row[data-depth="2"] { padding-left: 40px; }
.reader-root .reader-toc-row[data-depth="3"] { padding-left: 56px; }
.reader-root .reader-toc-row:disabled {
  cursor: default;
  color: var(--reader-muted);
}
.reader-root .reader-toc-row:disabled:hover {
  background: transparent;
  border-color: transparent;
}
/* A mark on text since edited has no place to go to; its Remove button
   follows its (disabled) row. */
.reader-root .reader-mark-remove {
  display: block;
  margin: 0 0 4px auto;
  min-height: 44px;
  padding: 0 12px;
  background: transparent;
  border: 1px solid var(--reader-popover-border);
  border-radius: 4px;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.reader-root .reader-mark-remove:hover {
  background: var(--reader-popover-hover-bg);
}
.reader-root .reader-toc-attention {
  padding: 8px;
  margin-top: 4px;
  border-top: 1px solid var(--reader-popover-border);
  color: var(--reader-muted);
  font-size: 12px;
}
.reader-root .reader-toc-row-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.reader-root .reader-toc-row-page {
  flex: 0 0 auto;
  color: var(--reader-muted);
  font-variant-numeric: tabular-nums;
}
.reader-root .reader-toc-empty {
  padding: 8px 12px;
  color: var(--reader-muted);
  font-style: italic;
}

/* Floating progress chip (iter BT3). Sits in the bottom-right of the
   reader viewport as a standalone overlay — pure progress meta, separate
   from the popover toolbar. Dissolves alongside the rest of the chrome
   via the layer's opacity transition. Themed against --reader-popover-*
   so it reads on both light/dark backdrops; opacity 0.85 keeps it
   present-but-recessive. Mobile-respecting: max-width caps the line and
   text-overflow:ellipsis clips on cramped viewports. */
.reader-root .reader-progress-chip {
  position: absolute;
  /* #119 chrome symmetry swap: progress chip moved from bottom-RIGHT
     to bottom-LEFT to match the editor's chrome (no editor-side
     equivalent on the right; bottom-right is the floating-toolbar
     corner). See .reader-floating-toolbar above for the paired swap. */
  bottom: 16px;
  left: 16px;
  pointer-events: auto;
  font: 11px/1.4 system-ui, -apple-system, sans-serif;
  color: var(--reader-popover-ink);
  background: var(--reader-popover-bg);
  border: 1px solid var(--reader-popover-border);
  border-radius: 12px;
  padding: 4px 10px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
  opacity: 0.85;
  max-width: calc(100% - 32px);
  /* Two-row stack (iter DU / #290): the "n pages left in chapter"
     tail wraps to its own row so the chip's first row doesn't
     collide with the bottom-right floating toolbar at narrow widths.
     Left-aligned to keep the visible left edge clean as the rows
     swap length between page-turns. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  box-sizing: border-box;
}
.reader-root .reader-progress-headline,
.reader-root .reader-progress-tail {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
/* Empty tail collapses so the chip shrinks to a single row when the
   controller has no pages-left phrase to report (e.g. last page of
   last chapter without the "last page in chapter" copy, or pre-init). */
.reader-root .reader-progress-tail:empty {
  display: none;
}
/* Chip itself collapses only when BOTH rows are empty. The flex
   container can't use :empty (children are always present), so gate
   on a marker class written by refreshProgress when both rows go
   blank — kept simple by hiding via an `:has` rule on the empty
   children. Modern browsers (Safari/Chrome/Firefox latest) all
   support :has; older browsers see a degenerate empty pill, which is
   visually unobtrusive. */
.reader-root .reader-progress-chip:has(.reader-progress-headline:empty):has(.reader-progress-tail:empty) {
  display: none;
}

/* Width-only breakpoint (landscape phones included) so the chrome
   stops drifting toward the page centre on tight viewports. */
@media (max-width: 720px) {
  .reader-root .reader-popover {
    top: 4px;
    right: 4px;
    padding: 4px 6px;
    gap: 2px;
  }
  .reader-root .reader-close {
    top: 4px;
    left: 4px;
  }
  .reader-root .reader-floating-toolbar {
    /* #119: matches the bottom-right swap above. */
    bottom: 4px;
    right: 4px;
    padding: 4px 6px;
    gap: 2px;
  }
  .reader-root .reader-progress-chip {
    /* #119: matches the bottom-left swap above. */
    bottom: 4px;
    left: 4px;
  }
  .reader-root .reader-bookmarks-panel,
  .reader-root .reader-toc-panel {
    top: 44px;
    right: 4px;
  }
}
/* A phone's rounded corners clip the corner chrome this low; 10px up clears
   them, as the editor's toolbar (ebook.css). */
@media (max-width: 720px) and (pointer: coarse) {
  .reader-root .reader-floating-toolbar,
  .reader-root .reader-progress-chip {
    bottom: calc(4px + 10px);
  }
}

/* Hover-for-Latin tooltip. Lives at body-level (not inside the chapter
   shadow root) because a transformed ancestor (.reader-spread-buffer
   carries translateX(...)) would re-anchor position:fixed away from the
   viewport. Intentionally dark on both light and dark themes — it's a
   contrast pill, not part of the page surface. Pointer-events are
   enabled so moving the mouse onto the tooltip cancels its hide timer
   (readerTooltip.js bindHoverLookup). */
.reader-tooltip {
  position: fixed;
  z-index: 50;
  background: #222;
  color: #fff;
  padding: 4px 10px;
  border-radius: 6px;
  font: 14px/1.3 system-ui, -apple-system, sans-serif;
  max-width: min(80vw, 360px);
  transform: translate(-50%, -100%);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.reader-tooltip[data-placement="below"] {
  transform: translate(-50%, 0);
}
.reader-tooltip[hidden] {
  display: none;
}

/* Annotation palette — body-level fixed popover (same containing-block
   rationale as .reader-tooltip: a transformed .reader-spread-buffer ancestor
   would break position:fixed). Styled as the .cw-picker toolbar family: a tidy small
   container of .cw-pick-btn icon buttons, NOT a heavy drop-shadow popover. */
.ann-palette {
  position: fixed;
  z-index: 51;
  display: flex;
  gap: 4px;
  align-items: center;
  padding: 4px;
  border-radius: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
  transform: translate(-50%, -100%);
}
.ann-palette[data-placement="below"] {
  transform: translate(-50%, 0);
}
.ann-palette[hidden] {
  display: none;
}
/* Colour swatches are standard 28x28 icon-button frames with a small colour
   dot inside — matching .cw-pick-btn, never a raw colour block. Selected =
   a clear accent ring, consistent with the rest of the app's controls. */
.ann-swatch-dot {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
}
.ann-swatch-y .ann-swatch-dot { background: var(--ann-hl-y); }
.ann-swatch-g .ann-swatch-dot { background: var(--ann-hl-g); }
.ann-swatch-p .ann-swatch-dot { background: var(--ann-hl-p); }
.ann-swatch-b .ann-swatch-dot { background: var(--ann-hl-b); }
.ann-swatch.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}

/* Highlight wrapper — light DOM (editor panes). The reader's chapters live
   in a shadow root, which can't see this sheet, so readerRender.js injects an
   identical rule into each chapter's shadow baseStyle; both consume the
   --ann-hl-* palette vars defined on :root above. */
.ann-hl { border-radius: 2px; cursor: pointer; }

/* Comment margin pin (annotations §D.4, annotationMarginBox.js), on the
   reader's display CARD, in the outer margin of the page its comment is on
   (data-side), and in the left gutter of each editor pane. `top` is set
   inline from the highlight's rendered rect; colour-keyed border matches the
   highlight. It paints as the page it sits on: the reader's page, else the
   editor's pane. The ShaveIcons 'comment' SVG carries a viewBox but no
   intrinsic size — pin it to 16px. */
.ann-margin-box {
  position: absolute;
  right: 6px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  background: var(--reader-page-bg, var(--bg-pane));
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  font: 15px/1 system-ui, -apple-system, sans-serif;
  color: var(--reader-page-ink, var(--text));
  cursor: pointer;
  padding: 0;
}
.ann-margin-box[data-side="left"] { left: 6px; right: auto; }
.ann-margin-box[data-ann-color="y"] { border-color: var(--ann-hl-y); }
.ann-margin-box[data-ann-color="g"] { border-color: var(--ann-hl-g); }
.ann-margin-box[data-ann-color="p"] { border-color: var(--ann-hl-p); }
.ann-margin-box[data-ann-color="b"] { border-color: var(--ann-hl-b); }
.ann-margin-box .shave-icon {
  width: 16px;
  height: 16px;
  display: block;
}
/* A shared comment's pin: two bubbles, in the link indigo the page's theme
   sets, on a wash of it. */
.ann-margin-box[data-ann-shared] {
  color: var(--link);
  background: linear-gradient(color-mix(in srgb, var(--link) 14%, transparent),
              color-mix(in srgb, var(--link) 14%, transparent)),
              var(--reader-page-bg, var(--bg-pane));
}
.ann-hl[data-ann-color="y"] { background: var(--ann-hl-y); }
.ann-hl[data-ann-color="g"] { background: var(--ann-hl-g); }
.ann-hl[data-ann-color="p"] { background: var(--ann-hl-p); }
.ann-hl[data-ann-color="b"] { background: var(--ann-hl-b); }

/* Focused highlight: the annotation whose note editor is currently open.
   Its comment popup no longer repeats the anchored text, so the in-document
   highlight is the sole anchor cue. The editor already marks its active/
   current selection with the .hl-current accent ring (ebook.css); the reader
   reuses the SAME visual on its .ann-hl spans so "currently editing this one"
   reads identically in both surfaces. Same accent-ring recipe, theme-aware. */
.ann-hl.hl-current {
  box-shadow: 0 0 0 2px var(--accent);
}
:root[data-theme="dark"] .ann-hl.hl-current {
  box-shadow: 0 0 0 2px #818cf8;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ann-hl.hl-current {
    box-shadow: 0 0 0 2px #818cf8;
  }
}

/* Depressed-toggle state for popover buttons (parallel + spread toggles).
   Darker fill, inset shadow, and contrasted border read clearly as
   "this control is currently engaged" — distinct from a plain hover. */
.reader-root .reader-popover-btn.is-active {
  background: var(--reader-popover-active-bg, rgba(0, 0, 0, 0.18));
  border-color: rgba(0, 0, 0, 0.35);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
}
:root[data-theme="dark"] .reader-root .reader-popover-btn.is-active {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.4);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .reader-root .reader-popover-btn.is-active {
    background: rgba(255, 255, 255, 0.22);
    border-color: rgba(255, 255, 255, 0.4);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
  }
}

