/* ============================================================
   THE BOOK
   ------------------------------------------------------------
   A large legal reference volume, built in CSS 3D.

   Geometry: the body spans two page-widths. The spine sits at the
   horizontal centre (x = W). Closed, the volume occupies the right
   half and the whole body slides left by W/2 so it reads centred.
   Opening slides it back to 0 as the cover swings about the spine.

   Depth is real: covers, the page block, the fore-edge, the tail
   and every leaf occupy their own Z. Nothing here is a flat card
   with a drop shadow.
   ============================================================ */

.bookstage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  perspective: var(--book-perspective);
  perspective-origin: 50% 42%;
  z-index: var(--z-book);
  pointer-events: none;
}

.bk {
  --open: 0;          /* 0 closed → 1 open */
  --turned: 0;        /* share of the block that has been read */
  --reveal: 0;        /* 0 unlit → 1 fully lit */
  --tiltx: 0deg;
  --tilty: 0deg;
  --lift: 0px;
  --scale: 1;
  --T: var(--book-thickness);
  --W: var(--book-w);
  --H: var(--book-h);

  position: relative;
  width: calc(var(--W) * 2);
  height: var(--H);
  transform-style: preserve-3d;
  /* The yaw is what makes it an object rather than a picture of one. It
     is full when shut and eases away as the boards open, so the spread
     ends up square to the reader. */
  transform:
    translate3d(0, var(--lift), 0)
    scale(var(--scale))
    rotateX(calc(var(--book-tilt) + var(--tiltx)))
    rotateY(calc(var(--book-yaw) * (1 - var(--open)) * 0.86 + var(--tilty)))
    rotateZ(calc(-0.5deg * (1 - var(--open))));
  will-change: transform;
}

/* ---------- the volume ---------- */
.bk__body {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: translate3d(calc(var(--W) * -0.5 * (1 - var(--open))), 0, 0);
}

/* ---------- contact shadow on the archive floor ----------
   Deliberately outside the 3D scene. A plane laid at 72 degrees
   inside the volume intersects the pages and cuts a dark band
   across the spread; a flat, blurred pool under the book reads the
   same and cannot collide with anything. */
.bookstage__shadow {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 0;
  width: calc(var(--book-w) * (1.4 + 1.1 * var(--open, 0)));
  height: calc(var(--book-h) * 0.3);
  transform: translate3d(-50%, calc(var(--book-h) * 0.42), 0) scaleY(0.62);
  /* The softness is in the gradient's own stops. A blur filter on top
     of an already-soft radial buys nothing visible and makes a large
     element expensive to paint. */
  background: radial-gradient(
    50% 50% at 50% 50%,
    rgb(0 0 0 / 0.9) 0%,
    rgb(0 0 0 / 0.66) 22%,
    rgb(0 0 0 / 0.34) 44%,
    rgb(0 0 0 / 0.12) 62%,
    rgb(0 0 0 / 0) 78%
  );
  opacity: calc((0.3 + 0.6 * var(--reveal, 0)) * (1 - var(--recede, 0)));
  pointer-events: none;
}

/* ============================================================
   COVERS
   ============================================================ */

/* The boards are larger than the block. That overhang — the "square" —
   is a couple of millimetres on a real binding, and it is most of what
   makes a cover read as a cover rather than one face of a cuboid. */
.bk__coverface {
  container-type: inline-size;
  position: absolute;
  top: calc(var(--book-square) * -1);
  height: calc(100% + var(--book-square) * 2);
  width: calc(var(--W) + var(--book-square));
  border-radius: var(--radius-1) var(--radius-2) var(--radius-2) var(--radius-1);
  /* The grain is the first background layer, not a blended pseudo-element.
     Twenty-odd `mix-blend-mode` overlays across the volume measured ~89ms
     a frame, because each one forces the compositor to read back what is
     beneath it. The tile is pre-tinted, so plain alpha gives the same
     leather. */
  background-image:
    var(--grain-leather),
    /* Mottling. Hide is not uniform: a few broad, soft variations in
       density under the grain stop the tile from reading as a repeat,
       without adding a texture. Painted once; nothing here moves. */
    radial-gradient(38% 26% at 70% 30%, rgb(0 0 0 / 0.16), rgb(0 0 0 / 0) 100%),
    radial-gradient(30% 22% at 24% 72%, rgb(255 255 255 / 0.035), rgb(255 255 255 / 0) 100%),
    radial-gradient(44% 30% at 62% 84%, rgb(0 0 0 / 0.14), rgb(0 0 0 / 0) 100%),
    linear-gradient(180deg, rgb(255 255 255 / 0.05), rgb(255 255 255 / 0) 14%),
    radial-gradient(120% 90% at 12% 8%, rgb(255 255 255 / 0.1), rgb(255 255 255 / 0) 52%),
    radial-gradient(90% 120% at 88% 96%, rgb(0 0 0 / 0.5), rgb(0 0 0 / 0) 60%),
    linear-gradient(152deg, var(--color-leather-high) 0%, var(--color-leather-mid) 26%, var(--color-leather-base) 58%, var(--color-leather-deep) 100%);
  background-size: 168px 168px, auto, auto, auto, auto, auto, auto, auto;
  box-shadow:
    /* rubbed edges: where hands have worn the board, the leather is a
       shade lighter and the grain flattened — a soft light rim, strongest
       at the fore-edge that gets opened */
    inset 0 0 0 1px rgb(190 200 222 / 0.07),
    inset 0 0 3px 1px rgb(190 200 222 / 0.06),
    inset -2px 0 5px -1px rgb(200 208 228 / 0.09),
    inset 0 0 60px rgb(0 0 0 / 0.55),
    var(--shadow-cover);
}

/* the hinge bevel — the lit edge where the board folds over the joint */
.bk__coverface::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 14px;
  background: linear-gradient(90deg,
    var(--color-leather-lit) 0%,
    var(--color-leather-high) 22%,
    rgb(0 0 0 / 0) 100%);
  opacity: 0.85;
  pointer-events: none;
  border-radius: inherit;
}

/* The blind-stamped panel. Not a line drawn on the leather: the panel is
   pressed INTO it — a shadowed upper and left wall where the tool bit, a
   lit lower lip where the leather rises back to the board, and a field a
   shade darker for having been compressed. */
.bk__coverface::after {
  content: '';
  position: absolute;
  inset: clamp(16px, 3.2%, 34px);
  border: 1px solid rgb(0 0 0 / 0.62);
  background: rgb(0 0 0 / 0.07);
  box-shadow:
    inset 0 2px 3px rgb(0 0 0 / 0.55),
    inset 2px 0 3px rgb(0 0 0 / 0.3),
    inset 0 -1px 0 rgb(255 255 255 / 0.06),
    inset 0 0 0 1px rgb(255 255 255 / 0.05),
    0 1px 0 rgb(255 255 255 / 0.08),
    0 -1px 0 rgb(0 0 0 / 0.35);
  border-radius: var(--radius-1);
  pointer-events: none;
}

.bk__backcover {
  left: var(--W);
  transform: translate3d(0, 0, calc(var(--T) * -0.5));
  border-radius: var(--radius-2) var(--radius-1) var(--radius-1) var(--radius-2);
}

/* front cover — swings about the spine */
.bk__cover {
  position: absolute;
  top: 0;
  left: var(--W);
  width: var(--W);
  height: 100%;
  transform-origin: 0% 50%;
  transform-style: preserve-3d;
  /* Order matters: the board is lifted to the top of the volume in its
     own frame and then swung about the spine, so when it is open it
     comes to rest underneath the left-hand stack rather than on top
     of it. Reversing these two turns the endpaper into a lid. */
  transform:
    rotateY(calc(-180deg * var(--open)))
    translate3d(0, 0, calc(var(--T) * 0.5));
  will-change: transform;
}

.bk__cover .bk__coverface {
  left: 0;
  backface-visibility: hidden;
}

/* Inside of the front board — marbled endpaper.

   Around an open spread this is the only part of the binding you see:
   a band of board above the left page, facing the back board's band
   above the right page. The two have to read as the same object. In
   ink with a bronze comb they do; in the warm brown this used to be,
   they read as two unrelated strips and the spread stopped looking
   like a book at all.

   The comb runs at 118deg and the crease darkens toward the hinge,
   so the board has a direction rather than being a flat panel. */
.bk__endpaper {
  position: absolute;
  /* The SAME square as the leather it is pasted to. `inset: 0` gave the
     front board an inner face exactly the size of a page, so the front
     board had no overhang while the back board had nine pixels of one —
     the two sides of the open volume were built to different dimensions.
     A pastedown covers the board, and the board is bigger than the block. */
  left: 0;
  top: calc(var(--book-square) * -1);
  width: calc(var(--W) + var(--book-square));
  height: calc(100% + var(--book-square) * 2);
  transform: rotateY(180deg);
  backface-visibility: hidden;
  border-radius: var(--radius-2) var(--radius-1) var(--radius-1) var(--radius-2);
  /* Gradients, not a texture.

     The Figma END PAPER frame specifies an engine-turned guilloche —
     "the pattern of a legal instrument, not marbling" — and it is the
     better idea. It was built (a rose-engine rosette generated into a
     canvas at boot, exactly like the grain tiles) and it looked right.
     It also cost 8 fps and took long frames from 11% to 30%, in both
     forms tried: as an extra background layer, and baked with the whole
     surface into one opaque texture. The board is inside a preserve-3d
     scene, so it is resampled at every step of the rotation and none of
     it can be cached. The performance constraint wins; the frame in
     Figma carries the note. */
  background-image:
    var(--grain-leather),
    /* the light that reaches the open board */
    radial-gradient(120% 86% at 26% 6%, rgb(255 246 226 / 0.09), rgb(255 255 255 / 0) 58%),
    /* the gutter shadow, on the board itself */
    linear-gradient(90deg, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0) 26%),
    linear-gradient(160deg,
      var(--color-endpaper-high),
      var(--color-endpaper-base) 48%,
      var(--color-endpaper-deep));
  background-size: 168px 168px, auto, auto, auto;
  box-shadow: inset 0 0 70px rgb(0 0 0 / 0.55);
}

/* The INNER face of the back board.

   Anatomy, reading across an open volume:

     front cover
       inner front cover   .bk__cover > .bk__endpaper
         hinge             the groove at the spine edge of each board
           page block      .bk__spread
         hinge
       inner back cover    .bk__endpaper--back
     back cover            .bk__backcover

   Both boards now present a pastedown to the reader and their leather
   faces away, which is what a book on a table actually does. The
   groove sits at the spine edge of each: on the front board that is
   its left edge, carried round to the right by the board's own 180
   degree swing; on the back board it is the left edge directly. The
   two grooves meet at the spine and read as the joint. */
.bk__endpaper--back {
  position: absolute;
  top: calc(var(--book-square) * -1);
  left: var(--W);
  height: calc(100% + var(--book-square) * 2);
  width: calc(var(--W) + var(--book-square));
  /* a hair in front of the board's leather so it covers it, and no
     rotation: this face is already pointing at the reader */
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 0.6px));
  backface-visibility: visible;
  border-radius: var(--radius-2) var(--radius-1) var(--radius-1) var(--radius-2);
  /* Shut, there is nothing to see: the block is in the way. It arrives
     with the boards, off a property that is already being written. */
  opacity: clamp(0, calc(var(--open) * 1.6 - 0.35), 1);
}

/* the turn-in: the leather folded over the board edge, a hairline of
   the cover material showing where the pastedown stops */
.bk__endpaper::after {
  content: '';
  position: absolute;
  /* the turn-in's edge, and inside it a single gold fillet tooled into the
     leather (Phase 5.4) — the line that frames the open pages */
  inset: calc(var(--book-square) * 0.42);
  border: 1px solid rgb(207 174 99 / 0.55);
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35), inset 0 0 0 1px rgb(0 0 0 / 0.3);
  border-radius: inherit;
  pointer-events: none;
}

/* ---------- cover typography: restrained gold foil ---------- */
.bk__plate {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(9px, 2.2cqw, 22px);
  padding: 13% 9%;
  text-align: center;
  backface-visibility: hidden;
  opacity: calc(0.06 + 0.94 * var(--reveal));
  /* no transition: --reveal is already eased in JS every frame */
}

/* The crest catches the light before the wordmark resolves.

   Two beats out of one property. `--reveal` is the key light crossing the
   board; the device is struck deeper than the type and takes the light
   first, so it leads by about a third of the sweep. No second variable
   and nothing extra written per frame — both ramps are derived here, in
   CSS, from the one value the book already writes.

     crest   lights from --reveal .08, full at .60
     type    lights from --reveal .45, full at .93 */
.bk__plate > .bk__crest {
  opacity: clamp(0, calc(var(--reveal) * 1.92 - 0.15), 1);
}
.bk__plate > .bk__coverline,
.bk__plate > .bk__rule-top,
.bk__plate > .bk__title,
.bk__plate > .bk__rule-bot,
.bk__plate > .bk__coverfoot {
  opacity: clamp(0, calc(var(--reveal) * 2.08 - 0.94), 1);
}

.bk__rule-top,
.bk__rule-bot {
  width: 46%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-accent-deep), transparent);
  opacity: 0.7;
}

.bk__title {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: 0.02em;
  font-size: clamp(1.9rem, 13cqw, 5.2rem);
}
.bk__title span { display: block; }
.bk__title .l1 { font-size: 1em; }
.bk__title .l2 { font-size: 0.62em; letter-spacing: 0.24em; margin-top: 0.42em; font-weight: 400; }

/* gold foil: a bright lip and a dark shadow, plus a slow travelling sheen */
.foil {
  /* FOIL — a material, not a colour. Mostly dark bronze with one narrow
     specular crest that the key light moves, so the type is almost unlit
     until light crosses it. The ramp and the emboss are tokens because
     the crest below is made of the same material and has to be lit by
     the same lamp. One element; the highlight costs no extra DOM. */
  background-image: var(--foil-ramp);
  background-size: 320% 100%;
  background-position: var(--foil-pos, 40%) 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: var(--foil-emboss);
}

/* ---------- the firm's device, stamped in the same foil ----------
   The mark is supplied as a grey+alpha PNG and used as a MASK, not as a
   picture: the cover paints the foil ramp through it, so the scales and
   the laurel catch the key light on exactly the beat the foiled type
   does. The artwork's own flat gold is discarded — a flat gold PNG on a
   lit cover reads as a sticker, which is the failure this whole phase is
   about.

   Cost: one static element, one mask, no blend mode, nothing written per
   frame except `--foil-pos`, which the plate already inherits. */
.bk__crest {
  width: clamp(54px, 36cqw, 196px);
  aspect-ratio: 560 / 516;
  /* the device sits apart from the words; a stamped board is not a stack */
  margin-bottom: clamp(4px, 2.4cqw, 20px);
  background-image: var(--foil-ramp);
  background-size: 320% 100%;
  background-position: var(--foil-pos, 40%) 0;
  -webkit-mask-image: var(--ad-crest);
  mask-image: var(--ad-crest);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  filter: var(--foil-emboss);
}

.bk__coverline,
.bk__coverfoot {
  font-family: var(--font-meta);
  font-weight: 500;
  font-size: clamp(0.46rem, 2.5cqw, 0.72rem);
  letter-spacing: var(--track-meta-wide);
  text-transform: uppercase;
  color: var(--color-accent-deep);
  opacity: 0.85;
}
.bk__coverfoot { font-size: clamp(0.38rem, 1.9cqw, 0.58rem); opacity: 0.62; }

/* ============================================================
   THE PAGE BLOCK — real thickness, real edges
   ============================================================ */

.bk__block {
  position: absolute;
  top: 1.2%;
  left: var(--W);
  width: var(--W);
  height: 97.6%;
  transform-style: preserve-3d;
  pointer-events: none;
}

/* fore-edge: the stack of page edges you see on the open side */
.bk__foreedge {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: calc(var(--T) - 10px);
  transform-origin: 100% 50%;
  transform: rotateY(90deg) translateZ(0);
  /* gilt (Phase 5.4): a reference volume's edges are gold, burnished,
     the leaves still just readable as fine lines through it */
  background:
    linear-gradient(90deg, rgb(0 0 0 / 0.42), rgb(0 0 0 / 0) 22%, rgb(0 0 0 / 0) 78%, rgb(0 0 0 / 0.36)),
    linear-gradient(180deg, rgb(255 240 200 / 0.28), rgb(255 240 200 / 0) 30%, rgb(255 240 200 / 0) 62%, rgb(255 230 180 / 0.18) 80%, rgb(0 0 0 / 0.2)),
    repeating-linear-gradient(90deg,
      rgb(255 236 190 / 0.10) 0px, rgb(255 236 190 / 0.10) 0.8px,
      rgb(60 40 12 / 0.10) 0.8px, rgb(60 40 12 / 0.10) 2.2px),
    linear-gradient(180deg, var(--gold-400), var(--gold-300) 40%, var(--gold-500) 100%);
  box-shadow: inset 0 0 22px rgb(0 0 0 / 0.42);
  backface-visibility: hidden;
  opacity: clamp(0, calc((0.74 - var(--open)) * 6), 1);
}

/* head: the top edge of the block. Board edge, paper, board edge — this
   strip is what communicates thickness, and that the pages are paper. */
.bk__head {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(var(--T) - 10px);
  transform-origin: 50% 0%;
  transform: rotateX(90deg);
  /* It used to be banded in the page's own ivory — the brightest value in
     the scene, in 7px stripes — and from the camera it read as a hard white
     bar across the top of the volume. A real head is a shade darker than a
     page (edges collect light at a grazing angle and dust with it), finely
     striated where the leaves meet, pressed darker against each board, and
     falling off toward the spine. */
  background:
    linear-gradient(180deg,
      var(--color-leather-high) 0px, var(--color-leather-base) 7px,
      rgb(0 0 0 / 0) 7px, rgb(0 0 0 / 0) calc(100% - 7px),
      var(--color-leather-base) calc(100% - 7px), var(--color-leather-high) 100%),
    linear-gradient(180deg,
      rgb(40 28 14 / 0.42) 7px, rgb(40 28 14 / 0) 24%,
      rgb(40 28 14 / 0) 76%, rgb(40 28 14 / 0.42) calc(100% - 7px)),
    repeating-linear-gradient(180deg,
      rgb(92 70 40 / 0.2) 0px, rgb(92 70 40 / 0.2) 0.8px,
      rgb(0 0 0 / 0) 0.8px, rgb(0 0 0 / 0) 2.1px,
      rgb(255 248 232 / 0.1) 2.1px, rgb(255 248 232 / 0.1) 2.6px,
      rgb(0 0 0 / 0) 2.6px, rgb(0 0 0 / 0) 3.4px),
    linear-gradient(90deg,
      var(--gold-500) 0%, var(--gold-400) 18%,
      var(--gold-300) 62%, var(--gold-400) 90%, var(--gold-500) 100%);
  box-shadow: inset 0 0 26px rgb(0 0 0 / 0.42);
  /* The block does not go anywhere while the board opens, so neither does
     its head: it was fading from the first degree of the swing, and at
     half-open hung in the air as a translucent strip. It holds until the
     spread has taken over, and reaches a true zero so it stops painting. */
  opacity: clamp(0, calc((0.74 - var(--open)) * 6), 1);
}

/* tail: the bottom edge of the block, visible because we look down on it */
.bk__tail {
  /* The camera looks down on the volume, so the tail faces away. It was
     being painted regardless — a full-width element with a repeating
     gradient, doing the head's job from the wrong side. */
  display: none;
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: calc(var(--T) - 10px);
  transform-origin: 50% 100%;
  transform: rotateX(-90deg);
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.1), rgb(0 0 0 / 0.42)),
    repeating-linear-gradient(0deg,
      var(--color-paper-fold) 0px, var(--color-paper-fold) 1px,
      var(--color-paper-deep) 1px, var(--color-paper-deep) 2.4px);
  box-shadow: inset 0 0 22px rgb(0 0 0 / 0.5);
}

/* ---------- the two open surfaces ----------
   Note: no opacity on this group. A grouping property here would
   force transform-style to flat and collapse every leaf onto one
   plane. The spread fades in through its children instead. */
.bk__spread {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* The stack under each live page.
   These are almost entirely occluded — all you ever see of one is the
   few pixels of edge that perspective exposes beyond the page on top.
   They used to be full-size sheets carrying a gradient, a blended grain
   overlay and a shadow each, and eighteen of them inside a preserve-3d
   scene that cannot be layer-cached measured ~100ms a frame. Now each is
   just the edge it contributes. */
.bk__leaf {
  position: absolute;
  /* only the few pixels of edge below the page show: gilt, like the head.
     Sized to that band (the foot 8 % of the page) rather than a full
     sheet clipped to it: a clip-path on a member of the 3D scene cost two
     offscreen passes per leaf, every frame — 12 for the six (Phase 5.7). */
  top: calc(1.2% + 97.6% * 0.92);
  height: calc(97.6% * 0.08);
  width: var(--W);
  background: linear-gradient(160deg, var(--gold-300), var(--gold-400) 60%, var(--gold-500));
}


.bk__leaf--l { left: 0; border-radius: var(--radius-2) 0 0 var(--radius-2); }
.bk__leaf--r { left: var(--W); border-radius: 0 var(--radius-2) var(--radius-2) 0; }

/* live content surfaces sit on top of their stacks */
.bk__page {
  position: absolute;
  top: 1.2%;
  height: 97.6%;
  width: var(--W);
  overflow: hidden;
  container-type: inline-size;
  background-image:
    var(--grain-paper),
    radial-gradient(130% 100% at 0% 50%, rgb(120 96 60 / 0.16), rgb(0 0 0 / 0) 34%),
    linear-gradient(165deg, var(--color-paper-leaf) 0%, var(--color-paper-fold) 62%, var(--color-paper-edge) 100%);
  background-size: 140px 140px, auto, auto;
  box-shadow: inset 0 0 90px rgb(120 96 60 / 0.16);
  pointer-events: auto;
}
.bk__page--l {
  left: 0;
  border-radius: var(--radius-2) 0 0 var(--radius-2);
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 6px + var(--leftz, 10px)));
  background-image:
    var(--grain-paper),
    radial-gradient(130% 100% at 100% 50%, rgb(120 96 60 / 0.16), rgb(0 0 0 / 0) 34%),
    linear-gradient(195deg, var(--color-paper-leaf) 0%, var(--color-paper-fold) 62%, var(--color-paper-edge) 100%);
}
.bk__page--r {
  left: var(--W);
  border-radius: 0 var(--radius-2) var(--radius-2) 0;
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 6px + var(--rightz, 40px)));
}

/* The lifting board's shadow on the first page: deepest at the fore-edge,
   where the board is still closest, open toward the hinge. Opacity only,
   and out of the scene once the board has cleared. */
.bk__lidshade {
  position: absolute;
  top: 1.2%;
  left: var(--W);
  width: var(--W);
  height: 97.6%;
  background: linear-gradient(90deg, rgb(6 5 4 / 0.55) 0%, rgb(6 5 4 / 0.85) 70%, rgb(6 5 4 / 0.95) 100%);
  border-radius: 0 var(--radius-2) var(--radius-2) 0;
  opacity: 0;
  pointer-events: none;
}

/* gutter — the shadowed valley at the spine */
.bk__gutter {
  position: absolute;
  top: 0;
  left: calc(var(--W) - 9%);
  width: 18%;
  height: 100%;
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 44px));
  /* the valley at the spine and, either side, the paper curving up out
     of it and catching the light (Phase 5.4) */
  background:
    linear-gradient(90deg,
      rgb(28 18 10 / 0) 0%,
      rgb(255 248 230 / 0.10) 16%,
      rgb(28 18 10 / 0.10) 30%,
      rgb(28 18 10 / 0.34) 42%,
      rgb(14 8 4 / 0.66) 50%,
      rgb(28 18 10 / 0.34) 58%,
      rgb(28 18 10 / 0.10) 70%,
      rgb(255 248 230 / 0.10) 84%,
      rgb(28 18 10 / 0) 100%);
  opacity: clamp(0, calc((var(--open) - 0.6) * 3), 1);
  pointer-events: none;
}

/* spine — present when closed, folds away as the spread flattens */
.bk__spine {
  position: absolute;
  top: 0;
  left: var(--W);
  width: var(--T);
  height: 100%;
  transform-origin: 0% 50%;
  transform: translate3d(0, 0, calc(var(--T) * -0.5)) rotateY(-90deg);
  border-radius: var(--radius-2);
  background-image:
    var(--grain-leather),
    /* the round of the spine: dark at both joints, lit across the crown */
    linear-gradient(90deg,
      rgb(0 0 0 / 0.72) 0%,
      var(--color-leather-high) 16%,
      var(--color-leather-lit) 34%,
      var(--color-leather-high) 56%,
      var(--color-leather-mid) 80%,
      rgb(0 0 0 / 0.72) 100%),
    linear-gradient(180deg, var(--color-leather-mid), var(--color-leather-base) 40%, var(--color-leather-deep));
  background-size: 168px 168px, auto, auto;
  box-shadow: inset 0 0 24px rgb(0 0 0 / 0.65);
  opacity: calc(1 - var(--open) * 0.82);
}
/* raised bands across the spine, as on a bound volume */
.bk__spine::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Five raised bands, as on a sewn binding: each gets a lit crown and a
     shadow beneath, so the spine reads as manufactured rather than as a
     surface with lines printed on it. */
  background: repeating-linear-gradient(
    180deg,
    rgb(0 0 0 / 0)            0px,
    rgb(0 0 0 / 0)            calc(var(--H) * 0.152),
    rgb(0 0 0 / 0.55)         calc(var(--H) * 0.152),
    var(--color-leather-lit)  calc(var(--H) * 0.158),
    var(--color-leather-high) calc(var(--H) * 0.168),
    var(--color-leather-mid)  calc(var(--H) * 0.176),
    rgb(0 0 0 / 0.72)         calc(var(--H) * 0.182),
    rgb(0 0 0 / 0)            calc(var(--H) * 0.19)
  );
}

/* ============================================================
   THE TURNING LEAF
   Nested vertical strips. Each strip adds a small rotation to the
   one before it, so the surface bends along an arc instead of
   pivoting like a card.
   ============================================================ */

.bk__turn {
  position: absolute;
  top: 1.2%;
  left: var(--W);
  width: var(--W);
  height: 97.6%;
  transform-origin: 0% 50%;
  transform-style: preserve-3d;
  transform: translate3d(0, 0, var(--turnz, 30px)) rotateY(var(--turn-angle, 0deg));
  opacity: var(--turn-on, 0);
  pointer-events: none;
  will-change: transform, opacity;
}

.bk__strip {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: var(--strip-w);
  transform-origin: 0% 50%;
  transform-style: preserve-3d;
}
/* The facet's flat surface (Phase 5.7, book.js): paper, shading, front
   and back in one plane, one offscreen pass per facet. */
.bk__face {
  position: absolute;
  inset: 0;
  transform-style: flat;
  /* The paper, printed continuously across the whole leaf and slid
     sideways by `--strip-bg-x` so the subdivision is invisible. It is
     never written after build: the shading of the facet is two children
     (`.bk__lit`) that only change opacity. */
  background-image:
    linear-gradient(180deg, rgb(0 0 0 / 0.05), rgb(0 0 0 / 0) 10%, rgb(0 0 0 / 0) 90%, rgb(0 0 0 / 0.07)),
    linear-gradient(165deg, var(--color-paper-leaf), var(--color-paper-fold) 60%, var(--color-paper-edge));
  background-size: var(--W) 100%, var(--W) 100%;
  background-position: var(--strip-bg-x, 0) 0, var(--strip-bg-x, 0) 0;
  /* no seam treatment here: a border or inset shadow on each strip
     would print the subdivision onto the paper */
}

/* ---------- the shading of each facet ----------
   Interpolated ACROSS the strip, from its left boundary's value to its
   right one's — a flat tint per strip gives the curl a low-poly fan you
   can count the facets of. A boundary is either catching the light or in
   shadow, never both.

   History: a child written every frame (fourteen layers repainting per
   frame); then the strip's own background written every frame (no extra
   layers, but all fourteen strips — print included — re-rastered on
   every frame of a turn). Now two fixed ramps per strip whose opacity
   carries the value: composited, never repainted. */
.bk__face > .bk__lit {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  /* The strip's full width, overlap included. Measured at two points of
     a turn (Phase 5.4): narrower leaves an antialiased gap — a light
     hairline at every facet — and wider doubles the shade — a dark one.
     At 100% the neighbour covers the overlap exactly and both vanish. */
  width: 100%;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  background-image: linear-gradient(90deg, rgb(46 32 16), rgb(46 32 16 / 0));
}
.bk__face > .bk__lit.is-sheen {
  background-image: linear-gradient(90deg, rgb(255 249 233), rgb(255 249 233 / 0));
}
.bk__face > .bk__lit--r {
  background-image: linear-gradient(270deg, rgb(46 32 16), rgb(46 32 16 / 0));
}
.bk__face > .bk__lit--r.is-sheen {
  background-image: linear-gradient(270deg, rgb(255 249 233), rgb(255 249 233 / 0));
}

/* ---------- the front of the turning leaf ----------
   A window the width of the strip onto one copy of the page. The copy is
   the same size as a live page and a container of its own, so its `cqw`
   type measures exactly as it did on the page it was copied from. */
.bk__face > .bk__recto {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  overflow: hidden;
  /* the recto faces the reader until the leaf passes upright */
  opacity: calc(1 - var(--turn-face, 0));
  pointer-events: none;
}
.bk__recto__page {
  position: absolute;
  top: 0;
  width: var(--W);
  height: 100%;
  container-type: inline-size;
}
.bk__recto__page > .pg { transition: none; }

/* ---------- the back of the turning leaf ----------

   Furniture, not a facsimile: two hairlines where a printed page has
   them, a running head and a folio at the outer margin. That is what a
   verso in a reference volume looks like when you catch it mid-turn,
   and it is enough to stop the leaf reading as blank card.

   The rules are background gradients, so they are identical in every
   strip and therefore continuous across the leaf without any per-strip
   arithmetic. The type is in the last strip because that is the facet
   the outer margin falls on. Nothing is written here after build. */
.bk__face > .bk__verso {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  /* the same 1.5px overlap the strips themselves carry, for the same
     reason: without it the facet boundaries print as a fan of hairlines
     across the back of the leaf */
  width: calc(100% + 1.5px);
  /* NOT `rotateY(180deg)` + `backface-visibility: hidden`. That is the
     textbook way to put something on the back of a 3D surface, and in
     this particular chain it culls wrong: fourteen strips nested inside
     each other inside a perspective, and the child's computed facing
     comes out back-facing exactly when the leaf has turned over. Tested
     with a flat tint — the verso vanished at −164 degrees, which is the
     one moment it has to be there.

     So the face is explicit instead. `--turn-face` is written by the
     book when the leaf passes its halfway point — twice per turn, not
     per frame — and `scaleX(-1)` undoes the mirroring the strip's own
     rotation would otherwise put on the type.

     Never quite 0 before the flip. At 0 the compositor does not draw the
     verso, so it was first rastered in the frame the leaf passed upright:
     the 33 ms frame at 0.94 of every chapter act (Phase 4.7 ablation,
     mains: gone in all four runs at 0.01). At 1 % over the recto it is
     invisible, and it is ready when the leaf turns over. */
  transform: scaleX(-1);
  opacity: max(var(--turn-face, 0), 0.01);
  transition: opacity 90ms linear;
  background-image:
    /* head rule */
    linear-gradient(180deg,
      rgb(0 0 0 / 0) 0 11.4%,
      var(--color-rule-leaf) 11.4% calc(11.4% + 1px),
      rgb(0 0 0 / 0) calc(11.4% + 1px)),
    /* foot rule */
    linear-gradient(180deg,
      rgb(0 0 0 / 0) 0 88.2%,
      var(--color-rule-leaf) 88.2% calc(88.2% + 1px),
      rgb(0 0 0 / 0) calc(88.2% + 1px)),
    /* the paper, a shade cooler than the recto: it is facing away from
       the key light for most of the turn. Vertical only (Phase 5.3): the
       verso is painted per strip, and an angled gradient repeated in each
       of fourteen strips printed fourteen vertical bands across the leaf
       as it landed. A vertical one is the same in every strip. */
    linear-gradient(180deg, var(--color-paper-fold), var(--color-paper-edge) 72%, var(--color-paper-deep));
  background-size: 100% 100%;
  pointer-events: none;
}
/* A leaf drawn in advance comes into the scene in stages, so no frame
   rasterises all of it (book.js, "the next leaf"): the strips' paper,
   then the printed front strip by strip (inline opacity on each
   .bk__recto), then — when this attribute goes — the back. Opacity 0 is
   what keeps a surface out of the raster until its turn. */
.bk__turn[data-warm] .bk__face > .bk__verso { opacity: 0; }

/* The outer margin of the verso, and it stays INSIDE its own facet —
   one fourteenth of the leaf, about forty pixels. Anything wider than
   that overhangs the free edge and floats off the paper, which is the
   same failure as putting the print on a flat panel.

   Forty pixels will not hold a running head across the page, so the
   running head runs UP the margin instead. That is real furniture — it
   is how a reference volume sets a running head in a narrow measure —
   and it is the one place on this page where the type does something
   you would notice. The folio sits at the foot of the same column. */
.bk__verso__edge {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 11.4% 0 9%;
  overflow: hidden;
}
.bk__verso__run {
  writing-mode: vertical-rl;
  font-family: var(--font-meta);
  font-weight: 500;
  /* no `cqw` here: the nearest container is outside the 3D scene, and
     giving `.bk__turn` one would mean containment on a preserve-3d
     ancestor. The volume is scaled by transform anyway, so plain
     viewport-relative sizes ride with it. */
  font-size: clamp(6px, 0.62vw, 10px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  white-space: nowrap;
  /* it must not grow past the rules at head and foot */
  max-height: 62%;
  overflow: hidden;
}
.bk__verso__folio {
  font-family: var(--font-display);
  font-size: clamp(11px, 1.15vw, 18px);
  line-height: 1;
  color: var(--color-ink-muted);
  font-variant-numeric: lining-nums;
}

/* shadow the turning leaf casts onto the page beneath it */
.bk__castshadow {
  position: absolute;
  top: 1.2%;
  left: var(--W);
  width: var(--W);
  height: 97.6%;
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 42px));
  background: linear-gradient(90deg, rgb(10 6 2 / 0.5), rgb(10 6 2 / 0.16) 38%, rgb(10 6 2 / 0) 72%);
  transform-origin: 0 50%;
  opacity: var(--cast, 0);
  pointer-events: none;
}

/* ============================================================
   WHAT IS PRINTED ON THE PAGE
   ============================================================ */

.pg {
  position: absolute;
  inset: 0;
  padding: 9% 9% 7%;
  display: flex;
  flex-direction: column;
  color: var(--color-ink-strong);
  opacity: 0;
  /* Asymmetric on purpose. The page arriving may take its time; the page
     LEAVING may not, because for part of the turn both are exposed and a
     long cross-fade prints one page's type through the other's. Paper
     does not dissolve. Swapping under the leaf instead of shortening the
     overlap is the real fix and is a beat-map change — see docs/MOTION.md. */
  transition: opacity var(--dur-micro) var(--ease-editorial);
}
.pg.is-on {
  opacity: 1;
  transition-duration: var(--dur-content);
}

.pg__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-4);
  padding-bottom: 5%;
}
.pg__rule { height: 1px; background: var(--color-border-ink); }
.pg__body { flex: 1; display: flex; flex-direction: column; justify-content: center; gap: 4cqw; }
.pg__foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-top: 5%;
}

.pg__chapno {
  font-family: var(--font-display);
  font-size: clamp(3rem, 22cqw, 10rem);
  line-height: 0.82;
  color: var(--color-ink-strong);
  opacity: 0.1;
  letter-spacing: -0.04em;
}

.pg__title {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 7.6cqw, 3.4rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  color: var(--color-ink-strong);
}

.pg__kicker {
  font-family: var(--font-meta);
  font-size: clamp(0.46rem, 1.1cqw, 0.68rem);
  font-weight: 500;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

.pg__prose {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: clamp(0.62rem, 1.55cqw, 0.96rem);
  line-height: 1.78;
  color: var(--color-ink-body);
  max-width: 34ch;
}

.pg__dropcap::first-letter {
  float: left;
  font-family: var(--font-display);
  font-size: 3.1em;
  line-height: 0.82;
  padding: 0.06em 0.12em 0 0;
  color: var(--color-ink-strong);
}

/* a printed ornament — restrained, not decorative noise */
.pg__ornament {
  width: 30%;
  height: 1px;
  background: var(--color-border-ink);
  position: relative;
}
.pg__ornament::after {
  content: '§';
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-paper-fold);
  padding: 0 0.5em;
  font-family: var(--font-display);
  font-size: clamp(0.6rem, 1.6cqw, 1rem);
  color: var(--color-ink-faint);
}

/* ============================================================
   MOBILE — the book becomes a vertical editorial object.
   No fore-edge trickery, no strip curl: a slimmer volume that
   still reads as a physical thing.
   ============================================================ */
@media (max-width: 720px) {
  .bk { --scale: 1; }
  .bk__body { transform: translate3d(0, 0, 0); }
  .bookstage__shadow { width: calc(var(--book-w) * 1.3); }

  /* Only the right-hand page is used; the left board stays shut. */
  .bk { width: var(--W); }
  .bk__block, .bk__cover, .bk__backcover, .bk__page--r, .bk__leaf--r, .bk__turn, .bk__castshadow, .bk__spine, .bk__lidshade { left: 0; }
  .bk__page--l, .bk__leaf--l, .bk__gutter { display: none; }
  .bk__cover { transform: rotateY(calc(-172deg * var(--open))) translate3d(0, 0, calc(var(--T) * 0.5)); }
  .pg { padding: 10% 9%; }
}

/* a page that is not the current one on its surface: laid out, not drawn,
   not a target (see swapPage in book.js) */
.bk__page > .pg:not(.is-on) { pointer-events: none; }

/* The gateway: the type dissolves and the rules stay — they are about to
   become architecture. `--ink` is written on the two page surfaces only
   while the spread is becoming a doorway. */
/* The wash: the page's own paper — same grain, same light, same inset
   shade — over the type, under the rules. */
.pg__wash {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  background-image:
    var(--grain-paper),
    radial-gradient(130% 100% at 0% 50%, rgb(120 96 60 / 0.16), rgb(0 0 0 / 0) 34%),
    linear-gradient(165deg, var(--color-paper-leaf) 0%, var(--color-paper-fold) 62%, var(--color-paper-edge) 100%);
  background-size: 140px 140px, auto, auto;
  box-shadow: inset 0 0 90px rgb(120 96 60 / 0.16);
}
.bk__page--l .pg__wash {
  background-image:
    var(--grain-paper),
    radial-gradient(130% 100% at 100% 50%, rgb(120 96 60 / 0.16), rgb(0 0 0 / 0) 34%),
    linear-gradient(195deg, var(--color-paper-leaf) 0%, var(--color-paper-fold) 62%, var(--color-paper-edge) 100%);
}
.pg__rule { position: relative; z-index: 2; }
.bk__page.is-inking .pg__wash { will-change: opacity; }

/* ---------- light out of the volume, and the floor's reflection (Phase 5.2) ----------
   Outside the 3D scene, like the contact shadow. Opacity and transform
   are written by book.js only when they change. */
.bookstage__glow {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 0;
  width: calc(var(--book-w) * 3.2);
  height: calc(var(--book-h) * 1.5);
  transform: translate3d(-50%, -52%, 0);
  background: radial-gradient(
    50% 50% at 50% 50%,
    rgb(255 206 146 / 0.22) 0%,
    rgb(255 190 120 / 0.11) 30%,
    rgb(255 180 110 / 0.04) 55%,
    rgb(255 180 110 / 0) 75%
  );
  opacity: 0;
  will-change: opacity;
  pointer-events: none;
}
.bookstage__refl {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 0;
  width: var(--book-w);
  height: calc(var(--book-h) * 0.34);
  transform: translate3d(-50%, calc(var(--book-h) * 0.47), 0) scaleX(1);
  transform-origin: 50% 0;
  background:
    linear-gradient(180deg, rgb(226 200 156 / 0.16), rgb(160 130 90 / 0.05) 45%, rgb(0 0 0 / 0) 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  opacity: 0;
  will-change: opacity, transform;
  pointer-events: none;
}

/* ---------- the back of the leaf, printed (Phase 5.3+) ----------
   The next left-hand page, through a window the width of the strip. Once
   it is printed the plain furniture (rules, running head, folio) steps
   aside: the page brings its own. */
.bk__verso__win {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.bk__turn.has-verso .bk__face > .bk__verso {
  background-image: linear-gradient(180deg, var(--color-paper-leaf), var(--color-paper-fold) 70%, var(--color-paper-edge));
}
.bk__turn.has-verso .bk__verso__edge { display: none; }

/* ---------- the ribbon marker and the headband (Phase 5.4) ----------
   Oxblood silk, its sheen running along it; it lies across the right
   page from the head of the spine and its swallow-tailed end hangs out
   below the tail. Comes with the spread, off `--open`. */
.bk__ribbon {
  position: absolute;
  top: -1.5%;
  left: calc(var(--W) * 1.045);
  width: clamp(8px, 0.9vw, 13px);
  height: 104%;
  background:
    linear-gradient(90deg, rgb(0 0 0 / 0.35), rgb(255 210 200 / 0.16) 38%, rgb(255 220 210 / 0.05) 55%, rgb(0 0 0 / 0.3)),
    linear-gradient(180deg, #4a0f14, #6e1820 30%, #5a141b 70%, #3c0b10);
  box-shadow: 1px 2px 3px rgb(20 10 4 / 0.35);
  opacity: 0;
  pointer-events: none;
}
.bk__ribbon__tail {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: clamp(18px, 3.4vh, 34px);
  background: linear-gradient(180deg, #3c0b10, #5a141b);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 72%, 0 100%);
}
.bk__headband {
  position: absolute;
  top: calc(1.2% - 4px);
  left: calc(var(--W) - 9px);
  width: 18px;
  height: 6px;
  border-radius: 3px;
  background: repeating-linear-gradient(90deg, #6e1820 0 2px, var(--gold-300) 2px 4px);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.5);
  opacity: 0;
  transform: translate3d(0, 0, calc(var(--T) * -0.5 + 30px));
  pointer-events: none;
}
@media (max-width: 720px) {
  .bk__ribbon { left: calc(var(--W) * 0.045); }
  .bk__headband { display: none; }
}
