/* 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;
}

/* ── 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;
}

.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; }

/* 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-hint { color: rgba(var(--ink-rgb), 0.45); font-size: 11px; }
.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);
}

#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;
  overflow: auto;
  display: flex;
  align-items: safe center;
  justify-content: safe center;
  padding: 36px 28px;
  gap: 6px;
  background: var(--stage-bg);
}

.vizz-twoup figure {
  margin: 0;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.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 { 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; the container scrolls past 100%.

   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 percentages resolve against .vizz-twoup's content
   box, which *is* the stage at every width, so there is nothing left to guess.

   The figure carries the size and the image fills it, because a percentage on
   the image would resolve against the figure rather than the stage. */
.vizz-twoup[data-fit="width"] figure {
  /* Both sides across the width, less half the 6px gap each. */
  width: calc(var(--twoup-zoom, 1) * (50% - 3px));
}
.vizz-twoup[data-fit="width"] img {
  width: 100%;
  height: auto;
  max-width: none;
}
.vizz-twoup[data-fit="height"] figure {
  height: calc(var(--twoup-zoom, 1) * 100%);
}
.vizz-twoup[data-fit="height"] img {
  /* Takes the height the caption leaves, and keeps its ratio from it. */
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  width: auto;
  max-width: none;
}

.vizz-twoup figcaption {
  font-size: 11px;
  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; }
}

/* ── 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-twoup::-webkit-scrollbar,
#vizz-stage ::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

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

.vizz-rail::-webkit-scrollbar-thumb,
.vizz-meta::-webkit-scrollbar-thumb,
.vizz-twoup::-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,
.vizz-twoup: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);
}
