/* ViZZ reader — Mirador 4 chrome.
   Values measured in design_handoff_vizz/prototype/ViZZ-v5-offline.html and
   design_handoff_vizz/03-reader-mirador4.md section 1. Loaded only on the
   reader route, after vizz-tokens.css.

   The prototype computes two things in JS that are variables here:
     ink    = #fbfcf7 (dark ground) | #23261f (light ground)
     mix(p) = ink at p% alpha, written rgba(var(--ink-rgb), .p) rather than
              color-mix() — see the theme block below for why
   The page header stays light in both themes; the rail, stage, bars and
   metadata panel all follow the ground. */

/* ── theme ────────────────────────────────────────────────────────── */

.vizz-reader {
  /* Light is the first-visit ground. The prototype defaulted to dark, but a
     visitor's own choice is remembered from then on — see the inline restore
     in citac.html. */
  --ink: var(--reader-ink-light);
  /* The same ink as a channel triplet, for the soft values below. See the note
     at the top of vizz-shell.css: color-mix() is dropped whole where it is not
     supported, which turned every softened border and caption to full ink —
     the reader's header pills lost their border entirely and fell back to the
     browser's default two-tone button chrome. rgba() is identical output and
     universally supported. It has to be declared per ground, because --ink is. */
  --ink-rgb: 35, 38, 31;            /* --reader-ink-light  #23261f */
  --stage-bg: var(--reader-bg-light);
}

.vizz-reader[data-ground="dark"] {
  --ink: var(--reader-ink-dark);
  --ink-rgb: 251, 252, 247;         /* --reader-ink-dark   #fbfcf7 */
  --stage-bg: var(--color-neutral-900);
}

/* ── shell ────────────────────────────────────────────────────────── */

/* The spec gives the grid `calc(100vh - 108px)`, 108px being the header band.
   A flex column is identical when the header is 108px, but the spec also says
   the header wraps, and a fixed offset overflows as soon as it does. */
.vizz-reader {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--stage-bg);
}

.vizz-reader-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr) minmax(300px, 380px);
}

.vizz-reader[data-meta="closed"] .vizz-reader-grid {
  grid-template-columns: 104px minmax(0, 1fr) 0px;
}

.vizz-reader[data-fullscreen="true"] {
  position: fixed;
  inset: 0;
  z-index: 90;
}

/* No Fullscreen API -- every browser on an iPhone -- so the button could only
   do nothing. citac.html asks the browser and sets the attribute. */
.vizz-reader[data-can-fullscreen="false"] #vizz-fullscreen { display: none; }

/* ── page header (light in both themes) ───────────────────────────── */

.vizz-reader-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  padding: 14px clamp(16px, 3vw, 40px);
  background: var(--color-bg);
  color: var(--color-text);
}

.vizz-reader-back {
  font-size: 12px;
  font-weight: 600;
  color: var(--color-accent-700);
  text-decoration: none;
  white-space: nowrap;
}
.vizz-reader-back:hover { color: var(--color-accent-800); text-decoration: underline; }

.vizz-reader-kicker {
  font-size: 12px;
  color: rgba(var(--color-text-rgb), 0.55);
  white-space: nowrap;
}
/* The postcard kicker is a link to its Zbirka, coloured like "Natrag na
   rezultate" beside it. */
a.vizz-reader-kicker { color: var(--color-accent-700); }
a.vizz-reader-kicker:hover { color: var(--color-accent-800); }

.vizz-reader-title {
  font-size: 12px;
  font-weight: 600;
  max-width: 44ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vizz-reader-controls {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Header pills sit on the light band, so they use --color-text, not --ink. */
.vizz-hdr-pill {
  cursor: pointer;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  background: transparent;
  border: 1px solid rgba(30, 33, 26, 0.16);
  color: var(--color-text);
  white-space: nowrap;
}
.vizz-hdr-pill:hover { background: rgba(var(--color-text-rgb), 0.06); }
.vizz-hdr-pill[aria-pressed="true"] {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

/* ── stage bars ───────────────────────────────────────────────────── */

.vizz-stage-wrap {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--stage-bg);
  color: var(--ink);
}

.vizz-bar {
  display: flex;
  align-items: center;
  padding: 10px 20px;
  flex-wrap: wrap;
}
.vizz-bar-top {
  gap: 8px;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.18);
}
.vizz-bar-bottom {
  gap: 10px;
  border-top: 1px solid rgba(var(--ink-rgb), 0.18);
}

.vizz-modes { display: flex; gap: 8px; }
.vizz-bar-right { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.vizz-step { display: flex; gap: 6px; }

/* View-mode tab. The prototype puts #fbfcf7 on the accent fill, but the
   handoff CLAUDE.md is explicit that ink on accent fills is #23261f and that
   getting it wrong fails contrast — so the token wins over the prototype. */
.vizz-tab {
  cursor: pointer;
  padding: 7px 13px;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid rgba(var(--ink-rgb), 0.3);
  background: transparent;
  color: var(--ink);
  white-space: nowrap;
}
.vizz-tab[aria-pressed="true"] {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-accent-ink);
}

.vizz-circle {
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(var(--ink-rgb), 0.26);
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
}
.vizz-circle:disabled { opacity: .35; cursor: not-allowed; }

.vizz-pill {
  cursor: pointer;
  padding: 7px 13px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(var(--ink-rgb), 0.26);
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.vizz-pill[aria-pressed="true"] {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-accent-ink);
}

.vizz-zoom-label {
  color: var(--ink);
  font-size: 12px;
  font-weight: 600;
  min-width: 48px;
  text-align: center;
}

.vizz-page-label { color: var(--ink); font-size: 12px; font-weight: 600; }
.vizz-note {
  margin-left: auto;
  color: rgba(var(--ink-rgb), 0.55);
  font-size: 11px;
}

/* ── page rail ────────────────────────────────────────────────────── */

.vizz-rail {
  border-right: 1px solid rgba(var(--ink-rgb), 0.18);
  padding: 14px 10px;
  overflow-y: auto;
  min-height: 0;
  background: var(--stage-bg);
  color: var(--ink);
}

.vizz-rail-label {
  font-size: 9.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: rgba(var(--ink-rgb), 0.55);
  margin-bottom: 10px;
}

.vizz-rail-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.vizz-rail-item {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: #201e1d;
  border-radius: var(--radius-sm);
  aspect-ratio: 3 / 2;
  overflow: hidden;
  cursor: pointer;
  outline: 1px solid rgba(var(--ink-rgb), 0.25);
}
.vizz-rail-item[aria-current="true"] { outline: 2px solid var(--color-accent); }
.vizz-rail-item[data-has-scan="false"] { opacity: .25; cursor: default; }

.vizz-rail-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.vizz-rail-chip {
  position: absolute;
  left: 0;
  bottom: 0;
  background: var(--color-text);
  color: #fbfcf7;
  font-size: 9px;
  font-weight: 600;
  padding: 2px 5px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── stage ────────────────────────────────────────────────────────── */

/* Mirador sizes itself to this element, and this element is a flex child of
   .vizz-stage-wrap. Without flex:1 it collapses to its content height — and
   its content is Mirador at height:100% of that zero, so nothing paints. */
/* Two stacked layers: Mirador underneath, the two-up over it. The stage is
   never display:none — OpenSeadragon reads its container size when shown, and
   a zero-sized container leaves getHomeZoom() meaningless, which is what made
   the zoom readout report 0% and 40000%. */
.vizz-stage-area {
  position: relative;
  flex: 1;
  min-width: 0;
  min-height: 0;
  background: var(--stage-bg);
  /* Its layers all sit at inset 0, so this only ever clips the swipe slide,
     which moves the whole stage sideways on a single side. */
  overflow: hidden;
}

#vizz-stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--stage-bg);
  color: var(--ink);
}
#vizz-stage > div { height: 100%; }

.vizz-stage-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 40px;
  text-align: center;
  font-size: 14px;
  color: rgba(var(--ink-rgb), 0.7);
}

/* ── two-up (Lice + naličje) ──────────────────────────────────────── */

/* An author `display` beats the hidden attribute's UA `display:none`, so the
   attribute has to be honoured explicitly or the panel never hides. */
.vizz-twoup[hidden] { display: none; }

.vizz-twoup {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Nothing scrolls: vizz-reader.js moves the pair with a transform, so it
     drags at any zoom the way OSD drags a single side. Scrolling could only
     reach overflow, and at 100% there is none. */
  overflow: hidden;
  display: flex;
  /* Plain centring, not `safe`: a zoomed pair spills evenly past both edges,
     which keeps the zoom anchored on the middle, and the pan reaches the
     rest. `safe` was for scrolling, which cannot reach overflow that lies
     above or left of the origin. */
  align-items: center;
  justify-content: center;
  padding: 36px 28px;
  gap: 6px;
  background: var(--stage-bg);
  /* One finger pans the pair through the pointer handler; a pinch stays the
     browser's. `none` first, for browsers that do not know `pinch-zoom`. */
  touch-action: none;
  touch-action: pinch-zoom;
  user-select: none;
  /* The stage is the query container, so the fits below can size the images
     in cqw/cqh. Safe here: it is absolutely positioned, so size containment
     cannot collapse it. */
  container-type: size;
  /* What the height fit leaves under each image: the figure's 8px gap and
     the caption's fixed 14px line. */
  --twoup-caption: 22px;
}

.vizz-twoup figure {
  margin: 0;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  transform: translate(var(--twoup-pan-x, 0px), var(--twoup-pan-y, 0px));
}
/* The spring back after a release past the limit. The drag itself follows
   the pointer with no easing. The curve is the shell's --motion-ease, which
   is not loaded on this page. */
.vizz-twoup[data-settling] figure {
  transition: transform .25s cubic-bezier(.22, .72, .18, 1);
}
@media (prefers-reduced-motion: reduce) {
  .vizz-twoup[data-settling] figure { transition: none; }
}

/* A swipe that changes the card (vizz-reader.js skips it under reduced
   motion). On the two-up, a copy of the old pair laid over the stage leaves
   while the new pair comes in behind it; on a single side, where Mirador
   swaps the image, the whole stage slides in from the side and fades up. */
.vizz-twoup[data-sliding] figure {
  transition: transform .28s cubic-bezier(.22, .72, .18, 1);
}
.vizz-twoup-ghost {
  z-index: 3;
  background: transparent;
  pointer-events: none;
}
#vizz-stage[data-sliding="from"] {
  transform: translateX(var(--slide-from, 0));
  opacity: 0;
}
#vizz-stage[data-sliding="run"] {
  transition: transform .28s cubic-bezier(.22, .72, .18, 1), opacity .28s ease-out;
}

.vizz-twoup img {
  display: block;
  flex: none;
  border-radius: 3px;
  box-shadow: 0 10px 30px rgba(35, 38, 31, .18);
  cursor: zoom-in;
  /* the container handles drag-to-pan itself */
  user-select: none;
  -webkit-user-drag: none;
}

.vizz-twoup { cursor: grab; }
.vizz-twoup:active,
.vizz-twoup[data-panning="true"] img { cursor: grabbing; }
.vizz-reader[data-ground="dark"] .vizz-twoup img {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35);
}

/* Zoom is a multiplier on the fitted size; past 100% the pair spills past the
   stage and is dragged into view.

   Measured against the stage, not the viewport. These were `50vw - 120px` and
   `100dvh - 300px`: a fixed inset guessed from the desktop layout, where the
   rail and the metadata panel take the difference. Subtracting a constant is
   what broke narrow screens -- at 390px "half the viewport" is 195px and the
   120px came off the top of it, leaving 75px cards, and below 240px it would
   have gone negative. The container units resolve against .vizz-twoup's
   content box, which *is* the stage at every width, so there is nothing left
   to guess.

   The image carries the size and the figure wraps it. cqw/cqh reach the stage
   however deep the image sits, where a percentage would only reach the figure.
   The height fit used to size the figure instead and let the image flex into
   it, which left the figure as wide as the image's *natural* width -- 800px at
   every zoom -- so the two sides overlapped zoomed in and drifted apart zoomed
   out. */
.vizz-twoup[data-fit="width"] img {
  /* Both sides across the width, less half the 6px gap each. */
  width: calc(var(--twoup-zoom, 1) * (50cqw - 3px));
  height: auto;
  max-width: none;
}
.vizz-twoup[data-fit="height"] img {
  /* The full height, less the caption under it; the width follows the ratio. */
  width: auto;
  height: calc(var(--twoup-zoom, 1) * (100cqh - var(--twoup-caption)));
  max-width: none;
}

.vizz-twoup figcaption {
  font-size: 11px;
  line-height: 14px;   /* fixed: the height fit subtracts it, see --twoup-caption */
  color: rgba(var(--ink-rgb), 0.6);
}

/* ── metadata panel ───────────────────────────────────────────────── */

.vizz-meta {
  background: var(--stage-bg);
  color: var(--ink);
  border-left: 1px solid rgba(var(--ink-rgb), 0.18);
  overflow-y: auto;
  min-height: 0;
}

.vizz-reader[data-meta="closed"] .vizz-meta { display: none; }

.vizz-meta-header {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px;
  background: var(--stage-bg);
}

.vizz-meta-header h2 {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
}

.vizz-meta-close {
  cursor: pointer;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  padding: 2px 4px;
}

.vizz-meta-body { padding: 26px 28px 48px; }

.vizz-meta-kicker {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--color-accent-700);
  margin: 0 0 8px;
}

.vizz-meta-body h3 {
  margin: 0 0 14px;
  font-size: 30px;
  line-height: 1.14;
  text-wrap: pretty;
}

.vizz-meta-desc {
  font-size: 15px;
  line-height: 1.65;
  color: rgba(var(--ink-rgb), 0.7);
  margin: 0 0 26px;
}

.vizz-meta-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.vizz-meta-table th {
  text-align: left;
  vertical-align: top;
  padding: 0 20px 14px 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 13px;
  color: rgba(var(--ink-rgb), 0.45);
  white-space: nowrap;
}
.vizz-meta-table td {
  padding: 0 0 14px;
  vertical-align: top;
  text-wrap: pretty;
}

/* ── responsive (doc 03 section 1: below 900px, single column) ────── */

@media (max-width: 900px) {
  .vizz-reader-grid,
  .vizz-reader[data-meta="closed"] .vizz-reader-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: minmax(0, auto);
  }
  .vizz-rail { display: none; }
  .vizz-reader { height: auto; min-height: 100dvh; overflow: visible; }
  .vizz-stage-wrap { min-height: 60vh; }

  /* Postcard stage bar. `.vizz-bar` wraps, but `.vizz-bar-right` is a nested
     flex, so the bar wrapped *around* the control group while the group itself
     stayed one unbreakable row -- 513px of it in a 490px viewport, which is
     what pushed the reader off the right edge and gave the page a sideways
     scroll. Books are unaffected: their controls live in the page header.
     `margin-left` goes with it, so once the group is on its own line it starts
     at the left like the tabs above rather than being pushed right. */
  .vizz-bar { padding-inline: 14px; }
  .vizz-modes,
  .vizz-bar-right { flex-wrap: wrap; }
  .vizz-bar-right { margin-left: 0; }

  /* The ← → pair goes last and right: the bar's bottom-right corner, nearest
     the thumb. The group spans the bar so that "right" is the bar's edge
     rather than the end of a short last line. */
  .vizz-bar-right { flex-basis: 100%; }
  .vizz-step { order: 1; margin-left: auto; }

  /* The metadata panel opens over the reader, the way the list's "Pregled
     zapisa" drawer does on a phone (vizz-tokens.css, same keyframes), rather
     than as a row under the stage. It starts closed here -- see citac.html --
     and the page behind it holds still while it is open, as behind the
     drawer. */
  .vizz-meta {
    position: fixed;
    inset: 0;
    z-index: 60;
    border-left: 0;
    overscroll-behavior: contain;
    animation: vizzDrawerIn .34s cubic-bezier(.22, .72, .18, 1) both;
  }
  body:has(.vizz-reader[data-meta="open"]) { overflow: hidden; }
}
@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .vizz-meta { animation: none; }
}

/* ── scrollbars ───────────────────────────────────────────────────────
   vizz-tokens.css paints the thumb with a fixed dark ink (#1e211a at 22%),
   which disappears against the dark ground. Inside the reader the thumb has
   to follow --ink like everything else. */

.vizz-reader { scrollbar-color: rgba(var(--ink-rgb), 0.32) transparent; }

.vizz-rail::-webkit-scrollbar,
.vizz-meta::-webkit-scrollbar,
#vizz-stage ::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.vizz-rail::-webkit-scrollbar-track,
.vizz-meta::-webkit-scrollbar-track,
#vizz-stage ::-webkit-scrollbar-track {
  background: transparent;
}

.vizz-rail::-webkit-scrollbar-thumb,
.vizz-meta::-webkit-scrollbar-thumb,
#vizz-stage ::-webkit-scrollbar-thumb {
  background: rgba(var(--ink-rgb), 0.32);
  border-radius: var(--radius-pill);
}

.vizz-rail:hover::-webkit-scrollbar-thumb,
.vizz-meta:hover::-webkit-scrollbar-thumb {
  background: rgba(var(--ink-rgb), 0.48);
}

/* ── react-mosaic gutters ─────────────────────────────────────────────
   Mirador lays its window out with react-mosaic, which insets the tile by
   3px of root padding plus a 3px tile margin and paints the window body
   white. With a single chrome-less window that reads as a panel floating
   inside our stage, with a visible edge the image cannot cross when zoomed.

   Doc 03 section 5 warns off CSS overrides because Mirador's own class names
   are Emotion hashes that change between builds. These three are not: they
   come from the react-mosaic library, not the build, and are the same stable
   names the library documents. Scoped to #vizz-stage so nothing else is at
   risk. */

#vizz-stage .mosaic-root { inset: 0 !important; }
#vizz-stage .mosaic-tile { margin: 0 !important; }
#vizz-stage .mosaic-window,
#vizz-stage .mosaic-window-body {
  background: transparent !important;
  border-radius: 0 !important;
}
/* The library's drag preview is `position: absolute; width: 100%` with a 1px
   border on each side and content-box sizing, so it sits 2px wider than the
   stage. Harmless at desktop widths; on a phone those 2px are a sideways
   scroll on the whole page. It is only ever shown while a window is dragged,
   and there is one chrome-less window here to drag. */
#vizz-stage .mosaic-preview { box-sizing: border-box; }

/* ── book filmstrip ───────────────────────────────────────────────────
   Doc 03 §1, the approved "Čitač — knjiga drugi" stage. Ours rather than
   Mirador's `scroll` view — see the note in citac.html. Overlays #vizz-stage
   so Mirador keeps a measurable size underneath. */

.vizz-strip[hidden] { display: none; }

.vizz-strip {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  /* `safe` matters once a page is zoomed past the stage: plain centring puts
     the overflow above the scroll origin, so the top of a tall page could not
     be reached by scrolling or dragging — only by zooming back out. */
  align-items: safe center;
  gap: 28px;
  padding: 18px 28px;
  /* Both axes scroll. A zoomed page is taller than the stage, and clipping
     that was what forced the reader back to 100% to see a page's head. */
  overflow: auto;
  overscroll-behavior: contain;
  touch-action: pan-x pan-y;
  /* Proximity, not mandatory: the reader centres pages itself, and a mandatory
     snap re-scrolls the strip on its own after any layout change, fighting
     that centring instead of complementing it. */
  scroll-snap-type: x proximity;
  background: var(--stage-bg);
}

/* Zoomed in, the strip is a pannable surface rather than a row of pages to
   pick from: snapping would tug the page away mid-inspection, and the cursor
   should offer the drag. Clicking still leafs. */
.vizz-strip[data-zoomed="true"] { scroll-snap-type: none; cursor: grab; }
.vizz-strip[data-zoomed="true"] .vizz-strip-page img { cursor: grab; }
.vizz-strip[data-panning="true"],
.vizz-strip[data-panning="true"] .vizz-strip-page img { cursor: grabbing; }
.vizz-strip[data-panning="true"] { user-select: none; }

.vizz-strip-page {
  margin: 0;
  flex: none;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  gap: 8px;
  opacity: .55;
  transition: opacity .2s ease;
}
.vizz-strip-page[data-current="true"] { opacity: 1; }

/* Pages are sized by height, so a spread keeps a common baseline whatever
   each page's aspect ratio is. The zoom multiplier scales that height. */
.vizz-strip-page img {
  display: block;
  flex: none;
  height: calc(var(--strip-zoom, 1) * var(--strip-page-h, 60vh));
  /* Each page carries its own ratio inline, so the box is the right width
     from first layout rather than after the image decodes. 2/3 is doc 03 §1's
     placeholder ratio, used only if a page somehow has no stored size. */
  aspect-ratio: var(--page-ar, 2 / 3);
  width: auto;
  max-width: none;
  background: #fff;
  box-shadow: 0 12px 34px rgba(35, 38, 31, .18);
  user-select: none;
  -webkit-user-drag: none;
}
.vizz-reader[data-ground="dark"] .vizz-strip-page img {
  box-shadow: 0 12px 34px rgba(0, 0, 0, .35);
}

/* A click leafs to a page, and that is the only thing a click does — so the
   focused page, which a click cannot move, offers no pointer. A zoom-in cursor
   here would promise a deep-zoom gesture the strip deliberately no longer has. */
.vizz-strip-page img { cursor: pointer; }
.vizz-strip-page[data-current="true"] img { cursor: default; }

.vizz-strip-page figcaption {
  font-size: 11px;
  color: rgba(var(--ink-rgb), 0.6);
}

/* Missing scan — doc 03 §1: label plus the pending note, bottom-left. */
.vizz-strip-missing {
  flex: none;
  height: calc(var(--strip-zoom, 1) * var(--strip-page-h, 60vh));
  aspect-ratio: 2 / 3;
  background: var(--color-neutral-300);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 2px;
  padding: 20px;
  font-size: 11px;
  color: rgba(30, 33, 26, 0.16);
}

/* The page label sits in the header for this layout, so it needs the header's
   ink rather than the stage's. */
.vizz-page-label-hdr {
  color: var(--color-text);
  min-width: 48px;
  text-align: center;
}

/* ── controls that live in the header ─────────────────────────────────
   The book layout puts paging and zoom in the page header, which stays light
   in both themes. Those controls default to --ink, which follows the *stage*
   ground — so on a dark ground they became near-white on white and vanished.
   Inside the header they take the page's own ink instead. */

.vizz-reader-header .vizz-circle,
.vizz-reader-header .vizz-zoom-label,
.vizz-reader-header .vizz-page-label {
  color: var(--color-text);
}

.vizz-reader-header .vizz-circle {
  border-color: rgba(30, 33, 26, 0.16);
}
.vizz-reader-header .vizz-circle:hover:not(:disabled) {
  background: rgba(var(--color-text-rgb), 0.06);
}
