/* Hallmark · macrostructure: Editorial Catalogue · page: reader
   Pre-emit: P5 H4 E4 S5 R4 V4 — no axis below 3.
   Justification: the reader is the object itself — a book on a desk,
   chrome that recedes, nothing competing with the page. */

/* ==========================================================================
   InihaoStudio — pages/reader.css
   Section 7: the book reader. Scoped to body.reader.

   LOCKED tokens are never redefined here. Everything themed lives on a
   reader-local --r-* layer that defaults to the locked values, so the
   paper / sepia / night themes can shift the page without touching
   tokens.css.

   Turn contract (Section 7):
     turn.js 3rd release · assets/vendor/turn.js · jQuery 3.7.1
     A4 leaf (210:297) · double display · 780ms ease · gradients + elevation
     The library owns every transform it needs: it wraps each page in a
     .turn-page-wrapper, absolutely positions the page inside it, and swings
     a folding copy across the gutter on its own pointer-events:none layer.
     Everything below only sizes the box it measures and styles the page.
   ========================================================================== */

/* ---- Reader theme layer ------------------------------------------------ */

.reader {
  --r-desk:      var(--paper-2);
  --r-page:      var(--paper);
  --r-page-alt:  var(--paper-2);
  --r-ink:       var(--ink);
  --r-ink-soft:  var(--ink-2);
  --r-ink-faint: var(--ink-3);
  --r-rule:      var(--line);
  --r-chrome:    rgba(247, 241, 230, 0.94);

  --turn-duration: 780ms;
  --turn-ease: cubic-bezier(.22, 1, .36, 1);

  --page-inline: clamp(1.25rem, 4.5vw, 3.5rem);

  /* A4: 210 x 297mm, so a single leaf is 1 : sqrt(2) and a spread is 2 : sqrt(2).
     Height comes from the viewport, width is derived — that keeps the page a
     real A4 portrait at every window size instead of letting content stretch
     it. 0.70711 = 210/297 and 1.41421 = 297/210. */
  --a4-ratio: 0.70711;
  /* The 100vh term keeps a leaf short enough that the whole spread plus the
     top and bottom chrome still fits the window, so the reader never has to
     scroll to show the book. */
  --a4-page-h: clamp(260px, min(74vh, 100vh - 210px, 860px), 860px);
  --a4-page-w: calc(var(--a4-page-h) * var(--a4-ratio));

  --reader-top-h: 64px;
  --reader-bottom-h: 68px;

  background: var(--r-desk);
  color: var(--r-ink);
  min-height: 100vh;
}

/* Reader-only theme palette. These 14 hex values sit outside the locked
   Section 1 tokens and are confined to these two blocks; nothing else in
   the site may reference them. Default (paper) uses the locked tokens. */

.reader[data-theme="sepia"] {
  --r-desk:      #E4D6BC;
  --r-page:      #F2E7D0;
  --r-page-alt:  #EADCC1;
  --r-ink:       #2A2118;
  --r-ink-soft:  #4E4234;
  --r-ink-faint: #6B5C4A;
  --r-rule:      #CBB897;
  --r-chrome:    rgba(242, 231, 208, 0.95);
}

.reader[data-theme="night"] {
  --r-desk:      #141210;
  --r-page:      #1E1B17;
  --r-page-alt:  #242019;
  --r-ink:       #EDE4D4;
  --r-ink-soft:  #C6BBA6;
  --r-ink-faint: #9A8F7C;
  --r-rule:      #3A342B;
  --r-chrome:    rgba(30, 27, 23, 0.95);
}

/* Night mode needs its own focus ring and button label ink; both inherit
   --r-ink above, so nothing else has to fork. */

/* ---- Shell -------------------------------------------------------------
   A reader owns the window: the document must never be scrollable here. If
   it is, the folding sheet — which turn.js positions in document
   coordinates — can widen the scroll area mid-turn, which pops a scrollbar
   in and out and shifts the book under the pointer. Pinning the document to
   the viewport removes both the bar and the shake; the book is what scales
   to the window instead.
   ----------------------------------------------------------------------- */

html:has(body.reader) {
  overflow: hidden;
  scrollbar-gutter: auto;
}

body.reader {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
}

.reader-shell {
  display: grid;
  grid-template-rows: 1fr;
  height: 100%;
  min-height: 0;
}

.reader-main {
  display: grid;
  place-items: center;
  padding-block: calc(var(--reader-top-h) + var(--space-5)) calc(var(--reader-bottom-h) + var(--space-5));
  padding-inline: clamp(0.5rem, 2vw, 2rem);
  min-height: 0;
  /* Not a scroll container: the turning sheet must be clipped by the
     viewport, never by an inner scroller that would draw its own bar. */
  overflow: visible;
  overscroll-behavior: contain;
}

/* ---- Chrome ------------------------------------------------------------ */

.reader-chrome {
  position: fixed;
  inset-inline: 0;
  z-index: var(--z-sticky);
  background: var(--r-chrome);
  backdrop-filter: blur(8px);
  color: var(--r-ink);
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}

.reader-chrome--top {
  top: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-4);
  min-height: var(--reader-top-h);
  padding-inline: clamp(0.75rem, 3vw, 2rem);
  border-bottom: 1px solid var(--r-rule);
}

.reader-chrome--bottom {
  bottom: 0;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--r-rule);
}

.reader-chrome.is-hidden {
  transform: translateY(calc(-100% - 1px));
  opacity: 0;
  pointer-events: none;
}

.reader-chrome--bottom.is-hidden {
  transform: translateY(calc(100% + 1px));
}

.reader-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  justify-self: start;
  min-height: 44px;
  padding-inline: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--r-ink-soft);
  text-decoration: none;
  border-radius: var(--radius-sm);
}

.reader-back:hover {
  color: var(--r-ink);
}

.reader-back svg {
  flex: none;
}

.reader-book-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: -0.01em;
  text-align: center;
  text-wrap: balance;
  color: var(--r-ink);
}

.reader-counter {
  justify-self: end;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--r-ink-soft);
  white-space: nowrap;
}

/* ---- Progress + tools -------------------------------------------------- */

.reader-progress {
  height: 2px;
  background: var(--r-rule);
}

.reader-progress__bar {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--accent);
  transition: width var(--dur-base) var(--ease-out);
}

.reader-tools {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: var(--space-2) clamp(0.75rem, 3vw, 2rem);
}

.reader-tool {
  color: var(--r-ink-soft);
}

.reader-tool:hover:not([disabled]) {
  color: var(--r-ink);
}

.reader-tool[disabled] {
  opacity: 0.35;
}

.reader-tools__rule {
  width: 1px;
  height: 24px;
  margin-inline: var(--space-2);
  background: var(--r-rule);
}

/* ---- Settings + thumbnails panels ------------------------------------- */

.reader-settings {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  padding: var(--space-4) clamp(0.75rem, 3vw, 2rem) var(--space-5);
  border-top: 1px solid var(--r-rule);
  background: var(--r-page);
}

.reader-settings__group {
  min-width: 0;
  margin: 0;
}

.reader-settings__label {
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--r-ink-faint);
}

.reader-settings .chip {
  color: var(--r-ink-soft);
  border-color: var(--r-rule);
  background: transparent;
}

.reader-settings .chip[aria-pressed="true"] {
  color: var(--r-page);
  background: var(--r-ink);
  border-color: var(--r-ink);
}

.reader-settings .hint {
  margin-top: var(--space-2);
  color: var(--r-ink-faint);
}

.reader-thumbs {
  padding: var(--space-3) clamp(0.75rem, 3vw, 2rem) var(--space-5);
  border-top: 1px solid var(--r-rule);
  background: var(--r-page);
}

.reader-thumbs__list {
  display: flex;
  gap: var(--space-2);
  margin: 0;
  padding: 0 0 var(--space-2);
  list-style: none;
  overflow-x: auto;
}

.reader-thumbs__item {
  flex: none;
}

.reader-thumbs__btn {
  display: grid;
  place-items: center;
  min-width: 52px;
  height: 44px;
  padding: 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--r-ink-soft);
  background: var(--r-page-alt);
  border: 1px solid var(--r-rule);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.reader-thumbs__btn:hover {
  color: var(--r-ink);
  border-color: var(--r-ink-faint);
}

.reader-thumbs__btn[aria-current="true"] {
  color: var(--r-page);
  background: var(--r-ink);
  border-color: var(--r-ink);
}

/* ---- Stage + book ----------------------------------------------------- */

.reader-stage {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  max-width: 1180px;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.reader-book {
  position: relative;
  /* One A4 page wide per side. turn.js takes this box's width as the whole
     spread and splits it in two, so the width here IS the spread width. */
  width: min(100%, calc(var(--a4-page-w) * 2));
  margin-inline: auto;
  /* A perspective here would multiply with the one turn.js applies to each
     folding sheet and pull the curl inside out. */
  perspective: none;
}

/* A landscape leaf (a picture book) is wider than it is tall, so the A4 page
   height says nothing useful about the spread. The stage takes the column and
   reader.js derives the height from the leaf ratio instead. */
.reader-stage[data-aspect="wide"] {
  max-width: 1600px;
}

.reader-book[data-aspect="wide"] {
  width: 100%;
}

.reader-spine {
  position: absolute;
  inset-block: 0;
  left: 50%;
  width: 32px;
  transform: translateX(-50%);
  pointer-events: none;
  background: linear-gradient(
    to right,
    rgba(28, 25, 21, 0) 0%,
    rgba(28, 25, 21, 0.16) 42%,
    rgba(28, 25, 21, 0.26) 50%,
    rgba(28, 25, 21, 0.16) 58%,
    rgba(28, 25, 21, 0) 100%
  );
  opacity: 1;
}

/* ---- Track -------------------------------------------------------------
   turn.js writes an inline transform on the node it owns, so the recentring
   cannot live there. The track is the spread-sized box around it: sliding the
   track slides the book and its gutter together, and the library never sees
   the transform it did not write.
   ----------------------------------------------------------------------- */

.reader-track {
  position: relative;
  /* Sized to the spread by applyFlipSize(), so a quarter of this box is
     exactly a quarter of the spread and the leaf lands dead centre. */
  margin-inline: auto;
  /* The slide rides on the same clock as the fold, so the book opens out from
     the middle instead of jumping once the page has landed. */
  transition: transform var(--turn-duration) var(--turn-ease);
}

/* A view holding one leaf — the cover before anything is opened, and the
   closing page — is centred by sliding the track a quarter of the spread.
   Both leaves showing means the spread is already centred and nothing moves. */
.reader-track[data-sole="right"] { transform: translateX(-25%); }
.reader-track[data-sole="left"]  { transform: translateX(25%); }

/* First paint has no turn to ride along with. */
.reader-track.is-settling { transition: none; }

/* ---- Pages ------------------------------------------------------------ */

.reader-page {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: clamp(1.5rem, 4vw, 3rem) var(--page-inline);
  background: var(--r-page);
  color: var(--r-ink);
  border: 1px solid var(--r-rule);
  overflow: hidden;
}

.reader-page--left {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  box-shadow: inset -18px 0 24px -22px rgba(28, 25, 21, 0.5);
}

.reader-page--right {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  box-shadow: inset 18px 0 24px -22px rgba(28, 25, 21, 0.5);
}

.reader-page--blank {
  background: var(--r-page-alt);
}

.reader-page__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  max-width: var(--measure-read);
  margin-inline: auto;
}

.reader-page__body {
  font-family: var(--font-body);
  font-size: var(--r-font-size, 1.0625rem);
  line-height: var(--r-leading, 1.6);
  color: var(--r-ink);
  text-wrap: pretty;
}

.reader-page__body p {
  margin: 0 0 1em;
}

.reader-page__body p:last-child {
  margin-bottom: 0;
}

.reader-page__image {
  display: block;
  width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.reader-page__figure {
  display: grid;
  place-items: center;
  flex: 1;
  min-height: 0;
  margin: 0;
}

.reader-page__caption {
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--r-ink-faint);
  text-align: center;
}

/* ---- Plates (illustrated picture-book pages) ---------------------------
   A plate is a finished illustration rather than a text column, so it runs to
   the edge of the leaf: no inline padding, no folio. The page is already the
   leaf ratio, so object-fit:cover fills it without cropping anything.
   ---------------------------------------------------------------------- */

.reader-page--plate {
  padding: 0;
  gap: 0;
  justify-content: flex-end;
  background-color: #FFFFFF;
  /* A plate is full bleed. The 1px leaf border that suits a text page lands
     twice at the gutter — one on each leaf — and reads as a gap through the
     middle of a picture book. */
  border-width: 0;
  /* The gutter shading comes from the spine alone; the inset edge drawn on a
     paper page would double it up. */
  box-shadow: none;
}

/* Two illustrations meeting means the gutter is the only thing between them,
   and at the weight that suits a paper page it reads as a gap rather than a
   crease. Halved and narrowed. */
.reader-track:has(.reader-page--plate) .reader-spine {
  width: 22px;
  background: linear-gradient(
    to right,
    rgba(28, 25, 21, 0) 0%,
    rgba(28, 25, 21, 0.07) 40%,
    rgba(28, 25, 21, 0.13) 50%,
    rgba(28, 25, 21, 0.07) 60%,
    rgba(28, 25, 21, 0) 100%
  );
}

.reader-page__plate {
  position: absolute;
  inset: 0;
  margin: 0;
}

.reader-page__plate > .reader-page__image {
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: cover;
  border-radius: 0;
}

/* The paywall has to sit on top of the illustration, not behind it. */
.reader-page--plate > .reader-paywall {
  position: relative;
  z-index: 2;
  margin: 0;
  border-width: 1px 0 0;
  border-radius: 0;
  background-color: var(--r-page);
}

.reader-page__cover {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  gap: var(--space-5);
}

.reader-page__cover-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 4vw, 2.75rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--r-ink);
  text-wrap: balance;
}

.reader-page__cover-mark {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--r-ink-faint);
}

.reader-page__folio {
  margin-top: auto;
  padding-top: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  color: var(--r-ink-faint);
  text-align: center;
}

/* ---- Flipbook (turn.js) -----------------------------------------------
   turn.js builds its own layers per page:
     .turn-page-wrapper   absolute slot for the page, overflow hidden
     .reader-sheet        the page element, absolute at 0/0, exact px size
     wrapper > div        the flip wrapper the curl transform is applied to
   plus one pointer-events:none layer inside the container that carries the
   folding page as it swings across the gutter.

   .reader-sheet is sized by the library, so it declares no size of its own.
   Turn.js writes z-index onto the wrappers from its own calculation, so the
   flip container gets a stacking context and the gutter shadow is layered
   against that rather than against individual pages.
   ---------------------------------------------------------------------- */

.reader-flip {
  position: relative;
  z-index: 1;
}

.reader-sheet {
  overflow: visible;
}

.reader-sheet > .reader-page {
  width: 100%;
  height: 100%;
}

/* The gutter sits above the flip container's stacking context so it reads as
   the fold between two pages rather than a smudge on one. */
.reader-spine { z-index: 2; }

/* The library sets no cursor of its own. */
.turn-page-wrapper { cursor: default; }
.turn-page-wrapper:hover { cursor: grab; }

/* While a sheet is being dragged, the pointer should read as closed. */
.reader-stage.is-flipping .turn-page-wrapper { cursor: grabbing; }

/* ---- Click zones ------------------------------------------------------
   These are real buttons for keyboard and screen-reader users, but they must
   never own the pointer: turn.js listens for mousedown/touchstart on its own
   pages and document, and a transparent button on top of them swallows the
   gesture before the library ever sees it. So the zones are pointer-
   transparent and the stage resolves taps by coordinate instead (see
   onStageClick). The w-resize / e-resize cursor lives on the stage.
   ---------------------------------------------------------------------- */

.reader-zone {
  position: absolute;
  inset-block: 0;
  z-index: 6;
  padding: 0;
  border: 0;
  background: transparent;
  pointer-events: none;
}

.reader-zone--prev { left: 0; width: 30%; }
.reader-zone--next { right: 0; width: 30%; }
.reader-zone--center { left: 30%; width: 40%; }

.reader-zone:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
}

/* Cursor affordance lives on the stage, which does receive pointer events. */
.reader-stage[data-tap-zone="prev"] { cursor: w-resize; }
.reader-stage[data-tap-zone="next"] { cursor: e-resize; }
.reader-stage[data-tap-zone="center"] { cursor: pointer; }

/* ---- Settings state --------------------------------------------------- */

.reader[data-font="0"] { --r-font-size: 1rem; }
.reader[data-font="1"] { --r-font-size: 1.0625rem; }
.reader[data-font="2"] { --r-font-size: 1.1875rem; }

.reader[data-leading="0"] { --r-leading: 1.45; }
.reader[data-leading="1"] { --r-leading: 1.6; }
.reader[data-leading="2"] { --r-leading: 1.8; }

/* ---- Paywall card (sample mode) --------------------------------------- */

.reader-paywall {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding: var(--space-5);
  background: var(--r-page-alt);
  border: 1px solid var(--r-rule);
  border-radius: var(--radius-md);
}

.reader-paywall__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  line-height: 1.2;
  color: var(--r-ink);
}

.reader-paywall__body {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--r-ink-soft);
}

/* ---- Empty state ------------------------------------------------------ */

.reader-empty {
  display: grid;
  justify-items: center;
  gap: var(--space-4);
  max-width: 46ch;
  padding: var(--space-8) var(--space-4);
  text-align: center;
}

.reader-empty__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--r-ink);
}

.reader-empty__body {
  margin: 0;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--r-ink-soft);
}

/* ---- Small screens --------------------------------------------------- */

@media (max-width: 899px) {
  .reader-main {
    padding-inline: 0.5rem;
  }

  .reader {
    --a4-page-h: clamp(240px, min(70vh, 100vh - 200px, 720px), 720px);
  }

  .reader-page {
    padding: var(--space-5) var(--space-4);
  }

  .reader-page--left,
  .reader-page--right {
    border-radius: 0;
    box-shadow: none;
  }

  .reader-book-title {
    font-size: var(--text-sm);
  }

  .reader-counter {
    font-size: 0.6875rem;
  }

  .reader-settings {
    gap: var(--space-3) var(--space-4);
  }
}

@media (max-width: 599px) {
  .reader-chrome--top {
    grid-template-columns: auto 1fr auto;
    gap: var(--space-2);
  }

  .reader-back span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .reader-tools__rule {
    display: none;
  }
}

/* ---- Reduced motion: no 3D, no sheets -------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .reader-book {
    perspective: none;
  }

  .reader-progress__bar,
  .reader-chrome {
    transition-duration: 1ms;
  }

  .reader-track {
    transition: none;
  }
}

/* ---- Fullscreen ------------------------------------------------------- */

.reader-shell:fullscreen,
.reader-shell:-webkit-full-screen {
  background: var(--r-desk);
  padding: 0;
}

.reader-shell:fullscreen .reader-main {
  min-height: 100vh;
}
