/* ------------------------------------------------------------------
   The opened book.

   A bound codex, not two panels side by side. The boards run right around
   the outside. The two leaves meet at a shadowed gutter dead in the centre,
   the full height of the page. Stacked paper edges sit outside each leaf,
   and they shift as the reader moves through: everything on the right at
   the start, nothing on the left, and the stack crosses over as they go.

   Tabs are cut tabs. They stick out past the paper onto the board, on the
   right while they are still ahead and on the left once turned past.
   ------------------------------------------------------------------ */

.stage {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  overscroll-behavior: contain;
  padding: clamp(2rem, 5vh, 4rem) clamp(1rem, 9vw, 11rem);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-swap) var(--ease-out), visibility 0s linear var(--t-swap);
}
.stage.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t-swap) var(--ease-out), visibility 0s;
}

.stage__veil {
  position: fixed;
  inset: 0;
  background: radial-gradient(60% 60% at 50% 45%, rgba(4, 7, 10, 0.74), rgba(2, 4, 6, 0.95));
  backdrop-filter: blur(7px) saturate(115%);
}

/* ---------------------------------------------------------- the frame */

/* The spread is the size the book itself becomes when it is open: two boards
   wide and one board tall, taken from the same proportions the object uses.
   That is what lets the turn hand over without a seam, and it gives a page
   the shape of a page rather than a letterbox. */
.vessel {
  --bh: min(clamp(18rem, 66vh, 38rem), calc((100vw - 26rem) / 1.44));
  --bw: calc(var(--bh) * var(--book-w));

  /* ⭐⭐ WHAT A PAGE IS, SAID ONCE (Seth, 2026-10-27).
     *"let's make sure the reading version for the phone maps perfectly to
     the desktop version as well"*

     A page is forty-four characters across and sixteen and four fifths lines
     down. That is the page on a desktop, the page on a phone, and the page on
     the object the book is before it opens -- so the same words fall on page
     five wherever page five is read, and nothing reflows when one model hands
     over to the other.

     The type is sized to DRAW that page in whatever room the model has
     (shelf.js, fitType) rather than the page being whatever the room makes of
     the type, which is how the three of them came to disagree. It never grows
     past --page-max, so a desktop page is set exactly as it was and only
     smaller glass sets smaller type. */
  --page-ch: 44;              /* characters across */
  --page-ln: 16.8;            /* lines down        */
  --page-lh: 1.66;            /* and their leading */
  --page-max: 1.05rem;        /* the largest the type is ever set */

  position: relative;
  flex: 0 0 auto;
  margin: auto;            /* centred, and still reachable if it overflows */

  /* Two boards across. The height above is already capped at
     (100vw - 26rem) / 1.44, so two boards can never be wider than the
     window and there is nothing for a min() against 100% to do. */
  width: calc(var(--bw) * 2);
  display: flex;
  flex-direction: column;
}

.vessel__bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + .55rem);
  animation: barIn var(--t-swap) var(--ease-out) both;
  display: flex;
  align-items: center;
  gap: .9rem;
  padding-inline: .15rem;
}

.vessel__mark {
  display: inline-flex;
  align-items: baseline;
  gap: .5rem;
  font-family: var(--font-hud);
  font-size: .72rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--accent);
  text-shadow: 0 0 14px var(--accent-glow);
}
.vessel__mark span { color: var(--text-2); letter-spacing: .18em; }

.vessel__seq { flex: 1 1 auto; height: 1px; background: linear-gradient(90deg, var(--accent-dark), transparent); }

.vessel__shut, .vessel__back {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .35rem .7rem;
  font-family: var(--font-hud);
  font-size: .66rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--text-1);
  border: 1px solid var(--line);
  border-radius: 3px;
  transition: color var(--t-state) var(--ease-out),
              border-color var(--t-state) var(--ease-out),
              background var(--t-state) var(--ease-out);
}
.vessel__shut:hover, .vessel__back:hover {
  color: var(--accent);
  border-color: var(--accent-dark);
  background: var(--accent-faint);
}

@keyframes barIn { from { opacity: 0; } to { opacity: 1; } }

/* Whichever of the two states the book is in. Both occupy the same box, so
   the book does not move when it becomes the spread. */
.vessel__phase { position: relative; }
.vessel__phase[hidden] { display: none; }

/* The state being handed away, for the length of the hand-over only. It is
   lifted out of the flow so the arriving state can take its place beneath
   without either of them moving, and faded over the top of it. */
/* Laid out but not shown, for the one moment a page has to be measured
   before the book has been opened. */
.vessel__phase.is-measuring {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: -1;
  visibility: hidden;
  pointer-events: none;
}

.vessel__phase.is-handing {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 4;
  pointer-events: none;
}

/* ---------------------------------------------------------- the boards */

/* Lit as the boards of the object are lit, so the open book and the one in
   the hand read as the same book. It was darker than the object before, which
   made no sense for the same piece of leather. */
/* The window onto the spread. On a wide screen it is not a box at all --
   `display: contents` takes it out of the layout entirely, so the block is
   the child of .codex it has always been and nothing about the desktop book
   is touched by it. It becomes a real box only on a phone. */
.spread { display: contents; }

/* The arrows belong to the one-page-at-a-time reading, and that is a phone.
   Where the whole spread is in view, a leaf's own corner is the handle. */
.pager { display: none; }

.pager__row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.1rem;
}

/* How far through the whole book. An outline around the arrows, the count
   and the bar together, so the three of them read as one instrument rather
   than as a control with a decoration under it. */
.pager__bar {
  position: relative;
  height: 3px;
  margin-top: .55rem;
  border-radius: 2px;
  background: rgba(37, 234, 181, .12);
  overflow: hidden;
}
.pager__bar i {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--through, 0%);
  background: linear-gradient(90deg, var(--accent-dark), var(--accent));
  box-shadow: 0 0 10px var(--accent-glow);
  transition: width var(--t-swap) var(--ease-out);
}

.pager__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.1rem;
  color: var(--accent);
  background: rgba(7, 12, 18, .72);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color var(--t-state) var(--ease-out),
              background var(--t-state) var(--ease-out),
              opacity var(--t-state) var(--ease-out);
}
.pager__go:hover { border-color: var(--accent); background: var(--accent-faint); }
.pager__go[disabled] { opacity: .25; cursor: default; }
.pager__go[disabled]:hover { border-color: var(--line); background: rgba(7, 12, 18, .72); }

.pager__at {
  min-width: 7rem;
  text-align: center;
  font-family: var(--font-hud);
  font-size: .62rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--text-2);
}

.codex {
  position: relative;
  overflow: visible;
  height: var(--bh);
  box-sizing: border-box;
  padding: 0;
  border-radius: 3px;
  background:
    linear-gradient(100deg, rgba(0,0,0,.26) 0 1.6%, transparent 7%),
    repeating-linear-gradient(94deg, rgba(0,0,0,.09) 0 1px, transparent 1px 3px),
    linear-gradient(160deg,
      hsl(var(--hue, 168) 16% 21%) 0%,
      hsl(var(--hue, 168) 18% 16%) 56%,
      hsl(var(--hue, 168) 14% 22%) 100%);
  box-shadow: inset 0 0 0 1px rgba(201,168,76,.24);
}


/* ---------------------------------------------------------- the paper */

.block {
  --turned: 0;                             /* leaves behind the reader */
  --ahead: 0;                              /* leaves still in front    */

  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;          /* so the join is exactly in the middle */
  height: 100%;
  min-height: 0;
  background: transparent;
  overflow: visible;
}

/* The stacked edges of the leaves, outside each page. They start wholly on
   the right and cross to the left as the reader moves through the book. */
.block__edge {
  position: absolute;
  top: var(--leafpad);
  bottom: var(--leafpad);
  z-index: 3;
  pointer-events: none;
  background: repeating-linear-gradient(90deg,
    var(--paper-0) 0 1px,
    var(--paper-2) 1px 2px,
    var(--paper-1) 2px 3px);
}
/* One leaf's thickness for each leaf actually turned, so a book of three
   pages shows three, not a bar that fills as a fraction. */
.block__edge--l {
  left: var(--leafpad);
  width: min(calc(var(--turned) * var(--leaf-thick)), var(--stack));
  box-shadow: inset -3px 0 6px rgba(27,23,16,.22), 2px 0 5px rgba(27,23,16,.12);
  border-right: 1px solid rgba(27,23,16,.14);
}
.block__edge--r {
  right: var(--leafpad);
  width: min(calc(var(--ahead) * var(--leaf-thick)), var(--stack));
  box-shadow: inset 3px 0 6px rgba(27,23,16,.22), -2px 0 5px rgba(27,23,16,.12);
  border-left: 1px solid rgba(27,23,16,.14);
}


/* One page, wherever it appears: in the book as it opens, or in the spread
   once it is open. Both are a board of one width with the page inset by the
   same margin, so the print never reflows between them. */
.leaf {
  position: relative;
  z-index: 1;
  margin: var(--leafpad);
  padding: var(--leafinner);
  color: var(--ink-1);
  font-family: var(--font-paper);
  background: var(--paper-1);
}
/* The two pages meet at the gutter with no margin and no shadow between
   them. That join is where the paper comes together, not a trench. */
.leaf--left  { margin-right: 0; border-radius: 2px 0 0 2px; box-shadow: inset -20px 0 26px -20px rgba(27,23,16,.42); }
.leaf--right { margin-left:  0; border-radius: 0 2px 2px 0; box-shadow: inset  20px 0 26px -20px rgba(27,23,16,.42); }

/* The grain of the paper, and the projection falling across it. */
.block::after {
  content: '';
  position: absolute;
  inset: var(--leafpad);
  z-index: 4;
  background:
    repeating-linear-gradient(0deg, rgba(27,23,16,.02) 0 1px, transparent 1px 3px),
    radial-gradient(130% 80% at 50% -10%, rgba(255,255,255,.35), transparent 55%),
    radial-gradient(90% 70% at 18% 112%, rgba(27,23,16,.08), transparent 60%);
  mix-blend-mode: multiply;
  pointer-events: none;
}

/* Either leaf may carry any page, so both are laid out the same way. */
.leaf { display: flex; flex-direction: column; overflow: hidden; }

.plate__seq {
  font-family: var(--font-hud);
  font-size: .58rem;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.plate__code {
  font-size: clamp(2.2rem, 5.6vw, 3.4rem);
  line-height: .92;
  font-weight: 600;
  letter-spacing: .05em;
  color: var(--ink-0);
}
.plate__code em {
  display: block;
  margin-top: .55rem;
  font-size: .3em;
  font-style: normal;
  letter-spacing: .28em;
  color: var(--ink-2);
}
.plate__rule { width: 3rem; height: 2px; background: linear-gradient(90deg, var(--gilt), rgba(201,168,76,.1)); }
.plate__genre {
  font-family: var(--font-hud);
  font-size: .64rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-1);
}
.plate__tag {
  margin-top: auto;
  font-size: clamp(.98rem, 1.6vw, 1.12rem);
  font-style: italic;
  line-height: 1.42;
  color: var(--ink-0);
  text-wrap: pretty;
}
.plate__tag::before { content: '\201C'; }
.plate__tag::after  { content: '\201D'; }
.plate__foot {
  font-family: var(--font-hud);
  font-size: .54rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-2);
}


.leaf__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: .7rem;
  padding-bottom: .8rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid rgba(27,23,16,.12);
}
.leaf__head h3 {
  font-family: var(--font-hud);
  font-size: .64rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-1);
}
.leaf__folio {
  margin-left: auto;
  font-family: var(--font-paper);
  font-size: .78rem;
  color: var(--ink-2);
}

/* ⭐⭐ THE SAME MEASURE ON EVERY SCREEN (Seth, 2026-10-27, twice).
   *"Can we get the characters per line to perfectly match the desktop as
   well please"* -- and then, when the count matched but the words still
   moved: *"the text shifts when we change models"*.

   The count was capped (`max-width: 44ch`) and the HEIGHT was not, so a page
   was 44 characters across and whatever number of lines the glass happened
   to allow -- 16.9 on a desktop, 18.8 on a phone. Same measure, different
   page, and every break in a different place: eight pages against ten.

   The page is now STATED, both ways, and in the page's own units. 44ch by
   16.8 lines of 1.66, which is 44ch x 27.888em, which is the same box in
   every window and on both models of the book. Give a box the same size in
   em and the same type, and the words have nowhere else to fall.

   ⚠️ A stated width is why there is no `margin-inline: auto` and no
   `max-width` here. A leaf is a COLUMN flex, so the horizontal axis is the
   cross axis -- an auto margin on the cross axis cancels `stretch`, and the
   page sized itself to its own text at 232px. `align-self` centres it
   without touching how wide it is.

   ⚠️ And the padding-right that used to hold a scrollbar is gone. With the
   width stated, padding comes out of the text: .4rem of it took six
   characters off every line. A page does not scroll; there was nothing for
   the gutter to hold. */
.leaf__body {
  flex: 0 0 auto;
  align-self: center;
  width: calc(var(--page-ch) * 1ch);
  height: calc(var(--page-ln) * var(--page-lh) * 1em);
  overflow: hidden;
  font-size: var(--page-fs, var(--page-max));
  line-height: var(--page-lh);
  color: var(--ink-1);
}
.leaf__body p { margin-bottom: .9em; text-wrap: pretty; }
.leaf__body p:last-child { margin-bottom: 0; }
/* The fitter is named here too, and that matters: a drop cap is three lines
   tall, and measuring an opening page without one allowed it about two lines
   more than it could hold. Invisible on a desktop page of thirty lines; on a
   phone page of ten it cut the last line in half. shelf.js puts the class on
   the fitter to match the page it is standing in for. */
.leaf__body.is-opening-page p:first-child:not(.is-waiting)::first-letter,
.fitter.is-opening-page p:first-child:not(.is-waiting)::first-letter {
  float: left;
  margin: .06em .08em 0 0;
  font-size: 3em;
  line-height: .78;
  font-weight: 600;
  color: var(--ink-0);
}
.leaf__body strong { color: var(--ink-0); font-weight: 600; }
.leaf__body em { font-style: italic; }

/* Nothing announced. Said plainly, because a prepared spot that pretends to
   hold something is worse than one that admits it is waiting. */
.awaiting {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  padding: 1.3rem 1.1rem;
  border: 1px dashed rgba(27,23,16,.22);
  border-radius: 3px;
  background: rgba(27,23,16,.025);
}
.awaiting__k {
  font-family: var(--font-hud);
  font-size: .56rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.awaiting p { font-size: .98rem; line-height: 1.58; color: var(--ink-1); margin: 0; }

.vol {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding: .85rem 0;
  border-bottom: 1px solid rgba(27,23,16,.1);
}
.vol:last-child { border-bottom: 0; }
.vol__n { font-family: var(--font-hud); font-size: .56rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-2); }
.vol__t { font-size: 1.05rem; font-weight: 600; color: var(--ink-0); }
.vol__d { font-family: var(--font-hud); font-size: .7rem; color: var(--ink-2); }

/* ---------------------------------------------------------- cut tabs */
/* They belong to the paper, so they begin on the page and run outward, past
   the board and clear of the book altogether. Ahead of the reader on the
   right; once turned past, on the left. */

.tabrail {
  --out: clamp(1.4rem, 3vw, 2.6rem);

  position: absolute;
  top: clamp(2rem, 4vw, 3.2rem);
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: .45rem;
  animation: barIn var(--t-swap) var(--ease-out) both;
}
.tabrail--r { left:  calc(100% - var(--leafpad)); align-items: flex-start; }
.tabrail--l { right: calc(100% - var(--leafpad)); align-items: flex-end; }

.tab {
  display: block;
  max-width: clamp(4.5rem, 7vw, 8.5rem);
  padding: .45rem .7rem;
  font-family: var(--font-hud);
  font-size: .55rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink-1);
  transition: transform var(--t-state) var(--ease-out),
              color var(--t-state) var(--ease-out),
              background var(--t-state) var(--ease-out);
}
/* Paper at the end that is still on the page, falling into shade where it
   leaves the board. */
.tabrail--r .tab {
  border-radius: 0 4px 4px 0;
  background: linear-gradient(90deg, var(--paper-0) 0%, var(--paper-1) 62%, var(--paper-2) 100%);
  box-shadow: 2px 2px 7px rgba(0,0,0,.45);
}
.tabrail--l .tab {
  border-radius: 4px 0 0 4px;
  background: linear-gradient(270deg, var(--paper-0) 0%, var(--paper-1) 62%, var(--paper-2) 100%);
  box-shadow: -2px 2px 7px rgba(0,0,0,.45);
}
.tabrail--r .tab:hover, .tabrail--r .tab:focus-visible { transform: translateX(.4rem); color: var(--ink-0); }
.tabrail--l .tab:hover, .tabrail--l .tab:focus-visible { transform: translateX(-.4rem); color: var(--ink-0); }

.tab.is-here { color: var(--void-0); }
.tabrail--r .tab.is-here {
  background: linear-gradient(90deg, var(--accent-pale), var(--accent) 60%, var(--accent-deep));
  box-shadow: 2px 2px 7px rgba(0,0,0,.45), 0 0 18px var(--accent-glow);
}
.tabrail--l .tab.is-here {
  background: linear-gradient(270deg, var(--accent-pale), var(--accent) 60%, var(--accent-deep));
  box-shadow: -2px 2px 7px rgba(0,0,0,.45), 0 0 18px var(--accent-glow);
}
/* ---------------------------------------------------------- the gust */
/* Four leaves a hair apart, crossing the right-hand page and gone in under a
   third of a second however far the reader asked to travel. The flip is a
   gesture, not a tally -- turning each leaf in turn at a believable speed
   would cost seconds, and nothing here may make the reader wait. */

/* Inset to the paper, so a turning sheet is exactly one page. */
.gust {
  position: absolute;
  inset: var(--leafpad);
  z-index: 6;
  overflow: hidden;
  pointer-events: none;
  perspective: 1500px;
}
.gust__sheet {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  background: linear-gradient(90deg, var(--paper-0), var(--paper-1) 60%, var(--paper-2));
  box-shadow: -10px 0 26px rgba(27,23,16,.32);
  border-radius: 0 2px 2px 0;
  backface-visibility: hidden;
  will-change: transform, opacity;
}
.gust--fwd  .gust__sheet { left: 50%;  transform-origin: left center;  animation: gustFwd  var(--t-flip) var(--ease-both) both; }
.gust--back .gust__sheet { right: 50%; border-radius: 2px 0 0 2px; transform-origin: right center; animation: gustBack var(--t-flip) var(--ease-both) both; }

.gust__sheet:nth-child(1) { animation-delay: 0ms; }
.gust__sheet:nth-child(2) { animation-delay: 38ms; }
.gust__sheet:nth-child(3) { animation-delay: 76ms; }
.gust__sheet:nth-child(4) { animation-delay: 112ms; }

@keyframes gustFwd  { from { transform: rotateY(0deg);  opacity: 1; } 60% { opacity: 1; } to { transform: rotateY(-172deg); opacity: 0; } }
@keyframes gustBack { from { transform: rotateY(0deg);  opacity: 1; } 60% { opacity: 1; } to { transform: rotateY(172deg);  opacity: 0; } }

.leaf--right.is-turning .leaf__head,
.leaf--right.is-turning .leaf__body { opacity: 0; }
.leaf--right .leaf__head, .leaf--right .leaf__body { transition: opacity var(--t-tick) var(--ease-out); }

/* The whole block shifts as the stack crosses over, which is what sells it. */
.block, .leaf--left, .leaf--right, .block__edge--l, .block__edge--r {
  transition: all var(--t-flip) var(--ease-both);
  transition-property: padding, width;
}

/* ---------------------------------------------------------- narrow screens */
/* One page at a time, by his instruction. No spread, no stack, no gust. */

@media (max-width: 860px) {
  /* ⭐ THE SPREAD IS KEPT ON A PHONE (Seth, 2026-10-27).
     ⛔ This reverses the earlier instruction recorded above -- "one page at a
     time, no spread, no stack, no gust" -- which he withdrew on seeing it on
     a real phone: two cream slabs with buttons under them read as a form, not
     as a book. The instruction now is the same book the desktop has, with the
     text a little larger and as many more pages as that takes.

     ⭐ NOTHING HERE PAGINATES, AND THAT IS WHY THIS WORKS. The reader in
     shelf.js measures each section against the real page box and starts a new
     page when it runs out, so a smaller box and larger type produce the extra
     pages by themselves. There is no page count to change. */

  /* Centred, not pinned to the top. A book held up is held up in front of
     you. `margin: auto` on the vessel does the centring rather than
     align-items alone, so that a book taller than the window still scrolls
     from its top instead of having its head cut off. */
  /* Biased upward. The padding is uneven on purpose: with the vessel centred
     by `margin: auto`, giving the bottom more than the top lifts the whole
     thing, which is what he asked for and what stops the tabs crowding the
     foot of the window. */
  /* ⭐ No side rails. The page you are NOT on has to run off the edge of the
     SCREEN, not stop short of it against a margin -- a strip that ends in a
     gap reads as a second object beside the book rather than as the rest of
     the book going past the glass. The arrows and the tabs keep their own
     margins below; only the book bleeds. */
  .stage {
    align-items: center;
    overflow-y: auto;
    padding: .5rem 0 2.25rem;
  }

  /* ⭐⭐ ONE PAGE AT A TIME, AND IT IS STILL THE WHOLE BOOK (Seth, 2026-10-27).
     *"Maybe we keep the phone book exactly like the desktop one, tabs and all,
     but we only see one page at a time. Then we have nav arrows to see the
     next one."*

     The spread is built exactly as the desktop builds it -- two boards, the
     gutter between them, the stack of leaves at both edges, the tabs. What
     changes is how much of it is on the glass: the window is ONE page wide
     and the spread is slid sideways behind it.

     ⭐ And this is what fixes the measure. Fitting both pages on a phone gave
     a column of twenty-one characters, half the desktop's forty-four, which
     is too narrow to read. A page that has the screen to itself gets about
     forty-two -- the desktop's own measure, on a phone. The book did not have
     to get smaller; it had to stop showing two pages at once. */
  .vessel {
    /* ⭐ The window is one board PLUS a sliver, so the page you are not on is
       always half an inch of itself at the edge of the screen. Without it a
       page had no neighbour and nothing said whether you were looking at the
       left of an opening or the right of one. */
    --peek: 1.4rem;

    /* One board as wide as the glass allows, and short enough that the bar
       above it, the arrows below it and the tabs under those all fit without
       the stage having to scroll. */
    --bh: min(
      calc((100vw - var(--peek)) / var(--book-w)),
      calc(100svh - 19rem)
    );
    width: calc(var(--bw) + var(--peek));
    margin: auto;

    /* ⭐⭐ ON THE VESSEL, NOT ON THE BLOCK -- and this was the shift.
       A smaller page gives its margins away before it gives away its text,
       so these are trimmed for a phone. They used to be trimmed on .block,
       which is the spread and ONLY the spread: the object the book is before
       it opens sits outside it and kept the desktop's margins. Its page came
       out 313px wide against the spread's 321, the 44ch cap bound on one and
       not the other, and the words reset from 43 characters to 44 at the
       moment one model handed over to the other. Set here, both models are
       inside it and the two rooms are the same room. */
    --leafpad: .3rem;
    --leafinner: .7rem .75rem;
    --stack: .25rem;
  }

  /* Everything that is not the book keeps a margin, so only the paper
     touches the edges of the glass. */
  .pager { margin-inline: .7rem; }
  .vessel__bar { padding-inline: .8rem; }

  .spread {
    display: block;
    position: relative;
    overflow: hidden;
    width: 100%;
    height: 100%;
    /* The swipe belongs to the book. Without this the browser takes a
       vertical drag for a scroll of the stage behind it and the gesture
       never reaches the page. */
    touch-action: none;
  }
  .block {
    width: calc(var(--bw) * 2);

    /* Slid sideways to choose a page, and scaled down to show the whole book
       at once. The scale is set from JS as a bare number, because calc()
       cannot divide one length by another and the zoom that fits the spread
       in the window is exactly that division. */
    transform: translateX(var(--pan, 0px)) scale(var(--zoom, 1));
    transform-origin: 0 50%;
    transition: transform var(--t-swap) var(--ease-out);
  }

  /* The pass where the whole book is shown takes longer than a slide, so the
     reader has time to see where the page sits in it. */
  .block.is-showing-all { transition-duration: var(--t-pull); }

  /* ⭐⭐ THE OBJECT MEETS THE SPREAD AT THE SAME SIZE.
     They are two models of one book and they change places. The object used
     to be full size at the moment it handed over while the spread was
     already pulled back, so the swap was a jump -- the big book opening onto
     a sharply zoomed-in page. shelf.js sets --shrink to exactly the scale
     the spread will be at, so the object arrives there as its cover swings,
     and grows back to full as the cover comes over again on the way out. */
  .vessel__phase[data-phase="inspect"] {
    transform: scale(var(--shrink, 1));
    transform-origin: 50% 50%;
    transition: transform var(--t-pull) var(--ease-out);
  }

  /* ⭐⭐ THE COVER FOLLOWS THE BOOK.
     The board is the .codex's background, and .codex does not scale -- so
     when the block was pulled back to show the whole book, the cover stayed
     the size of the window and the book floated in a slab of green that had
     stopped meaning anything. Drawn on the block instead, it is part of what
     scales and stays the thing the pages are bound into. */
  .codex { background: none; box-shadow: none; }
  .block::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: 3px;
    background:
      linear-gradient(100deg, rgba(0,0,0,.26) 0 1.6%, transparent 7%),
      repeating-linear-gradient(94deg, rgba(0,0,0,.09) 0 1px, transparent 1px 3px),
      linear-gradient(160deg,
        hsl(var(--hue, 168) 16% 21%) 0%,
        hsl(var(--hue, 168) 18% 16%) 56%,
        hsl(var(--hue, 168) 14% 22%) 100%);
    box-shadow: inset 0 0 0 1px rgba(201, 168, 76, .24);
  }

  /* The corner of a leaf turns a page, and it only works when you can see the
     leaf. With one page in the window there is no corner on the page you are
     not looking at, so the arrows carry the job instead. */
  .pager {
    display: block;
    margin-top: .6rem;
    padding: .5rem .7rem .55rem;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: rgba(7, 12, 18, .5);
  }
  /* ⚠️⚠️ NOTHING ABOUT THE PAGE IS SET HERE ANY MORE, AND THAT IS THE POINT.
     This block used to hold a phone's own typography -- .95rem on a leading
     of 1.5, hyphens on, paragraphs .75em apart, a two-and-a-half-line drop
     cap. Every one of those is a decision about where a line ends, and each
     one was a decision the desktop had made differently. Forty-four
     characters a line and the words still moved, because the lines were a
     different height, the gaps between paragraphs were a different size and
     `hyphens: auto` broke words the desktop left whole. Ten pages against
     eight.

     A page is 44ch x 16.8 lines of 1.66 everywhere (see --page-ch at the top
     of this file) and shelf.js sets the type to the size that draws it. The
     head is furniture round the page rather than part of it, so it may still
     be trimmed. */
  .leaf__head h3 { font-size: .7rem; }
  .plate__code { font-size: clamp(1.5rem, 9vw, 2.4rem); }
  .plate__foot { display: none; }

  /* The tabs become a row across the top, in reading order, and stay there.
     There is no room for a column beside the book on a phone, and this is
     the one place the phone is allowed to differ from the desktop. */
  .tabrail {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: .35rem;
    padding: .7rem .7rem 0;
  }
  .tabrail--l { order: -2; }
  .tabrail--r { order: -1; }
  .tabrail .tab,
  .tabrail--l .tab,
  .tabrail--r .tab { transform: none; border-radius: 3px; max-width: none; }
  .tabrail .tab:hover, .tabrail .tab:focus-visible { transform: none; }

  /* The turning sheets stay. They used to be hidden here, alongside goTo
     bailing out of the animation on a narrow screen -- so on a phone a page
     turn was a silent substitution of text, and the one thing on the page
     that says "this is a book" was missing from the screen that most needs
     telling. */
  .codex { animation: none; }
}

/* The tab bodies sit in the page as ordinary markup, hidden, so the words are
   real words a search engine can read and the site still works unscripted.
   Their headings only show themselves when scripting is unavailable. */
.panels { display: none; }
.panel-src__k { display: none; }

/* ------------------------------------------------------------------
   The title plate belongs to the first opening only

   A book does not repeat its title page on every leaf. On the first tab the
   left-hand leaf carries the plate; after that the text runs across both
   leaves, as it would in any book, and the gutter falls between the columns.
   ------------------------------------------------------------------ */


/* The drop capital belongs to the opening of a section, not to a column. */

/* The veil lifts as the book goes back to the shelf, so the shelf is already
   there when it arrives. */
.stage__veil { transition: opacity var(--t-pull) var(--ease-out); }

/* With no plate, the leaves run the whole spread, so the leaves that blow past
   have to cover it. */
.gust--wide .gust__sheet { width: 50%; }

/* While a book is in the hand the vessel is only as wide as the book, so the
   space beside it belongs to the page and a click there puts the book away.

   It stands down for the length of a hand-over, when both states are on screen
   at once and the vessel must be the width of the spread. Without that, the
   vessel sized itself to whichever content it could see: on the way in, to one
   unbroken line of the book's text, five thousand pixels of it running off
   both sides of the screen; on the way out, to the shut book, which crushed
   the spread to a single board's width for the length of the fade. */
.vessel:not(:has(.vessel__phase.is-handing)):not(:has(.vessel__phase.is-measuring)):has(> .vessel__phase[data-phase="inspect"]:not([hidden])) {
  width: auto;
}

/* ------------------------------------------------------------------
   Turning a leaf by hand

   The outer bottom corner of a page lifts wherever there is a leaf to turn.
   It is the page itself that lifts, so it reads as something to take hold
   of rather than as a control put on top of the paper.
   ------------------------------------------------------------------ */

.corner {
  position: absolute;
  bottom: 0;
  width: clamp(2.6rem, 7%, 4.2rem);
  height: clamp(2.6rem, 9%, 4.2rem);
  z-index: 7;
  cursor: pointer;
  transition: width var(--t-state) var(--ease-out), height var(--t-state) var(--ease-out);
}
.corner[hidden] { display: none; }

.corner--on   { right: 0; }
.corner--back { left: 0; }

/* The lifted triangle of paper, with the underside of the leaf showing. */
/* The crease runs across the corner; the flap turned back lies below it,
   toward the outer edge, with the underside of the leaf showing. */
.corner::before {
  content: '';
  position: absolute;
  inset: 0;
  transition: filter var(--t-state) var(--ease-out);
}
.corner--on::before {
  background: linear-gradient(135deg,
    transparent 0 46%,
    rgba(27, 23, 16, .34) 48%,
    var(--paper-2) 51%,
    var(--paper-0) 72%,
    var(--paper-1) 100%);
  border-radius: 0 0 3px 0;
}
.corner--back::before {
  background: linear-gradient(45deg,
    var(--paper-1) 0%,
    var(--paper-0) 28%,
    var(--paper-2) 49%,
    rgba(27, 23, 16, .34) 52%,
    transparent 54% 100%);
  border-radius: 0 0 0 3px;
}

/* The direction the leaf would go. */
.corner::after {
  content: '';
  position: absolute;
  bottom: 26%;
  width: .55rem;
  height: .55rem;
  border-top: 1.5px solid var(--ink-2);
  border-right: 1.5px solid var(--ink-2);
  transition: transform var(--t-state) var(--ease-out), border-color var(--t-state) var(--ease-out);
}
.corner--on::after   { right: 26%; transform: rotate(45deg); }
.corner--back::after { left: 26%;  transform: rotate(-135deg); }

.corner:hover, .corner:focus-visible { width: clamp(3.2rem, 9%, 5.4rem); height: clamp(3.2rem, 11%, 5.4rem); }
.corner:hover::after, .corner:focus-visible::after { border-color: var(--ink-0); }

/* While a turn is running the words are out of the way beneath the sheets. */
.block.is-turning .leaf__head,
.block.is-turning .leaf__body { opacity: 0; }
.leaf__head, .leaf__body { transition: opacity var(--t-tick) var(--ease-out); }

.leaf.is-blank .leaf__head { display: none; }

/* The title plate, wherever in the book it falls. */
.leaf.is-plate .leaf__head { display: none; }
.leaf.is-plate .leaf__body { display: flex; flex-direction: column; gap: 1rem; }

/* ------------------------------------------------------------------
   The measuring box

   A page is broken by setting it into a box the size of a real page and
   seeing where it overruns. That box had no rules at all, so it stood in
   the flow at the size the measuring gave it -- several hundred pixels of
   invisible block beneath the spread, which is what pushed the open book
   up against the top of the window.

   It is out of the flow now, and it carries the page's own typography,
   because a measurement taken in the wrong face is not a measurement.
   ------------------------------------------------------------------ */

.fitter {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  visibility: hidden;
  pointer-events: none;
  overflow: hidden;

  font-family: var(--font-paper);
  font-size: var(--page-fs, var(--page-max));
  line-height: var(--page-lh);
  color: var(--ink-1);
}
.fitter p { margin-bottom: .9em; text-wrap: pretty; }
.fitter p:last-child { margin-bottom: 0; }
.fitter strong { font-weight: 600; }
.fitter em { font-style: italic; }

/* ------------------------------------------------------------------
   A paragraph that runs on, and a section that does.
   ------------------------------------------------------------------ */

/* ⭐ Paragraphs are set the way a book sets them: indented, with a little
   air between them rather than a blank line. That air was .9em, which on a
   page of sixteen lines is most of a line given away at every break.

   ⚠️ The fitter is changed with the page, always, together. The measuring
   box carries its own copy of the page's typography, and a measurement
   taken in the wrong face is not a measurement. */
.leaf__body p,
.fitter p {
  margin-bottom: .45em;
  text-indent: 1.45em;
}

/* ⭐ The second half of a split paragraph is not a new paragraph. It takes
   no indent, because the indent is what says "a paragraph begins here" -- and
   it does not begin, it carries on from the word it stopped at overleaf.
   shelf.js puts this class on the tail when it breaks one. */
.leaf__body p.runs-on,
.fitter p.runs-on { text-indent: 0; }

/* The first line of a section keeps its drop cap, and a continuation never
   takes one -- a three-line capital in the middle of a sentence is worse
   than no capital at all. */
.leaf__body.is-opening-page p:first-child.runs-on::first-letter,
.fitter.is-opening-page p:first-child.runs-on::first-letter {
  float: none;
  margin: 0;
  font-size: inherit;
  line-height: inherit;
  font-weight: inherit;
}
/* A paragraph that opens a section is already marked by the drop cap, so it
   does not also need the indent. */
.leaf__body.is-opening-page p:first-child:not(.runs-on),
.fitter.is-opening-page p:first-child:not(.runs-on) { text-indent: 0; }

/* ⭐ THERE IS MORE OF THIS SECTION OVERLEAF.
   Drawn on the leaf rather than inside the page, so it is not part of what
   the fitter measures -- a mark that changed how much text fitted would
   change which pages it appeared on. */
.leaf.has-more::after {
  content: '';
  position: absolute;
  right: 50%;
  bottom: calc(var(--leafpad) + .35rem);
  width: .42rem;
  height: .42rem;
  transform: translateX(50%) rotate(-45deg);
  border: solid var(--accent);
  border-width: 0 1.5px 1.5px 0;
  opacity: .85;
  animation: leafMore 2.4s var(--ease-both) infinite;
  pointer-events: none;
}
@keyframes leafMore {
  0%, 100% { transform: translateX(50%) rotate(-45deg) translate(0, 0); opacity: .55; }
  50%      { transform: translateX(50%) rotate(-45deg) translate(2px, 2px); opacity: .95; }
}

/* It belongs to the page it is on, so it goes when that page turns. */
.leaf.is-blank::after, .leaf.is-plate::after { content: none; }

@media (prefers-reduced-motion: reduce) {
  .leaf.has-more::after { animation: none; opacity: .85; }
}
