/* ==========================================================================
   Interactive journal: About page
   A real book: leaves hinge at the spine and turn on scroll or drag.
   ========================================================================== */

.journal-section {
  margin: var(--space-12) 0 var(--space-16);

  /* The star used as a list marker, as an SVG mask so it takes the page's ink
     colour. Held here because url() in a custom property resolves against the
     stylesheet, and a data URI sidesteps that entirely. Filled rather than
     stroked: at three quarters of an em a stroked outline turns to mush. */
  --page-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M12 2.2l2.98 6.5 7.02.82-5.2 4.82 1.42 6.98L12 17.72 5.78 21.32l1.42-6.98L2 9.52l7.02-.82z'/%3E%3C/svg%3E");
}

.journal-intro {
  max-width: 640px;
  margin-bottom: var(--space-6);
}

.journal-intro p {
  color: var(--color-text-secondary);
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   The desk: background photo sits here. Drop a file at
   assets/images/journal-bg.jpg and it takes over from the gradient below.
   -------------------------------------------------------------------------- */

/* --journal-bg is set inline on .journal once a background photo exists;
   until then the gradient below stands in on its own. */
.journal {
  position: relative;
  padding: clamp(var(--space-5), 4vw, var(--space-16)) clamp(var(--space-4), 3vw, var(--space-12));
  border-radius: var(--radius-xl);
  overflow: hidden;
  background-color: #dfe8e2;
  background-image:
    var(--journal-bg, none),
    linear-gradient(170deg, #cfe0d6 0%, #b8cfc4 40%, #8fb3ad 100%);
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}

/* A light scrim: enough to seat the book, not enough to hide the view. */
.journal::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(75% 60% at 50% 48%, rgba(20, 30, 24, 0.16) 0%, rgba(20, 30, 24, 0) 72%),
    linear-gradient(180deg, rgba(20, 30, 24, 0.08) 0%, rgba(20, 30, 24, 0) 40%, rgba(20, 30, 24, 0.14) 100%);
}

/* --------------------------------------------------------------------------
   Book shell
   -------------------------------------------------------------------------- */

.book-stage {
  position: relative;
  z-index: 1;
  perspective: 2400px;
  perspective-origin: 50% 45%;
  max-width: 940px;
  margin: 0 auto;
  touch-action: pan-y;
}

/* The book is the paper itself: no heavy cover framing it. A thin warm rim
   and a deep soft shadow are enough to lift it off the backdrop. */
.book {
  position: relative;
  aspect-ratio: 2 / 1.32;
  transform-style: preserve-3d;
  border-radius: 3px;
  padding: 0;
  background: #efe9dc;
  box-shadow:
    0 0 0 1px rgba(120, 106, 84, 0.14),
    0 40px 80px -30px rgba(38, 34, 26, 0.5),
    0 10px 24px -12px rgba(38, 34, 26, 0.3);
}

/* No binding hardware: the gutter shadow alone carries the fold. */

.book-block {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  /* leaves rotate out of this box by design, so nothing is clipped and the
     fold stays visible through the whole turn */
  border-radius: 3px;
  background: #f7f3e9;
  /* the stacked edge of the paper block, seen from the fore-edge */
  box-shadow:
    2px 0 0 -1px #f2ecdd,
    4px 0 0 -2px #eae2ce,
    6px 0 0 -3px #e0d6bf;
}

/* the spine shadow, drawn over everything, soft, like a real gutter */
.book-gutter {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 78px;
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 30;
  background: linear-gradient(90deg,
    rgba(120, 106, 84, 0) 0%,
    rgba(120, 106, 84, 0.07) 32%,
    rgba(120, 106, 84, 0.20) 50%,
    rgba(120, 106, 84, 0.07) 68%,
    rgba(120, 106, 84, 0) 100%);
}

/* --------------------------------------------------------------------------
   Pages and leaves
   -------------------------------------------------------------------------- */

.page {
  position: absolute;
  top: 0;
  height: 100%;
  width: 50%;
  overflow: hidden;
  background-color: #f7f3e9;
  /* aged paper: warm wash + fibre noise */
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 60%),
    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='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}

.page-static-left { left: 0; z-index: 1; }
.page-static-right { right: 0; z-index: 0; }

/* inner shadow falling away from the spine */
.page-left-face::after,
.page-right-face::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 46px;
  pointer-events: none;
}

.page-left-face::after {
  right: 0;
  background: linear-gradient(270deg, rgba(120, 106, 84, 0.16), rgba(120, 106, 84, 0));
}

.page-right-face::after {
  left: 0;
  background: linear-gradient(90deg, rgba(120, 106, 84, 0.16), rgba(120, 106, 84, 0));
}

/* --shade / --cast / --hover are written per frame by journal.js so the
   lighting tracks the fold continuously instead of stepping between states. */
.leaf {
  position: absolute;
  top: 0.6%;
  left: 50%;
  width: 49.4%;
  height: 98.8%;
  --shade: 0;
  --cast: 0;
  --cast-side: 1;
  --hover: 0px;
  transform-style: preserve-3d;
  transform-origin: left center;
  will-change: transform;
  /* a hair of thickness, so the turning sheet isn't a zero-width plane */
  translate: 0 0 var(--hover);
}

.leaf-face {
  position: absolute;
  inset: 0;
  width: 100%;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  background-color: #f7f3e9;
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 60%),
    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='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}

.leaf-face-back {
  transform: rotateY(180deg);
}

/* Gradient along the spine edge: the curl of paper catching light as it lifts.
   Opacity is bound to --shade, so it swells at the top of the arc and fades as
   the sheet lies flat again. */
.leaf-face::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 20;
  opacity: var(--shade);
}

.leaf-face-front::before {
  background: linear-gradient(90deg,
    rgba(74, 64, 46, 0.42) 0%,
    rgba(74, 64, 46, 0.14) 14%,
    rgba(255, 252, 244, 0.16) 34%,
    rgba(74, 64, 46, 0) 60%);
}

.leaf-face-back::before {
  background: linear-gradient(270deg,
    rgba(74, 64, 46, 0.42) 0%,
    rgba(74, 64, 46, 0.14) 14%,
    rgba(255, 252, 244, 0.16) 34%,
    rgba(74, 64, 46, 0) 60%);
}

/* The shadow the lifted leaf throws across the spread underneath it. Sits on
   the leaf's own plane but scales/fades with the turn, reading as cast light. */
.leaf::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  pointer-events: none;
  z-index: 1;
  opacity: var(--cast);
  transform: scaleX(var(--cast-side));
  transform-origin: left center;
  background: linear-gradient(90deg,
    rgba(58, 50, 36, 0.34) 0%,
    rgba(58, 50, 36, 0.14) 26%,
    rgba(58, 50, 36, 0) 62%);
}

.page-inner {
  position: relative;
  z-index: 10;
  height: 100%;
  padding: clamp(var(--space-5), 3.4vw, var(--space-10));
}

/* The journal sits inside .case-study-body, whose two-class rules outrank the
   single-class rules below and impose article styling on a page: a 680px
   measure and a bottom margin on every paragraph. Reset it once here so each
   page element can set its own measure. */
.page-inner p,
.page-inner ul,
.page-inner li {
  max-width: none;
  margin-bottom: 0;
}

/* `.case-study-body li` also sets its own font-size, which overrode the list's
   and left the items at 14px while their line spacing was still computed from
   the 16px the list asked for, loose, uneven lines. Items inherit the list. */
.page-inner li {
  font-size: inherit;
  line-height: inherit;
}

/* The same leak, on prints. `.case-study-body img` gives article figures a
   32px vertical margin, a hairline border and rounded corners, inside a paper
   frame that reads as a floating rounded picture in an oversized white mat,
   and it stretched every frame 64px taller than its picture. A print in a
   scrapbook is trimmed square and sits flush in its frame. */
.page-inner img {
  margin: 0;
  border: 0;
  border-radius: 0;
}

/* And on lists. `.case-study-body ul` restores disc bullets and its own indent,
   which doubles up on the hairline the list already hangs from and pushes every
   line off that rule. */
.page-inner .page-list {
  list-style: none;
  padding-left: clamp(var(--space-3), 1.4vw, var(--space-5));
}

/* ...then restore each element's own measure at the same specificity. */
.page-inner .page-prose { max-width: 34ch; }
.page-inner .page-list { max-width: 34ch; }
.page-inner .page-run { max-width: 32ch; }
.page-inner .page-quote { max-width: 26ch; }
.page-inner .page-hand { max-width: 30ch; }
.page-inner .photo-aside { max-width: min(78%, 268px); }

/* Both stickers are <p>, so the reset above zeroed the bottom margin they use
   to hold their own bleed clear, the run below "Currently into" started 3px
   inside the sticker's cut edge. Restored here at the same specificity as the
   reset. Values live with the sticker rules; these only re-enable them. */
.page-inner .page-stamp { margin-bottom: 4px; }
/* More than the pennant's 5px bleed, because this one is followed immediately
   by a hand-written run whose first line rises well above its box: at 9px the
   sticker's shadow fell across the ascenders. */
.page-inner .page-hand-head { margin-bottom: 14px; }

/* Text pages are a vertical composition: eyebrow and headline anchor the top,
   the prose sits under the rule, and any sign-off drops to the foot. The extra
   bottom padding is the folio band, content never reaches the page number. */
.page-text .page-inner,
.page-left-face .page-inner {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--space-2), 1.2vw, var(--space-3));
  /* The folio band. It has to clear the page number *plus* the air a printed
     page leaves around it, or the last line reads as sitting on the number. */
  padding-bottom: clamp(var(--space-10), 5.5vw, var(--space-14));
  overflow: hidden;
}

/* Type never shrinks. A shrunk text block keeps its own line height and simply
   spills past its box, landing on whatever follows, that was the source of the
   overlapping paragraphs. Only the print gives way, and it does so by scaling. */
.page-text .page-inner > *,
.page-left-face .page-inner > * {
  flex-shrink: 0;
}

/* ...except the picture blocks, which shrink by scaling rather than spilling. */
.page-text .page-inner > .page-coda,
.page-left-face .page-inner > .page-coda,
.page-text .page-inner > .text-figure,
.page-left-face .page-inner > .text-figure,
.page-text .page-inner > .inline-photo,
.page-left-face .page-inner > .inline-photo {
  flex-shrink: 1;
  min-height: 0;
}

/* An image set in a text column scales to the room it is given. */
.inline-photo img {
  max-height: 100%;
}

/* A closing block on a text page: the last words on the left, a small print on
   the right. Setting them side by side rather than stacked is what keeps a
   words-plus-picture page inside its trim. */
.text-figure {
  display: flex;
  /* Top-aligned. Hanging both from the bottom pushed the shorter column away
     from the headline it belongs to and opened a gap under the rule. */
  align-items: flex-start;
  gap: clamp(var(--space-3), 1.8vw, var(--space-5));
  min-height: 0;
  /* Not clipped. A print's drop shadow reaches ~36px past its paper, and this
     row's edge falls within a couple of pixels of the frame, clipping sheared
     the soft shadow into a hard grey band under the picture. The page itself
     still clips, which is the boundary that actually matters. */
  overflow: visible;
}

.text-figure-body {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}

/* Inside the figure the column is narrower than the page, so the ideal measure
   yields to the actual room rather than running under the print. */
.text-figure-body > * { max-width: 100%; }
.text-figure > .page-prose { max-width: 100%; }

/* The print holds its width; the text beside it rewraps. Allowing the print to
   shrink on the row's main axis squeezed it to a sliver. Narrower than it was:
   at 38% the column beside it fell to 25ch, and a hand-written run at 25ch rags
   badly. The pages have vertical room to spare, so the print gives up width. */
.text-figure > .inline-photo {
  flex: 0 0 auto;
  align-self: flex-start;
  width: min(30%, 104px);
  margin-top: 0;
}

/* A print set beside a text column is landscape regardless of the ratio the
   page asked for. At this width a portrait crop stood taller than the row,
   so it rode up over the prose and out past the column. */
.text-figure > .inline-photo img {
  aspect-ratio: 4 / 3;
}

/* A print closing a page whose body already needs the full measure. It sits to
   the right of the column, in its own row, so the text above stays full width
   and the picture reads as a print laid on the page rather than a column mate. */
/* Beside a list, whose lines are longer than a paragraph's, the print takes
   less width so the list keeps a readable measure instead of a 3-word rag. */
.text-figure-tight > .inline-photo {
  align-self: flex-end;
  width: min(28%, 96px);
}

/* A page with room to spare sets the print on its own line instead of beside
   the text. Narrowing a column to 26ch to make space sideways costs more in
   legibility than the vertical room it saves. */
.inline-photo-block {
  align-self: flex-end;
  flex: 0 1 auto;
  min-height: 0;
  width: min(46%, 152px);
  margin-top: clamp(var(--space-2), 1.2vw, var(--space-3));
  margin-right: clamp(2px, 0.6vw, 10px);
}

/* The foot of a page whose body needs its full measure: the star sits at the
   left where a reader's eye leaves the last line, and a wide print closes the
   page beside it. Splitting the column would have crushed the text. */
.page-coda {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(var(--space-3), 1.8vw, var(--space-5));
  margin-top: auto;
  padding-top: clamp(var(--space-2), 1.2vw, var(--space-3));
  /* The one block on a text page that yields: it holds a picture, which scales,
     where every text block above it would spill instead of shrinking. */
  flex: 0 1 auto;
  min-height: 0;
  overflow: hidden;
}

.inline-photo-wide {
  flex: 0 1 auto;
  width: min(52%, 168px);
  max-height: 100%;
  margin-top: 0;
}

/* The chin no longer needs stripping for a print without handwriting: the frame
   is a flex column, so a print with no caption simply has nothing below the
   picture and closes up on its own. */

.page-left-face .page-run { margin-bottom: 0; }
.page-left-face .page-hand { margin-bottom: 0; }

/* the block that should sink to the bottom of the page */
.page-foot { margin-top: auto; }

/* --------------------------------------------------------------------------
   Photo page: one hero print, taped, filling the page with room to breathe.
   -------------------------------------------------------------------------- */

/* A photo page is a hero print plus a smaller one, laid out in a column with
   the handwritten note given its own row. Nothing overlaps text. */
.page-photo .page-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--space-3), 1.8vw, var(--space-5));
  padding: clamp(var(--space-4), 2.6vw, var(--space-7));
  padding-bottom: clamp(var(--space-8), 3.6vw, var(--space-10));
  overflow: hidden;
}

/* Prints are tilted, so their rotated corners reach further than their boxes.
   Letting the row shrink: and capping its height, keeps every corner inside
   the paper instead of hanging over the trim. */
.page-photo .page-inner > * {
  min-height: 0;
  flex-shrink: 1;
}

/* The hero print sits beside a column of smaller ones, so every thing named on
   the facing page has a picture without the page turning into a grid. */
.photo-pair {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--space-2), 1.4vw, var(--space-4));
  width: 100%;
  min-height: 0;
  /* Inline padding is the room the tilt needs: each print is rotated a few
     degrees, which swings a corner out past its own box. */
  padding: 0 clamp(4px, 0.6vw, 9px);
  /* The row gives way before the page does. 3vw of headroom leaves space for
     the tape above the hero print and the tilt on the stack. */
  max-height: calc(100% - 3vw);
}

.photo-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--space-2), 1.4vw, var(--space-4));
  flex: 0 1 auto;
  min-height: 0;
  width: min(32%, 138px);
  max-height: 100%;
}

.photo-stack .side-photo { width: 100%; }

.hero-photo {
  position: relative;
  /* A column, like the inline print: the caption sits in flow below the picture
     so the chin is exactly as deep as the handwriting needs, however many lines
     it runs to. Pinned to the bottom it grew upward onto the photograph. */
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  min-height: 0;
  max-height: 100%;
  width: min(56%, 236px);
  padding: clamp(7px, 1vw, 12px);
  padding-bottom: clamp(9px, 1.2vw, 14px);
  border: 0;
  background: #fffefb;
  cursor: zoom-in;
  transform: rotate(var(--r, -2deg));
  box-shadow:
    0 2px 3px rgba(60, 52, 38, 0.14),
    0 26px 46px -18px rgba(50, 44, 34, 0.45);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.4s ease;
}

.hero-photo img {
  display: block;
  width: 100%;
  height: auto;
  /* The frame may be squeezed by the page; the picture gives way rather than
     pushing the caption out of the chin, and crops rather than overflowing. */
  flex: 0 1 auto;
  min-height: 0;
  max-height: 100%;
  aspect-ratio: var(--ar, 3 / 4);
  object-fit: cover;
  object-position: center var(--fy, 45%);
  pointer-events: none;
}

/* the caption written on the polaroid's chin, in a real hand */
.hero-photo .photo-note {
  display: block;
  flex: 0 0 auto;
  margin: clamp(6px, 0.9vw, 11px) 6% 0;
  font-family: 'Caveat', cursive;
  font-weight: 500;
  font-size: clamp(0.85rem, 1.6vw, 1.2rem);
  line-height: 1.2;
  color: #2b3446;
  text-align: center;
  transform: rotate(-1.2deg);
}

.hero-photo:hover,
.hero-photo:focus-visible {
  transform: rotate(0deg) scale(1.02) translateY(-5px);
  box-shadow:
    0 3px 5px rgba(60, 52, 38, 0.16),
    0 38px 60px -20px rgba(50, 44, 34, 0.5);
  outline: none;
}

.hero-photo:focus-visible {
  box-shadow: 0 0 0 3px rgba(38, 51, 73, 0.5), 0 38px 60px -20px rgba(50, 44, 34, 0.5);
}

/* Tape across the top edge of the print. It sits mostly *on* the paper rather
   than above it, so the page edge can never clip it. */
.hero-photo::before {
  content: "";
  position: absolute;
  top: clamp(-7px, -0.6vw, -4px);
  left: 50%;
  width: clamp(48px, 6.4vw, 80px);
  height: clamp(16px, 2vw, 25px);
  transform: translateX(-50%) rotate(-2.5deg);
  /* Washi tape, not the clear kind: a soft pink that picks up the sticky notes
     on the same page, still translucent enough to read as tape. */
  background: linear-gradient(120deg, rgba(247, 199, 212, 0.82), rgba(240, 178, 195, 0.68));
  box-shadow: 0 1px 3px rgba(60, 52, 38, 0.14);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   A print set into a text page, so no page is words alone.
   -------------------------------------------------------------------------- */

/* Set into a text column, so it must shrink rather than push the page over.
   min-height:0 lets flexbox actually compress it; the aspect ratio follows. */
.inline-photo {
  position: relative;
  /* A column, so the picture and the handwriting below it stack in flow and the
     frame's height is the sum of the two. The chin is then exactly as deep as
     the caption needs, rather than a guessed padding the words could outgrow. */
  display: flex;
  flex-direction: column;
  align-self: flex-start;
  flex: 0 1 auto;
  min-height: 0;
  width: min(58%, 190px);
  margin-top: var(--space-1);
  padding: clamp(5px, 0.7vw, 8px);
  padding-bottom: clamp(7px, 1vw, 11px);
  border: 0;
  background: #fffefb;
  cursor: zoom-in;
  transform: rotate(var(--r, -2deg));
  box-shadow:
    0 2px 3px rgba(60, 52, 38, 0.13),
    0 20px 36px -16px rgba(50, 44, 34, 0.42);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.4s ease;
}

.inline-photo img {
  display: block;
  width: 100%;
  height: auto;
  flex: 0 1 auto;
  min-height: 0;
  aspect-ratio: var(--ar, 4 / 3);
  object-fit: cover;
  object-position: center var(--fy, 45%);
  pointer-events: none;
}

/* Declared after the rule above so it wins on source order: a print that has to
   fit a remaining gap takes its height from that gap, not from a set ratio. */
.text-figure > .inline-photo img { aspect-ratio: 4 / 3; }

/* Also after `.inline-photo`, whose `width: min(58%, 190px)` is the same
   specificity and was silently winning on source order, the block print
   measured 187px against the 152px it asked for, which is what left pages 07
   and 08 with no room to spare. */
.inline-photo-block { width: min(42%, 138px); }

.inline-photo-wide img {
  flex: 1 1 auto;
  aspect-ratio: auto;
  height: 100%;
  min-height: 0;
}

/* The handwriting sits in the flow at the foot of the print, not absolutely
   positioned in the chin. Pinned to the bottom it grew *upward* when the words
   ran to two lines, and the second line landed on the photograph. In flow the
   chin sizes itself to however many lines the caption actually takes. */
.inline-photo .photo-note {
  display: block;
  flex: 0 0 auto;
  /* Barely inset. A 6% margin each side cost enough width to tip these short
     captions onto a second line inside an already narrow frame. */
  margin: clamp(5px, 0.7vw, 9px) 1% 0;
  font-family: 'Caveat', cursive;
  font-weight: 500;
  font-size: clamp(0.78rem, 1.35vw, 1.05rem);
  line-height: 1.2;
  color: #2b3446;
  text-align: center;
  transform: rotate(-1deg);
}

.inline-photo .snap-caption {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.inline-photo:hover,
.inline-photo:focus-visible {
  transform: rotate(0deg) scale(1.03) translateY(-4px);
  box-shadow: 0 3px 5px rgba(60, 52, 38, 0.16), 0 30px 50px -18px rgba(50, 44, 34, 0.48);
  outline: none;
}

.inline-photo:focus-visible {
  box-shadow: 0 0 0 3px rgba(38, 51, 73, 0.5), 0 30px 50px -18px rgba(50, 44, 34, 0.48);
}

/* The second, smaller print: a plain bordered snapshot, no chin. */
.side-photo {
  position: relative;
  display: block;
  flex: 0 1 auto;
  min-height: 0;
  width: min(32%, 138px);
  padding: clamp(5px, 0.7vw, 8px);
  border: 0;
  background: #fffefb;
  cursor: zoom-in;
  transform: rotate(var(--r, 3deg));
  box-shadow:
    0 1px 2px rgba(60, 52, 38, 0.13),
    0 16px 30px -14px rgba(50, 44, 34, 0.42);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.4s ease;
}

.side-photo img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 100%;
  aspect-ratio: var(--ar, 3 / 4);
  object-fit: cover;
  object-position: center var(--fy, 45%);
  pointer-events: none;
}

.side-photo .snap-caption {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.side-photo:hover,
.side-photo:focus-visible {
  transform: rotate(0deg) scale(1.05) translateY(-4px);
  box-shadow: 0 2px 4px rgba(60, 52, 38, 0.16), 0 24px 40px -16px rgba(50, 44, 34, 0.5);
  z-index: 24;
  outline: none;
}

.side-photo:focus-visible {
  box-shadow: 0 0 0 3px rgba(38, 51, 73, 0.5), 0 24px 40px -16px rgba(50, 44, 34, 0.5);
}

/* The handwritten note, in the flow beneath the prints rather than over them.
   Overlapping type was the legibility problem; giving it a row solves it. */
/* Centred, not hung off the right edge: the tilt swings a corner outward, and
   at flex-end that corner crossed the page trim. Centring keeps both corners
   inside the margin at any rotation. */
.photo-aside {
  position: relative;
  align-self: center;
  max-width: min(78%, 268px);
  padding: clamp(var(--space-2), 1.3vw, var(--space-4)) clamp(var(--space-3), 1.6vw, var(--space-4));
  background: #f9dfe6;
  transform: rotate(-1.6deg);
  box-shadow: 0 2px 3px rgba(60, 52, 38, 0.13), 0 14px 26px -12px rgba(60, 52, 38, 0.4);
  font-family: 'Caveat', cursive;
  font-weight: 500;
  font-size: clamp(0.82rem, 1.4vw, 1.08rem);
  line-height: 1.4;
  color: #2b3446;
  z-index: 22;
}

.photo-aside-mint { background: #dcece0; }
.photo-aside-butter { background: #fbeec4; }

/* --------------------------------------------------------------------------
   Left page: handwritten journalling
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Stickers

   Every label in the book is a sticker, and no two come off the same sheet:
   a postage stamp, a scalloped tag, a torn strip, a striped label. Each is
   built from three layers, which is what keeps the shapes from costing any
   layout and the words from turning into pictures, they stay real text,
   selectable and searchable, crisp at any size:

     ::before   the paper. Bleeds a few pixels past the text box so the die
                cut bites into the stock rather than into the type's air.
                Carries the ground, the cut edge (border), the shape
                (mask / clip-path) and the printed keyline (inset shadow).
     ::after    the rest of the artwork, a second ring, a postmark.
     filter     the shadow, on the parent. box-shadow ignores a mask and
                would draw the very rectangle the die cut just removed;
                drop-shadow on the layer above follows the real shape.

   Both pseudo layers sit at z-index -1, behind the words but inside this
   element's own stacking context, which the filter establishes.
   -------------------------------------------------------------------------- */

/* One punched hole, shared by the perforated and scalloped edges. Same
   mechanism, different radius, set the tile to twice the radius and
   neighbouring holes touch, so a row of bites becomes a continuous wave. */
:root {
  --perf-hole: #0000 97%, #000;
}

/* The chapter eyebrow above each headline. Base species is a seal: an oval
   die cut with two printed rings, the "I MANIFESTED THIS" kind. */
.page-stamp {
  display: inline-block;
  align-self: flex-start;
  position: relative;
  font-family: 'Caveat', cursive;
  font-weight: 700;
  font-size: clamp(0.95rem, 1.8vw, 1.3rem);
  line-height: 1.12;
  letter-spacing: 0.01em;
  text-transform: none;
  color: #a25f47;
  /* Negative top margin pulls the sticker back over the space its own padding
     added: a sticker sits *on* the page without pushing the headline down. The
     bottom margin covers the 3.5px the paper bleeds past this box, and no more
     than that: the headline's own leading supplies the air, and these pages
     have single-digit room to spare. */
  margin: calc(-1 * clamp(2px, 0.5vw, 6px)) 0 4px;
  padding: 0.3em 1.05em 0.26em;
  transform: rotate(-1.1deg);
  filter:
    drop-shadow(0 1px 1px rgba(60, 52, 38, 0.18))
    drop-shadow(0 5px 9px rgba(50, 44, 34, 0.22));
}

.page-stamp::before,
.page-stamp::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
}

/* The paper. `background-clip: padding-box` keeps the ground inside the cut
   edge, so the border reads as the white rim of a die cut rather than a
   drawn box; the inset shadow is the artwork printed just inside that rim. */
.page-stamp::before {
  inset: -3.5px;
  border: 3px solid #fdfaf1;
  border-radius: 999px;
  background: #fdfaf1;
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1.5px rgba(162, 95, 71, 0.42);
}

/* The seal's second ring, drawn inside the first. */
.page-stamp::after {
  inset: 2.5px;
  border: 1px solid rgba(162, 95, 71, 0.28);
  border-radius: 999px;
}

/* Striped label: the "Seas The Day" kind. The stripes are two shades of the
   same cream, so they read as printed stock and never fight the script. */
.page-stamp-stripe {
  transform: rotate(-1.7deg);
}

.page-stamp-stripe::before {
  border-radius: 4px;
  background:
    repeating-linear-gradient(90deg,
      #efe6d1 0 4px,
      #fdfaf1 4px 9px);
  background-clip: padding-box;
}

.page-stamp-stripe::after {
  border-radius: 2px;
  border-color: rgba(162, 95, 71, 0.22);
}

/* Scalloped tag. Holes centred on the top and bottom edges at a pitch of
   exactly twice their radius, so neighbouring bites meet and the edge comes
   out as a wave rather than a row of punched dots. */
.page-stamp-scallop {
  transform: rotate(1.4deg);
}

.page-stamp-scallop::before {
  border-radius: 0;
  /* No printed keyline: a rectangular rule inside a wavy cut would contradict
     the shape it sits in. The scalloped edge is the whole decoration. */
  box-shadow: none;
  -webkit-mask:
    radial-gradient(circle 6px at 50% 0, var(--perf-hole)) 50% 0 / 12px 100% repeat-x,
    radial-gradient(circle 6px at 50% 100%, var(--perf-hole)) 50% 100% / 12px 100% repeat-x;
  -webkit-mask-composite: source-in;
  mask:
    radial-gradient(circle 6px at 50% 0, var(--perf-hole)) 50% 0 / 12px 100% repeat-x,
    radial-gradient(circle 6px at 50% 100%, var(--perf-hole)) 50% 100% / 12px 100% repeat-x;
  mask-composite: intersect;
}

/* The wave already does the decorating; a ring inside it would only crowd. */
.page-stamp-scallop::after { display: none; }

/* Torn strip: the "I love you" kind, ripped off something bigger. No cut
   edge and no printed rule: a tear has neither. The jitter in the polygon is
   deliberately uneven, because a rip that repeats reads as a pattern. */
.page-stamp-torn {
  transform: rotate(-1.9deg);
}

.page-stamp-torn::before {
  inset: -5px;
  border: 0;
  border-radius: 0;
  background: #fdfaf1;
  box-shadow: none;
  clip-path: polygon(
    1% 14%, 5% 3%, 13% 9%, 23% 1%, 35% 8%, 48% 0%, 61% 7%, 73% 1%, 85% 8%, 95% 2%, 99% 12%,
    97% 32%, 100% 54%, 98% 76%, 99% 89%, 91% 98%, 79% 92%, 67% 99%, 53% 93%, 40% 100%,
    27% 94%, 15% 99%, 5% 92%, 2% 80%, 0% 58%, 2% 36%
  );
}

.page-stamp-torn::after { display: none; }

/* Postage stamp. Perforations on all four edges: each mask layer is a
   full-height (or full-width) tile with one hole on the edge it belongs to,
   repeated along that edge, and the four intersect. */
.page-stamp-post {
  transform: rotate(1.1deg);
}

.page-stamp-post::before {
  inset: -5px;
  border-width: 3.5px;
  border-radius: 0;
  box-shadow: inset 0 0 0 1.5px rgba(162, 95, 71, 0.4);
  -webkit-mask:
    radial-gradient(circle 3.6px at 50% 0, var(--perf-hole)) 50% 0 / 11.5px 100% repeat-x,
    radial-gradient(circle 3.6px at 50% 100%, var(--perf-hole)) 50% 100% / 11.5px 100% repeat-x,
    radial-gradient(circle 3.6px at 0 50%, var(--perf-hole)) 0 50% / 100% 11.5px repeat-y,
    radial-gradient(circle 3.6px at 100% 50%, var(--perf-hole)) 100% 50% / 100% 11.5px repeat-y;
  -webkit-mask-composite: source-in, source-in, source-in;
  mask:
    radial-gradient(circle 3.6px at 50% 0, var(--perf-hole)) 50% 0 / 11.5px 100% repeat-x,
    radial-gradient(circle 3.6px at 50% 100%, var(--perf-hole)) 50% 100% / 11.5px 100% repeat-x,
    radial-gradient(circle 3.6px at 0 50%, var(--perf-hole)) 0 50% / 100% 11.5px repeat-y,
    radial-gradient(circle 3.6px at 100% 50%, var(--perf-hole)) 100% 50% / 100% 11.5px repeat-y;
  mask-composite: intersect;
}

/* The postmark, struck half off the edge the way a real one lands. Outside
   the ::before's mask, so the perforations don't nibble it. */
.page-stamp-post::after {
  inset: auto -0.5em -0.4em auto;
  width: 2.1em;
  height: 2.1em;
  border: 1.5px dashed rgba(90, 104, 122, 0.45);
  border-radius: 50%;
  transform: rotate(-14deg);
}

/* Serif body prose facing the photo. Generous leading, measured column. */
.page-prose {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(0.82rem, 1.35vw, 1rem);
  line-height: 1.85;
  color: #5b6472;
  margin: 0;
  max-width: 34ch;
}

.page-prose + .page-prose { margin-top: 0.9em; }

.page-prose strong {
  font-weight: 600;
  color: #263349;
}

/* The closing call to action */
.page-cta {
  margin: 0;
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(0.95rem, 1.7vw, 1.2rem);
}

.page-cta a {
  color: #b3705a;
  text-decoration: none;
  border-bottom: 1px solid rgba(179, 112, 90, 0.4);
  padding-bottom: 2px;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.page-cta a:hover {
  color: #263349;
  border-bottom-color: #263349;
}

/* A list hung off a single hairline rule, as in the reference. The rule is
   drawn on a pseudo-element inset from the last line rather than as a border on
   the <ul>, so it can't run on past the final item toward the folio. */
.page-list {
  position: relative;
  margin: 0;
  padding: 0 0 0 clamp(var(--space-3), 1.4vw, var(--space-5));
  list-style: none;
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(0.82rem, 1.35vw, 1rem);
  line-height: 1.7;
  color: #5b6472;
  max-width: 34ch;
}

.page-list::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.35em;
  bottom: 0.35em;
  width: 1.5px;
  background: rgba(90, 100, 114, 0.4);
}

.page-list li + li { margin-top: 0.8em; }

/* A starred list: each item is marked by a small drawn star in the margin rather
   than hung off a shared hairline. The star does the work the em dash used to:
   it separates the label from its note, so the label can simply be bold. */
.page-list-starred {
  padding-left: clamp(1.5em, 2.2vw, 1.9em);
  /* This list is the longest block in the book and its page is the tightest, so
     it sets its own measure wider than the shared 34ch. Two lines an item
     instead of three is what makes page 03 fit at every window size. */
  max-width: 40ch;
  line-height: 1.6;
}

/* Tighter between items than the 0.8em a hairline list wants: the stars already
   separate them, so the extra air only pushed the last line toward the folio. */
.page-list-starred li + li { margin-top: 0.62em; }

.page-list-starred::before { display: none; }

.page-list-starred li {
  position: relative;
}

.page-list-starred li::before {
  content: "";
  position: absolute;
  /* Optically centred on the first line rather than its box: a star reads as
     sitting high unless it is nudged into the x-height. */
  top: 0.42em;
  left: calc(-1 * clamp(1.5em, 2.2vw, 1.9em));
  width: 0.78em;
  height: 0.78em;
  background: currentColor;
  color: rgba(179, 112, 90, 0.85);
  -webkit-mask: var(--page-star) center / contain no-repeat;
  mask: var(--page-star) center / contain no-repeat;
}

.page-list-starred .page-list-note {
  color: #5b6472;
}

.page-list strong {
  font-weight: 600;
  color: #263349;
}

/* The headline is a high-contrast serif, not handwriting: it holds the page
   and lets the hand-lettering be the accent rather than the whole voice. */
.page-hand-title {
  font-family: 'Playfair Display', Georgia, serif;
  font-weight: 400;
  font-size: clamp(2.1rem, 5vw, 3.6rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  color: #263349;
  margin: 0;
  transform: none;
}

/* the second line falls to italic terracotta, the reference's move */
.page-hand-title .accent {
  display: inline-block;
  font-style: italic;
  font-weight: 500;
  color: #b3705a;
  text-shadow: none;
}

/* One divider rule under the whole headline, set to the prose measure. The
   earlier version underlined the closing phrase, which read as emphasis on
   that phrase rather than as the break between headline and body. */
.page-hand-title::after {
  content: "";
  display: block;
  width: min(100%, 19ch);
  height: 2px;
  margin-top: clamp(var(--space-3), 1.5vw, var(--space-5));
  background: rgba(179, 112, 90, 0.45);
}

.page-hand-title .underline { display: inline; }

.page-hand {
  font-family: 'Kalam', 'Caveat', cursive;
  font-size: clamp(0.92rem, 1.5vw, 1.06rem);
  line-height: 1.6;
  color: #2f2a20;
  max-width: 30ch;
  margin: 0 0 var(--space-4);
}

/* The label above a phrase run. Same three-layer construction as .page-stamp
   above, different species: where the chapter marks are cream paper with a
   printed hand, these are solid-ink shapes with knocked-out type, the "BEACH"
   pennant and "LUCKY" clover kind. Two families, so a chapter mark and a
   section label can never be mistaken for one another. */
.page-hand-head {
  display: inline-block;
  align-self: flex-start;
  position: relative;
  font-family: 'Space Mono', ui-monospace, monospace;
  font-weight: 700;
  font-size: clamp(0.62rem, 1.15vw, 0.76rem);
  letter-spacing: 0.16em;
  line-height: 1.15;
  text-transform: uppercase;
  color: #f7f2e4;
  /* Tighter than the seal's: this label is set in small caps mono, which has
     no descenders to leave air for, and it sits on the tightest page. */
  padding-block: 0.42em 0.38em;
  /* The paper bleeds 4px past this box and its shadow falls further still,
     neither of which the flex gap knows about, so the cut edge climbed onto
     the line above and the run below started against the sticker's underside.
     Only the bottom needs the correction: the gap above already clears it, and
     these pages have no vertical room to spend on both. */
  margin: 0 0 10px;
  /* Room on the right for the pennant's notch to bite into, so the cut never
     reaches the last letter. */
  padding: 0.55em 1.9em 0.5em 1em;
  transform: rotate(var(--sticker-r, -1.6deg));
  filter:
    drop-shadow(0 1px 1px rgba(60, 52, 38, 0.2))
    drop-shadow(0 5px 10px rgba(50, 44, 34, 0.26));
}

.page-hand-head::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -4px;
  pointer-events: none;
  border: 3px solid #fbf7ec;
  background: var(--sticker-ink, #7f8f6a);
  background-clip: padding-box;
  /* A pennant: square at the left where it's stuck down, notched at the right
     as though a tail were cut into it. clip-path applies to the border box, so
     the pale cut edge follows the notch too. */
  clip-path: polygon(0 0, 100% 0, 82% 50%, 100% 100%, 0 100%);
}

/* The registration mark of a cheaply printed sticker: a hairline of the stock
   colour just inside the ink, following the same cut. */
.page-hand-head::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 1.5px;
  pointer-events: none;
  border: 1px solid rgba(247, 242, 228, 0.5);
  clip-path: polygon(0 0, 100% 0, 82% 50%, 100% 100%, 0 100%);
}

/* Each sticker is stuck on by hand, so no two sit at the same angle, and no
   two are the same die. This one is a ticket stub: perforated down both ends,
   the "YAPPY HOUR" kind, with the padding evened up since it has no tail. */
.page-hand-head-alt {
  --sticker-ink: #b3705a;
  --sticker-r: 1.8deg;
  padding: 0.55em 1.1em 0.5em;
}

.page-hand-head-alt::before {
  clip-path: none;
  -webkit-mask:
    radial-gradient(circle 4px at 0 50%, var(--perf-hole)) 0 50% / 100% 12px repeat-y,
    radial-gradient(circle 4px at 100% 50%, var(--perf-hole)) 100% 50% / 100% 12px repeat-y;
  -webkit-mask-composite: source-in;
  mask:
    radial-gradient(circle 4px at 0 50%, var(--perf-hole)) 0 50% / 100% 12px repeat-y,
    radial-gradient(circle 4px at 100% 50%, var(--perf-hole)) 100% 50% / 100% 12px repeat-y;
  mask-composite: intersect;
}

/* The stub's tear guides, set in from each perforated end. */
.page-hand-head-alt::after {
  inset: 3px 9px;
  border: 0;
  border-left: 1px dashed rgba(247, 242, 228, 0.55);
  border-right: 1px dashed rgba(247, 242, 228, 0.55);
  clip-path: none;
}

/* The pull-quote from the reference: a light hand, generously leaded, sitting
   under the rule as the page's second voice. */
.page-quote {
  font-family: 'Caveat', cursive;
  font-weight: 500;
  font-size: clamp(1.05rem, 2vw, 1.45rem);
  line-height: 1.5;
  color: #5f6b7a;
  margin: 0;
  max-width: 26ch;
}

/* Dense run of short phrases, hyphen-joined, wrapping like handwriting */
.page-run {
  font-family: 'Kalam', 'Caveat', cursive;
  font-weight: 400;
  font-size: clamp(0.86rem, 1.35vw, 0.98rem);
  line-height: 1.9;
  color: #4a4438;
  margin: 0;
  max-width: 32ch;
  text-wrap: pretty;
}

.page-run .sep {
  color: #b3705a;
  padding: 0 0.3em;
  opacity: 0.7;
}

/* a few phrases lifted in a different pen, the way real notes accumulate */
.page-run .ink-2 { color: #2f56b8; }
.page-run .ink-3 { color: #6b4a2f; }
.page-run .ink-hi {
  color: #2b2419;
  background: linear-gradient(180deg, rgba(253, 224, 122, 0) 55%, rgba(253, 224, 122, 0.75) 55%);
  padding: 0 0.12em;
}

/* A whisper of tilt on the handwritten blocks only. The set type stays level.
   Rotating everything is what made the earlier version read as a costume. */
.page-run { transform: rotate(-0.3deg); transform-origin: left top; }
.page-hand { transform: rotate(0.24deg); transform-origin: left top; }
.leaf-face-back .page-run { transform: rotate(0.26deg); }

/* a rotated aside, like a note squeezed into the margin */
.page-margin-note {
  align-self: flex-end;
  max-width: 14ch;
  text-align: center;
  font-family: 'Caveat', cursive;
  font-size: clamp(0.9rem, 1.5vw, 1.05rem);
  line-height: 1.25;
  color: #a8452c;
  font-weight: 700;
  transform: rotate(-6deg);
  z-index: 12;
}

.page-margin-note::before {
  content: "↘";
  display: block;
  font-size: 1.1em;
  opacity: 0.7;
}

.page-number {
  position: absolute;
  bottom: clamp(var(--space-3), 1.8vw, var(--space-5));
  font-family: 'Space Mono', ui-monospace, monospace;
  font-size: clamp(0.56rem, 0.9vw, 0.66rem);
  letter-spacing: 0.14em;
  color: #b8ad98;
  z-index: 12;
}

.page-left-face .page-number { left: clamp(var(--space-5), 3.2vw, var(--space-10)); }
.page-right-face .page-number { right: clamp(var(--space-5), 3.2vw, var(--space-10)); }

/* stickers, decoration only, never in the a11y tree */
.sticker {
  position: absolute;
  line-height: 1;
  font-size: clamp(1rem, 2vw, 1.5rem);
  filter: drop-shadow(0 2px 3px rgba(92, 83, 68, 0.28));
  user-select: none;
  z-index: 11;
}

.sticker-sm { font-size: clamp(0.7rem, 1.3vw, 0.95rem); opacity: 0.8; }

/* --------------------------------------------------------------------------
   Right page: photo cluster
   -------------------------------------------------------------------------- */

.snap-cluster {
  display: grid;
  grid-template-columns: 1fr 1fr;
  /* rows share the page height so photos never spill past the paper edge */
  grid-auto-rows: 1fr;
  gap: clamp(var(--space-4), 2.4vw, var(--space-6)) clamp(var(--space-3), 2vw, var(--space-5));
  height: 100%;
}

.snap-cluster .snap {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.snap-cluster .snap-wide { grid-column: 1 / -1; }

.snap {
  position: relative;
  display: block;
  width: 100%;
  padding: 7px 7px 5px;
  border: 0;
  background: #fffdf7;
  border-radius: 2px;
  cursor: zoom-in;
  box-shadow: 0 5px 14px -5px rgba(92, 83, 68, 0.5);
  transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.3s ease;
}

/* washi tape holding the photo down */
.snap::before {
  content: "";
  position: absolute;
  top: -8px;
  left: 50%;
  width: 52px;
  height: 18px;
  transform: translateX(-50%) rotate(-3deg);
  background: linear-gradient(120deg, rgba(226, 213, 188, 0.9), rgba(240, 232, 214, 0.92));
  border-left: 1px dashed rgba(160, 146, 120, 0.55);
  border-right: 1px dashed rgba(160, 146, 120, 0.55);
  pointer-events: none;
  z-index: 2;
}

.snap img {
  display: block;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
  object-fit: cover;
  object-position: center 55%;
  border-radius: 1px;
  pointer-events: none;
}

.snap-caption {
  display: block;
  flex: 0 0 auto;
  margin-top: 5px;
  font-family: 'Kalam', 'Caveat', cursive;
  font-size: clamp(0.76rem, 1.2vw, 0.88rem);
  line-height: 1.3;
  color: #2f2a20;
  text-align: center;
}

/* scattered tilts, straightened when you reach for them */
.snap-cluster .snap:nth-child(1) { transform: rotate(-2deg); }
.snap-cluster .snap:nth-child(2) { transform: rotate(1.7deg); }
.snap-cluster .snap:nth-child(3) { transform: rotate(-1.4deg); }
.snap-cluster .snap:nth-child(4) { transform: rotate(2.2deg); }

.snap:hover,
.snap:focus-visible {
  transform: rotate(0deg) translateY(-6px) scale(1.035);
  box-shadow: 0 16px 30px -10px rgba(92, 83, 68, 0.55);
  z-index: 15;
  outline: none;
}

.snap:focus-visible {
  box-shadow: 0 16px 30px -10px rgba(92, 83, 68, 0.55), 0 0 0 3px rgba(31, 61, 99, 0.4);
}

/* sticky notes */
.note-cluster {
  display: grid;
  gap: clamp(var(--space-3), 1.8vw, var(--space-4));
  align-content: start;
}

.note {
  padding: clamp(var(--space-3), 1.8vw, var(--space-4));
  font-family: 'Kalam', 'Caveat', cursive;
  font-size: clamp(0.82rem, 1.3vw, 0.95rem);
  line-height: 1.5;
  color: #2b2419;
  border-radius: 2px;
  box-shadow: 0 4px 10px -4px rgba(92, 83, 68, 0.4);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.note:nth-child(odd) { background: #fdf3d4; transform: rotate(-1.4deg); }
.note:nth-child(even) { background: #e4efe3; transform: rotate(1.2deg); }
.note:nth-child(3n) { background: #f8e3e7; }

.note:hover {
  transform: rotate(0deg) translateY(-3px);
  box-shadow: 0 12px 22px -8px rgba(92, 83, 68, 0.45);
}

/* closing page */
.page-closing {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  text-align: center;
}

.page-closing .page-hand {
  max-width: none;
  margin-left: auto;
  margin-right: auto;
}

.page-closing a {
  font-family: 'Caveat', cursive;
  font-size: clamp(1.1rem, 2vw, 1.35rem);
  color: #1f3d63;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Side arrows: flanking the book, the way a reader reaches for a page edge
   -------------------------------------------------------------------------- */

.book-wrap {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--space-2), 1.6vw, var(--space-5));
}

.book-wrap .book-stage { flex: 1 1 auto; min-width: 0; }

.page-arrow {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(38px, 3.6vw, 48px);
  height: clamp(38px, 3.6vw, 48px);
  padding: 0;
  color: var(--color-forest-deep);
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: var(--radius-full);
  cursor: pointer;
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 18px -8px rgba(18, 30, 24, 0.55);
  transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease, box-shadow 0.2s ease;
}

.page-arrow svg {
  width: 45%;
  height: 45%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.page-arrow:hover:not(:disabled) {
  background: #ffffff;
  transform: scale(1.06);
  box-shadow: 0 10px 24px -8px rgba(18, 30, 24, 0.6);
}

.page-arrow:active:not(:disabled) { transform: scale(0.97); }

.page-arrow:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(31, 61, 99, 0.5);
}

.page-arrow:disabled {
  opacity: 0.28;
  cursor: default;
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   Progress + hint
   -------------------------------------------------------------------------- */

.journal-meta {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.journal-dots {
  display: flex;
  align-items: center;
  gap: 7px;
}

.journal-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 0 1px rgba(22, 36, 28, 0.25);
  cursor: pointer;
  transition: transform 0.25s ease, background 0.25s ease, width 0.25s ease;
}

.journal-dot[aria-selected="true"] {
  width: 22px;
  background: #ffffff;
}

.journal-hint {
  font-size: var(--font-size-sm);
  color: #1f3327;
  background: rgba(255, 255, 255, 0.62);
  padding: 4px 12px;
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
  transition: opacity 0.4s ease;
}

.journal.has-turned .journal-hint {
  opacity: 0;
}

/* --------------------------------------------------------------------------
   Lightbox
   -------------------------------------------------------------------------- */

.journal-lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-8);
  margin: 0;
  background: rgba(20, 32, 25, 0.9);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.28s ease, visibility 0.28s;
}

.journal-lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.journal-lightbox img {
  max-width: min(920px, 100%);
  max-height: 74vh;
  object-fit: contain;
  padding: 10px;
  background: #fffdf7;
  border-radius: 2px;
  box-shadow: 0 30px 70px -22px rgba(0, 0, 0, 0.75);
}

.journal-lightbox figcaption {
  font-family: 'Caveat', cursive;
  font-size: 1.3rem;
  color: #f5f2e8;
  text-align: center;
}

.journal-lightbox-close {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  width: 42px;
  height: 42px;
  font-size: 1.15rem;
  color: #f5f2e8;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.32);
  border-radius: var(--radius-full);
  cursor: pointer;
}

.journal-lightbox-close:hover {
  background: rgba(255, 255, 255, 0.24);
}

.journal-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   Small screens: one page at a time, swipe to turn
   -------------------------------------------------------------------------- */

@media (max-width: 700px) {
  .book-stage { perspective: none; }

  .book {
    aspect-ratio: auto;
    padding: 8px;
  }

  .book::before,
  .book::after { display: none; }

  .book-gutter { display: none; }

  .book-block {
    position: relative;
    inset: auto;
    display: grid;
    box-shadow: 0 0 0 1px rgba(120, 108, 88, 0.18);
  }

  /* stack every page vertically; the 3D machinery stands down */
  .page,
  .leaf {
    position: relative;
    left: auto;
    right: auto;
    top: auto;
    width: 100%;
    height: auto;
    transform: none !important;
    transform-style: flat;
    transition: none;
  }

  .leaf-face {
    position: relative;
    inset: auto;
    transform: none !important;
    backface-visibility: visible;
    -webkit-backface-visibility: visible;
  }

  .leaf-face::before,
  .leaf::after { display: none; }

  .page-left-face::after,
  .page-right-face::after { display: none; }

  /* on mobile the book scrolls as a normal stack, so hide leaf backs
     that duplicate a left page already shown above */
  .page-static-right { display: none; }

  .page-inner { padding: var(--space-6); }

  .page-margin-note {
    position: relative;
    right: auto;
    bottom: auto;
    max-width: none;
    margin-top: var(--space-4);
    transform: rotate(-2deg);
  }

  .page-number { display: none; }

  .snap-cluster {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
    gap: var(--space-6);
    height: auto;
  }

  .snap img { height: 200px; flex: 0 0 auto; }

  .journal-hint { text-align: center; }

  /* the stacked layout scrolls normally, so the arrows have nothing to drive */
  .page-arrow { display: none; }
}

/* --------------------------------------------------------------------------
   Reduced motion: cut the 3D, keep the content
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .leaf-face::before,
  .leaf::after { display: none; }

  .snap,
  .note,
  .journal-dot,
  .journal-lightbox {
    transition: none;
  }

  .snap:hover,
  .snap:focus-visible,
  .note:hover {
    transform: rotate(0deg);
  }
}

/* ==========================================================================
   Collage layer: overlapping, hand-placed, off-grid
   Everything below leans on absolute placement with per-item rotation so the
   spread reads as assembled by hand rather than laid out by a stylesheet.
   ========================================================================== */

/* Highlighter swash behind a phrase, the way a real marker sits low and wide */
.hl {
  position: relative;
  z-index: 0;
  padding: 0 0.18em;
}

.hl::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: -0.1em;
  right: -0.1em;
  top: 38%;
  bottom: -2%;
  border-radius: 2px 6px 3px 5px;
  transform: rotate(-0.7deg);
  /* uneven ends, like a marker lifting off */
  background: linear-gradient(90deg,
    rgba(253, 224, 122, 0) 0%,
    rgba(253, 224, 122, 0.9) 4%,
    rgba(253, 224, 122, 0.85) 94%,
    rgba(253, 224, 122, 0.25) 100%);
}

.hl-blue::before {
  background: linear-gradient(90deg,
    rgba(168, 205, 245, 0) 0%,
    rgba(168, 205, 245, 0.85) 5%,
    rgba(168, 205, 245, 0.8) 93%,
    rgba(168, 205, 245, 0.2) 100%);
}

.hl-pink::before {
  background: linear-gradient(90deg,
    rgba(247, 199, 209, 0) 0%,
    rgba(247, 199, 209, 0.85) 5%,
    rgba(247, 199, 209, 0.8) 93%,
    rgba(247, 199, 209, 0.22) 100%);
}

/* Circled word: a pen loop drawn around something that mattered */
.circled {
  position: relative;
  padding: 0.05em 0.3em;
}

.circled::after {
  content: "";
  position: absolute;
  inset: -22% -12%;
  border: 1.8px solid rgba(200, 92, 110, 0.7);
  border-radius: 48% 52% 47% 53% / 55% 45% 56% 44%;
  transform: rotate(-2.5deg);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Free collage canvas: children are positioned by inline custom properties
   so each spread can be composed individually.
   -------------------------------------------------------------------------- */

/* Positions are percentages of the page face, so a composition holds together
   at every book size instead of drifting apart as the page scales. */
.collage {
  position: absolute;
  inset: 0;
  z-index: 10;
}

.collage > * {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, auto);
  transform: rotate(var(--r, 0deg));
  transform-origin: center;
}

/* Photos in the collage: white border, uneven, shadowed like real prints */
.photo {
  display: block;
  padding: clamp(3px, 0.55vw, 7px);
  border: 0;
  background: #ffffff;
  cursor: zoom-in;
  z-index: 20;
  box-shadow:
    0 1px 2px rgba(60, 52, 38, 0.18),
    0 10px 22px -10px rgba(60, 52, 38, 0.5);
  transition: transform 0.32s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.32s ease;
}

.photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--ar, 4 / 3);
  object-fit: cover;
  object-position: center 45%;
  pointer-events: none;
}

/* The caption is written on the print itself, so the text inside the button
   exists only for the lightbox and screen readers. */
.photo .snap-caption,
.hero-photo .snap-caption {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* polaroid variant: deep chin, caption written on the border */
.photo-polaroid {
  padding: clamp(4px, 0.6vw, 8px) clamp(4px, 0.6vw, 8px) clamp(16px, 2vw, 26px);
}

.photo-polaroid .photo-note {
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: clamp(2px, 0.4vw, 5px);
  font-family: 'Kalam', cursive;
  font-size: clamp(0.52rem, 0.85vw, 0.74rem);
  line-height: 1.2;
  color: #3a3428;
  text-align: center;
  transform: rotate(-0.6deg);
}

.photo:hover,
.photo:focus-visible {
  transform: rotate(0deg) scale(1.05) translateY(-6px);
  box-shadow:
    0 2px 4px rgba(60, 52, 38, 0.2),
    0 22px 40px -14px rgba(60, 52, 38, 0.6);
  z-index: 25 !important;
  outline: none;
}

.photo:focus-visible {
  box-shadow: 0 0 0 3px rgba(47, 86, 184, 0.55), 0 22px 40px -14px rgba(60, 52, 38, 0.6);
}

/* --------------------------------------------------------------------------
   Ephemera: tape, clips, tickets, film. Decorative, aria-hidden in markup.
   -------------------------------------------------------------------------- */

/* Washi tape strip: semi-transparent so paper shows through */
.tape {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 58px);
  height: var(--h, 20px);
  transform: rotate(var(--r, -4deg));
  background: linear-gradient(125deg, rgba(226, 216, 196, 0.82), rgba(240, 233, 217, 0.88));
  border-left: 1px dashed rgba(150, 136, 112, 0.5);
  border-right: 1px dashed rgba(150, 136, 112, 0.5);
  box-shadow: 0 1px 2px rgba(60, 52, 38, 0.14);
  pointer-events: none;
  z-index: 24;
}

.tape-blue {
  background: linear-gradient(125deg, rgba(178, 206, 236, 0.8), rgba(203, 224, 245, 0.85));
}

.tape-pink {
  background: linear-gradient(125deg, rgba(240, 202, 210, 0.8), rgba(248, 221, 227, 0.85));
}

/* Binder clip at the top of the page */
.clip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: clamp(17px, 2.2vw, 26px);
  height: clamp(27px, 3.4vw, 40px);
  transform: rotate(var(--r, 0deg));
  pointer-events: none;
  z-index: 28;
}

.clip::before {
  /* the black body */
  content: "";
  position: absolute;
  inset: 30% 0 0;
  border-radius: 2px 2px 3px 3px;
  background: linear-gradient(100deg, #2a2a2c, #4a4a4e 45%, #232326);
  box-shadow: 0 3px 7px -2px rgba(20, 20, 22, 0.65);
}

.clip::after {
  /* the wire arms */
  content: "";
  position: absolute;
  top: 0;
  left: 19%;
  right: 19%;
  height: 50%;
  border: 1.6px solid #b9b9bd;
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
}

/* Paper clip: a thin wire loop over the page edge */
.paperclip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: clamp(10px, 1.3vw, 15px);
  height: clamp(29px, 3.6vw, 42px);
  transform: rotate(var(--r, 0deg));
  border: 1.6px solid #c3a668;
  border-radius: 8px;
  pointer-events: none;
  z-index: 26;
}

.paperclip::after {
  content: "";
  position: absolute;
  inset: 5px 4px 9px;
  border: 1.6px solid #cdb277;
  border-radius: 6px;
}

/* Ticket / postcard stub with a torn perforated edge */
.stub {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 74px);
  transform: rotate(var(--r, 0deg));
  padding: 8px 6px;
  background: #f3ece0;
  box-shadow: 0 1px 2px rgba(60, 52, 38, 0.16), 0 8px 16px -8px rgba(60, 52, 38, 0.4);
  font-family: 'Space Mono', monospace;
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #6f6552;
  text-align: center;
  z-index: 14;
}

.stub-vertical {
  writing-mode: vertical-rl;
  padding: 10px 7px;
}

.stub strong {
  display: block;
  font-size: 0.62rem;
  color: #2f56b8;
  letter-spacing: 0.1em;
}

/* dotted tear line down one side */
.stub::before {
  content: "";
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 5px;
  border-left: 1px dashed rgba(140, 126, 102, 0.55);
}

/* Film strip: sprocket holes top and bottom */
.filmstrip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 108px);
  transform: rotate(var(--r, 0deg));
  padding: 7px 0;
  background:
    repeating-linear-gradient(90deg, #f0ece2 0 4px, transparent 4px 10px) top / 100% 5px repeat-x,
    repeating-linear-gradient(90deg, #f0ece2 0 4px, transparent 4px 10px) bottom / 100% 5px repeat-x,
    #1d1a17;
  box-shadow: 0 8px 18px -9px rgba(30, 26, 22, 0.6);
  z-index: 15;
}

.filmstrip img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  opacity: 0.94;
}

/* Hand-drawn star / sparkle, drawn in CSS rather than an emoji glyph */
.star {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s, 16px);
  height: var(--s, 16px);
  transform: rotate(var(--r, 0deg));
  pointer-events: none;
  z-index: 22;
  background: var(--ink, #2f56b8);
  opacity: var(--o, 0.85);
  /* four-point sparkle */
  clip-path: polygon(50% 0%, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0% 50%, 42% 42%);
}

.star-outline {
  background: none;
  border: 1.6px solid var(--ink, #2f56b8);
  clip-path: none;
  border-radius: 0;
  /* five-point outline star via rotated squares reads poorly; use a glyph mask */
  -webkit-mask: none;
  transform: rotate(var(--r, 0deg));
}

/* Torn paper note: soft irregular edge, like a scrap ripped from a pad */
.scrap {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 150px);
  transform: rotate(var(--r, 0deg));
  padding: var(--space-3) var(--space-4);
  font-family: 'Kalam', cursive;
  font-size: 0.82rem;
  line-height: 1.55;
  color: #2b2419;
  background: var(--bg, #fdf3d4);
  box-shadow: 0 1px 2px rgba(60, 52, 38, 0.14), 0 10px 20px -10px rgba(60, 52, 38, 0.45);
  z-index: 16;
}

.scrap-blue { --bg: #cfe0f5; }
.scrap-mint { --bg: #dfeee0; }
.scrap-pink { --bg: #f8dee4; }

/* a sticky note's gummed strip along the top */
.scrap-sticky::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 26%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0));
  pointer-events: none;
}

/* Quote in a taped-down strip, as in the reference */
.quote-strip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 170px);
  transform: rotate(var(--r, 0deg));
  padding: var(--space-2) var(--space-3);
  background: #f6f2e6;
  box-shadow: 0 1px 2px rgba(60, 52, 38, 0.16), 0 8px 16px -9px rgba(60, 52, 38, 0.42);
  font-family: 'Kalam', cursive;
  font-size: 0.78rem;
  line-height: 1.5;
  color: #2b2419;
  z-index: 18;
}

/* --------------------------------------------------------------------------
   Loose handwriting on the collage, captions written straight onto the page
   beside a photo rather than inside a caption box. This is most of what makes
   the reference spreads read as written on, not laid out.
   -------------------------------------------------------------------------- */

.scrawl {
  font-family: 'Kalam', cursive;
  font-size: clamp(0.6rem, 1.05vw, 0.9rem);
  line-height: 1.4;
  color: #2f56b8;
  z-index: 21;
  pointer-events: none;
  text-wrap: balance;
}

.scrawl-dark { color: #2f2a20; }
.scrawl-red { color: #a8452c; }

/* a bigger, looser hand for the one phrase per spread that carries it */
.scrawl-big {
  font-family: 'Caveat', cursive;
  font-weight: 700;
  font-size: clamp(1rem, 2.1vw, 1.55rem);
  line-height: 1.1;
  color: #a8452c;
}

/* A small typed label, like a date stamped on the back of a print */
.tag {
  font-family: 'Space Mono', ui-monospace, monospace;
  font-size: clamp(0.42rem, 0.7vw, 0.58rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #8a7f6a;
  z-index: 21;
  pointer-events: none;
}

/* Hand-drawn arrow pointing from an annotation to the thing it describes.
   Drawn as an SVG stroke so the curve stays smooth at any size. */
.arrow {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w, 46px);
  transform: rotate(var(--r, 0deg));
  z-index: 21;
  pointer-events: none;
  opacity: 0.7;
}

.arrow svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.arrow path {
  fill: none;
  stroke: var(--ink, #2f56b8);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Reveal: the ephemera settle onto the page as the spread comes into view,
   so opening a page feels like things landing rather than appearing. */
@media (prefers-reduced-motion: no-preference) {
  .collage > * {
    animation: settle 0.5s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 45ms);
  }
}

@keyframes settle {
  from {
    opacity: 0;
    transform: rotate(var(--r, 0deg)) translateY(10px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: rotate(var(--r, 0deg));
  }
}

/* --------------------------------------------------------------------------
   Collage on small screens: absolute placement can't survive a stacked
   layout, so the pieces reflow into a simple readable column.
   -------------------------------------------------------------------------- */

@media (max-width: 700px) {
  .collage {
    position: relative;
    inset: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-4);
  }

  .collage > * {
    position: relative;
    left: auto;
    top: auto;
    transform: rotate(var(--r, 0deg));
    animation: none;
  }

  .photo { width: calc(50% - var(--space-2)); }

  .scrawl,
  .scrawl-big,
  .tag { width: 100%; }

  /* pure decoration; it has no room to breathe on a phone */
  .tape,
  .clip,
  .paperclip,
  .star,
  .arrow,
  .filmstrip,
  .stub { display: none; }

  .scrap,
  .quote-strip { width: 100%; }
}
