/* ── THE BOOK OBJECT ────────────────────────────────────────────────────────
   A spread drawn as two flat rectangles reads as two pictures, however
   carefully the gutter is shaded. What makes paper read as a book is the
   OBJECT around the picture: a page block with visible thickness, boards that
   overhang it, a spine with a hinge, paper that curves into the fold, and a
   shadow on a surface.

   Two rules learned the hard way on a 412 px phone:
   1. THE CUES MUST BE BIG. A 4 px page block on a 141 px page is invisible.
      Thickness, board overhang and shadow are sized in tens of pixels here,
      not millimetres, because the preview is a drawing, not a photograph.
   2. NO HARD LINE AT THE FOLD. Two page edges meeting produce a seam, and a
      seam reads as a UI divider. The fold is one soft valley drawn OVER both
      pages, plus a single hairline of light where the paper rises.

   Everything is transform/opacity only, and nothing moves unless the visitor
   allows motion. */

.bk-page { position: relative; }

.bk-stage {
  --fw: 320px;          /* one page face, set by norcha-book.js */
  --fh: 320px;
  --spine: 22px;        /* closed book spine */
  --block: 12px;        /* page-block thickness at the tail */
  --tilt: 15deg;        /* how far the open book leans away from you */
  --yaw: -2.2deg;       /* a photographed book is never square to the lens */
  --roll: -1.1deg;      /* and never level */
  position: relative;
  /* width:100% is load-bearing: without it the stage is a flex item sized by
     its own content, so setting --fw makes it wider, which makes --fw wider
     again. On a 412 px phone that ran it to 676 px and off the screen. */
  width: 100%;
  max-width: 100%;
  padding: 14px 2px 34px;
  display: flex;
  justify-content: center;
}

.bk-scene {
  position: relative;
  perspective: 1700px;
  perspective-origin: 50% 36%;
  width: 100%;
  display: flex;
  justify-content: center;
}

.bk-book {
  position: relative;
  transform-style: preserve-3d;
  transform:
    rotateX(calc(var(--tilt) + var(--rx, 0deg)))
    rotateY(calc(var(--ry, 0deg) + var(--yaw)))
    rotateZ(var(--roll));
  transition: transform .45s cubic-bezier(.22, 1, .36, 1);
  will-change: transform;
}

/* the surface: a wide, soft ellipse under the object — the cue that it is
   lying on something rather than floating in a panel */
.bk-ground {
  position: absolute;
  left: 50%;
  bottom: 8px;
  width: calc(100% - 4px);
  max-width: calc(var(--fw) * 2.3);
  height: 40px;
  transform: translateX(-50%) translateZ(-2px) translateY(2px);
  /* light from the top-left: the shadow falls right and down, is darkest where
     the object touches the surface, and dies out well before the edge. A
     symmetric glow under the middle reads as a page highlight, not a shadow. */
  background:
    radial-gradient(ellipse 46% 60% at 52% 62%,
      rgba(38, 26, 14, .52) 0%,
      rgba(38, 26, 14, .22) 46%,
      rgba(38, 26, 14, 0) 74%),
    radial-gradient(ellipse at 54% 58%,
      rgba(52, 38, 24, .34) 0%,
      rgba(52, 38, 24, .16) 42%,
      rgba(52, 38, 24, .05) 64%,
      rgba(52, 38, 24, 0) 80%);
  filter: blur(5px);
  pointer-events: none;
}

/* ── which object is on the table ────────────────────────────────────────── */
.bk-open, .bk-closed { display: none; }
.bk.is-open .bk-open { display: flex; }
.bk.is-closed .bk-closed { display: block; }

/* ── open spread ─────────────────────────────────────────────────────────── */
.bk-open {
  position: relative;
  display: flex;
  align-items: flex-start;
  transform-style: preserve-3d;
  border-radius: 2px;
  /* the board overhangs the page block on every side — 5 px here is a
     proportion, not a measurement, and it is what separates a book from a
     border around a picture */
  padding: 7px 7px 9px;
  border-radius: 3px;
  background: linear-gradient(180deg, #F6EDDD 0%, #E9DDC6 62%, #DCCBAE 100%);
  box-shadow:
    0 0 0 1px rgba(120, 99, 72, .34),
    0 1px 0 rgba(255, 255, 255, .62) inset,
    0 -3px 0 rgba(120, 99, 72, .22) inset,
    0 3px 0 rgba(255, 255, 255, .28) inset,
    0 34px 46px -24px rgba(40, 28, 16, .66);
}

.bk-pageface {
  position: relative;
  display: block;
  width: var(--fw);
  height: var(--fh);
  background: #FFFDF9;
  overflow: hidden;
}
/* THE V. Two pages that are exactly coplanar are two pictures; an open book
   rises from the gutter, so the outer edges sit closer to the viewer. The
   origin is the fold, so the inner edges never move and no gap can appear. */
.bk-pageface-l { transform-origin: right center; transform: rotateY(3.4deg); }
.bk-pageface-r { transform-origin: left center;  transform: rotateY(-3.4deg); }

/* Paper is not a flat colour. Silk stock throws a broad, soft sheen from the
   light side and a faint grain; without those two the page reads as a screen.
   Both are pseudo-elements so the markup and the drawing stay untouched. */
.bk-pageface::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(116deg,
      rgba(255, 255, 255, .62) 0%,
      rgba(255, 255, 255, .30) 20%,
      rgba(255, 255, 255, .05) 40%,
      rgba(255, 255, 255, 0) 56%),
    radial-gradient(ellipse 130% 70% at 8% -4%, rgba(255, 255, 255, .28), rgba(255, 255, 255, 0) 62%);
  mix-blend-mode: soft-light;
}
.bk-pageface::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: .16;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 140px 140px;
}
.bk-pageface canvas { display: block; width: 100%; height: 100%; }

/* the paper curves into the fold: a soft shade on the inner 22 % of each
   page, peaking a few pixels AWAY from the join and fading at the join */
.bk-curl {
  position: absolute;
  top: 0;
  width: 34%;
  height: 100%;
  pointer-events: none;
}
.bk-curl-r {
  right: 0;
  background: linear-gradient(to right,
    rgba(56, 41, 24, 0) 0%,
    rgba(56, 41, 24, .05) 42%,
    rgba(56, 41, 24, .13) 68%,
    rgba(56, 41, 24, .30) 90%,
    rgba(56, 41, 24, .12) 100%);
}
.bk-curl-l {
  left: 0;
  background: linear-gradient(to left,
    rgba(56, 41, 24, 0) 0%,
    rgba(56, 41, 24, .05) 42%,
    rgba(56, 41, 24, .13) 68%,
    rgba(56, 41, 24, .30) 90%,
    rgba(56, 41, 24, .12) 100%);
}

/* the board's inner lip, and the shadow it throws on the paper */
.bk-edge {
  position: absolute;
  top: 0;
  width: 2px;
  height: 100%;
  pointer-events: none;
}
.bk-edge-l {
  left: 0;
  background: rgba(90, 72, 50, .22);
  box-shadow: -1px 0 0 rgba(255, 255, 255, .55), 3px 0 7px -3px rgba(40, 30, 18, .45);
}
.bk-edge-r {
  right: 0;
  background: rgba(90, 72, 50, .22);
  box-shadow: 1px 0 0 rgba(255, 255, 255, .55), -3px 0 7px -3px rgba(40, 30, 18, .45);
}

/* THE CREASE. Two printed pages in a bound book do not become one surface:
   there is a dark line where they meet, ambient occlusion either side of it,
   and a lit paper edge just before the fall into the binding. The earlier
   version blurred the fold into a smooth valley — physically tidier, and it
   read as one page with a shadow down the middle. This is a crease you could
   put a fingernail into.
   The band is wider than the line so the occlusion has somewhere to live;
   the line itself is under a pixel of blur, which is what keeps it paper
   rather than vector. */
.bk-gutter {
  position: absolute;
  left: 50%;
  top: 5px;
  width: calc(var(--fw) * .06 + 26px);
  height: calc(var(--fh));
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 4;
  background:
    /* the lit inner edges, then the crease itself */
    linear-gradient(to right,
      rgba(255, 253, 246, 0) 41%,
      rgba(255, 253, 246, .34) 45.6%,
      rgba(58, 42, 24, .30) 48.2%,
      rgba(30, 21, 11, .74) 50%,
      rgba(58, 42, 24, .30) 51.8%,
      rgba(255, 253, 246, .34) 54.4%,
      rgba(255, 253, 246, 0) 59%),
    /* occlusion that spreads out of the binding onto both pages */
    radial-gradient(ellipse 46% 100% at 50% 50%,
      rgba(36, 26, 14, .40) 0%,
      rgba(36, 26, 14, .20) 34%,
      rgba(36, 26, 14, .06) 62%,
      rgba(36, 26, 14, 0) 84%);
  filter: blur(.7px);
}

/* The binding seen through the crease: the paper stack runs vertically here,
   which is the one place it is visible on an open book. */
.bk-gutter::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: calc(var(--block) * .9);
  transform: translateX(-50%);
  opacity: .5;
  background: repeating-linear-gradient(to bottom,
    rgba(255, 253, 248, .55) 0px, rgba(255, 253, 248, .55) 1px,
    rgba(58, 42, 24, .5) 1px, rgba(58, 42, 24, .5) 2px);
  mix-blend-mode: multiply;
  filter: blur(.4px);
}

/* the page block: stacked sheets at the tail. The stripes are the whole cue —
   without them a thick edge is just a beige bar. */
.bk-block {
  position: absolute;
  left: 5px;
  right: 5px;
  bottom: calc(var(--block) * -1 + 2px);
  height: var(--block);
  border-radius: 0 0 6px 6px;
  /* the stack is denser in the middle (the spine pulls it tight) and each
     sheet is a slightly different warm white, which is what a stack of paper
     looks like edge-on. Two stripe periods overlaid make it irregular. */
  background:
    linear-gradient(to right,
      rgba(58, 42, 24, .26) 0%,
      rgba(58, 42, 24, .04) 12%,
      rgba(58, 42, 24, 0) 30%,
      rgba(58, 42, 24, 0) 70%,
      rgba(58, 42, 24, .04) 88%,
      rgba(58, 42, 24, .26) 100%),
    repeating-linear-gradient(to bottom,
      #FFFDF8 0px, #FFFDF8 1.5px,
      #C7BAA2 1.5px, #D8CCB6 3px);
  box-shadow:
    0 1px 0 rgba(120, 99, 72, .55),
    0 6px 10px -6px rgba(48, 35, 22, .55);
  pointer-events: none;
}
/* fore-edges: the same stack seen edge-on down each side */
.bk-open::before,
.bk-open::after {
  content: "";
  position: absolute;
  top: 5px;
  bottom: calc(var(--block) - 2px);
  width: calc(4px + var(--block) * .35);
  background: repeating-linear-gradient(to bottom,
    #FFFDF8 0px, #FFFDF8 1px,
    #D3C7B1 1px, #D3C7B1 2px);
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(120, 99, 72, .18);
}
.bk-open::before { left: 0; border-radius: 2px 0 0 2px; }
.bk-open::after { right: 0; border-radius: 0 2px 2px 0; }

/* ── closed cover ────────────────────────────────────────────────────────── */
.bk-closed {
  position: relative;
  width: calc(var(--fw) + var(--spine) + var(--block) * .6);
  height: calc(var(--fh) + var(--block) * .5);
  transform-style: preserve-3d;
  transform: rotateY(-15deg) rotateX(5deg);
  transition: transform .5s cubic-bezier(.22, 1, .36, 1), opacity .35s ease;
  border-radius: 2px 4px 4px 2px;
}
.bk-c-face {
  position: absolute;
  top: 0;
  bottom: calc(var(--block) * .5);
  left: var(--spine);
  right: calc(var(--block) * .6);
  display: block;
  overflow: hidden;
  border-radius: 1px 3px 3px 1px;
  box-shadow:
    0 0 0 1px rgba(120, 99, 72, .22),
    0 26px 40px -24px rgba(48, 35, 22, .7);
}
.bk-c-face canvas { display: block; width: 100%; height: 100%; }

/* the spine: a rounded back, a hinge groove, and the art bled under it */
.bk-c-spine {
  position: absolute;
  left: 0;
  top: 0;
  bottom: calc(var(--block) * .5);
  width: var(--spine);
  border-radius: 3px 0 0 3px;
  background:
    linear-gradient(to right,
      rgba(255, 255, 255, .34) 0%,
      rgba(0, 0, 0, .05) 14%,
      rgba(0, 0, 0, .20) 44%,
      rgba(0, 0, 0, .36) 74%,
      rgba(255, 255, 255, .14) 100%);
  box-shadow: inset 0 0 0 1px rgba(40, 30, 18, .20);
  overflow: hidden;
}
.bk-c-hinge {
  position: absolute;
  right: 3px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(28, 20, 12, .42);
  box-shadow: -1px 0 0 rgba(255, 255, 255, .40);
}

/* the page block behind the cover: the book's thickness, seen on the right
   and along the bottom. The strongest "it is an object" cue there is. */
.bk-c-block {
  position: absolute;
  top: 4px;
  bottom: 2px;
  right: 0;
  width: calc(var(--block) * .6);
  border-radius: 0 2px 2px 0;
  background: repeating-linear-gradient(to right,
    #FFFDF8 0px, #FFFDF8 1px,
    #CFC3AC 1px, #CFC3AC 2px);
  box-shadow: 3px 0 8px -4px rgba(48, 35, 22, .6);
}
.bk-closed::after {
  content: "";
  position: absolute;
  left: var(--spine);
  right: 0;
  bottom: 0;
  height: calc(var(--block) * .5);
  border-radius: 0 0 3px 2px;
  background: repeating-linear-gradient(to bottom,
    #FFFDF8 0px, #FFFDF8 1px,
    #CFC3AC 1px, #CFC3AC 2px);
  box-shadow: 0 2px 6px -2px rgba(48, 35, 22, .55);
}

/* one soft sheen across the board — light from the upper left */
.bk-c-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 2px 4px 4px 2px;
  background: linear-gradient(118deg,
    rgba(255, 255, 255, .30) 0%,
    rgba(255, 255, 255, .07) 30%,
    rgba(255, 255, 255, 0) 52%,
    rgba(0, 0, 0, .12) 100%);
  mix-blend-mode: soft-light;
}

/* ── the page turn ───────────────────────────────────────────────────────── */
.bk-flip {
  position: absolute;
  top: 5px;
  left: 50%;
  width: var(--fw);
  height: var(--fh);
  transform-origin: left center;
  transform-style: preserve-3d;
  transform: rotateY(0deg);
  pointer-events: none;
  z-index: 5;
}
.bk-flip.is-flipping {
  /* a hinged sheet is not a rotating rectangle: it lifts off the block in the
     middle of the turn and settles back down. The keyframe carries the whole
     move, so the lift and the rotation share one curve. */
  animation: bk-turn .64s cubic-bezier(.62, .03, .36, 1) both;
}
@keyframes bk-turn {
  0%   { transform: rotateY(0deg) rotateX(0deg) translateZ(0); }
  38%  { transform: rotateY(-74deg) rotateX(2.2deg) translateZ(10px); }
  72%  { transform: rotateY(-148deg) rotateX(-1.1deg) translateZ(4px); }
  100% { transform: rotateY(-176deg) rotateX(0deg) translateZ(0); }
}
.bk-ff {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(60, 46, 30, .12);
}
.bk-ff canvas { display: block; width: 100%; height: 100%; }
.bk-ff-back {
  transform: rotateY(180deg);
  /* the reverse of a printed sheet: paper, a hint of show-through, a shadow
     where it lifts. A mirrored copy of the photo reads as a bug. */
  background:
    linear-gradient(to left, rgba(60, 46, 30, .18), rgba(60, 46, 30, 0) 42%),
    #FFFDF8;
}
/* the sheet darkens as it lifts, then lightens as it lands */
.bk-flip.is-flipping::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to right,
    rgba(40, 30, 18, .40),
    rgba(40, 30, 18, .04) 55%,
    rgba(255, 255, 255, .12));
  opacity: 0;
  animation: bk-shade .62s ease-in-out;
  pointer-events: none;
}
@keyframes bk-shade {
  0% { opacity: 0; }
  38% { opacity: .85; }
  100% { opacity: 0; }
}

/* ── arriving ────────────────────────────────────────────────────────────── */
@keyframes bk-rise {
  from { opacity: 0; transform: translateY(12px) scale(.975); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.bk-enter { animation: bk-rise .46s cubic-bezier(.22, 1, .36, 1) both; }

/* Arriving at the object: the spread unfolds from the spine, the closed book
   settles onto the table. Display changes restart these, which is exactly when
   they should run. */
.bk.is-open .bk-open { animation: bk-unfold .58s cubic-bezier(.22, 1, .36, 1) both; }
.bk.is-closed .bk-closed { animation: bk-settle .5s cubic-bezier(.22, 1, .36, 1) both; }
@keyframes bk-unfold {
  from { opacity: 0; transform: rotateX(-16deg) scale(.955); }
  to   { opacity: 1; transform: rotateX(0deg) scale(1); }
}
@keyframes bk-settle {
  from { opacity: 0; transform: translateY(-6px) scale(.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* The strip arrives with the object, a page at a time. Stagger is capped at
   eight items so a 40-page book does not wait four seconds to be usable. */
/* Only when the strip changes length — the list is rebuilt on every selection,
   and animating it on each rebuild would make the strip twitch under the thumb
   while someone is paging through their own book. */
#bkList.is-enter .bk-item { animation: bk-item-in .34s cubic-bezier(.22, 1, .36, 1) both; }
#bkList.is-enter .bk-item:nth-child(1) { animation-delay: 0ms; }
#bkList.is-enter .bk-item:nth-child(2) { animation-delay: 34ms; }
#bkList.is-enter .bk-item:nth-child(3) { animation-delay: 68ms; }
#bkList.is-enter .bk-item:nth-child(4) { animation-delay: 102ms; }
#bkList.is-enter .bk-item:nth-child(5) { animation-delay: 136ms; }
#bkList.is-enter .bk-item:nth-child(6) { animation-delay: 170ms; }
#bkList.is-enter .bk-item:nth-child(7) { animation-delay: 204ms; }
#bkList.is-enter .bk-item:nth-child(n+8) { animation-delay: 238ms; }
@keyframes bk-item-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .bk-book,
  .bk-closed { transition: none; }
  .bk-enter,
  .bk.is-open .bk-open,
  .bk.is-closed .bk-closed,
  #bkList.is-enter .bk-item,
  .bk-flip.is-flipping { animation: none; }
  .bk-flip { display: none !important; }
  .bk-flip.is-flipping::after { animation: none; }
}

/* the flat canvas is the print proof and the test target; the 3D object is
   what a person looks at. Both are painted, only one is shown. */
body.book-3d .bk-flat {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  clip-path: inset(50%);
}
