/* ============================================================
   CLOSER — film layout & chapter grammar
   Motion lives in js/film.js; this file is geometry and voice.
   ============================================================ */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

html {
  scroll-behavior: auto; /* Lenis owns scrolling */
}

body {
  font-family: var(--font-body);
  font-weight: 380;
  color: var(--c-ink);
  background: var(--c-bone);
  overflow-x: clip;
}

/* The ground — one fixed layer whose colour the film scrubs. */
.ground {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--c-bone);
}

img {
  max-width: 100%;
  display: block;
}

::selection {
  background: var(--c-amber);
  color: var(--c-bone);
}

/* ---------------- ambient watercolour ---------------- */

.pigment-defs {
  position: absolute;
  width: 0;
  height: 0;
}

.pigments {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* no blend: plain alpha compositing keeps 4 moving layers cheap.
     JS fades this wrapper out as the film darkens — the wash belongs
     to the light half of the story.

     ⚠️ `z-index: 0` IS THAT SAME RULE, FOR TIER C. LOAD-BEARING.

     Tier A fades the wash with a GSAP scrub (js/option4.js:209-213,
     `.chapter--night` top-bottom → top-top). Tier C returns before a
     single gsap.* call, so it cannot have that, and a scroll listener
     is out too — `stCount === 0` is a tested property of that tier.

     What removes the wash there instead is paint order. `.film` below
     is `position: relative; z-index: 1`, so every `.chapter` in it
     paints ABOVE this layer, and the opaque night/golden/ring grounds
     at the reduced-motion block (:683 and its html.static twin) occlude
     the wash exactly where Tier A's scrub has already taken it to 0.
     Same rendered result, reached without JavaScript. This is also why
     an opaque background on ALL seven chapters is forbidden there: it
     would occlude the wash over the light half too, which Tier A shows.

     Computed style cannot see any of this — `.pigments` computes
     `opacity: 1` across the whole Tier C page and is invisible over
     half of it. MEASURED instead, from the rendered frame: lifting this
     layer to `z-index: 2` moves the dark half's bare ground by 38-95
     channels and the light half's by 0, at both gate viewports.
     tests/gates/tier-c.test.mjs, "the wash belongs to the light half
     (C5)"; tests/helpers/paint.mjs for how and why it is read back out
     of the PNG. Four ways of breaking it were run and each turns that
     gate red — including `html.static .pigments { opacity: 0 }`, which
     reads like the safe version of this note and takes 10 of its 11
     assertions down with it. */
}

.pigment {
  position: absolute;
  width: 56vw;
  height: 70vh;
  overflow: visible;
  will-change: transform, opacity; /* painted once, moved by the compositor */
  /* two animations, different periods — drift never reads as a loop */
  animation:
    pigment-drift 13s ease-in-out infinite alternate,
    pigment-breathe 8s ease-in-out infinite alternate;
}

.pigment--1 { left: -16vw; bottom: -18vh; }
.pigment--2 {
  right: -16vw;
  top: -16vh;
  animation: pigment-drift-r 16s ease-in-out -6s infinite alternate, pigment-breathe 11s ease-in-out -4s infinite alternate;
}
.pigment--3 {
  left: -17vw;
  top: -17vh;
  animation: pigment-drift-r 12s ease-in-out -9s infinite alternate, pigment-breathe 9s ease-in-out -7s infinite alternate;
}
.pigment--4 {
  right: -17vw;
  bottom: -17vh;
  animation: pigment-drift 17s ease-in-out -3s infinite alternate, pigment-breathe 7s ease-in-out -2s infinite alternate;
}

@keyframes pigment-drift {
  from { transform: translate(-9%, -6%) rotate(-10deg) scale(0.98); }
  to { transform: translate(9%, 7%) rotate(11deg) scale(1.12); }
}

@keyframes pigment-drift-r {
  from { transform: translate(9%, 6%) rotate(10deg) scale(1.12); }
  to { transform: translate(-9%, -7%) rotate(-11deg) scale(0.98); }
}

@keyframes pigment-breathe {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

/* content rides above the ambient layer */
.film,
.details,
.marquee,
footer {
  position: relative;
  z-index: 1;
}

/* R1 */
@media (prefers-reduced-motion: reduce) {
  .pigment {
    animation: none;
  }
}

/* R1 class twin of the @media (prefers-reduced-motion: reduce) block above.
   A URL parameter cannot trigger a media query; ?portal=1&motion=off sets
   html.static (js/option4.js). Edit both or neither. */
html.static .pigment {
  animation: none;
}

@media (max-width: 767px) {
  .pigment {
    width: 70vw;
    height: 44vh;
  }

  .pigment--3,
  .pigment--4 {
    display: none;
  }
}

/* ---------------- wordmark ---------------- */

.wordmark {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2) var(--space-3);
  mix-blend-mode: difference;
  color: #f0e9dc;
  pointer-events: none;
}

.wordmark__cta {
  position: absolute;
  right: var(--space-3);
  top: 50%;
  translate: 0 -50%;
}

.wordmark__row {
  display: flex;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-1);
  letter-spacing: 0.04em; /* Beth: names more compact, position unchanged */
}

.wordmark__row .wm {
  display: inline-block;
  will-change: transform;
}

.wordmark__row .wm--amp {
  font-style: italic;
  font-weight: 400;
}

.wordmark__row .wm--sp {
  width: 0.38em;
}

.wordmark__cta {
  pointer-events: auto;
  font-size: var(--step--1);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 0.2em;
}

/* invisible tap pad — the text is thinner than a fingertip */
.wordmark__cta::after {
  content: '';
  position: absolute;
  inset: -14px;
}

/* ---------------- chapters: shared ---------------- */

.film {
  position: relative;
}

.chapter {
  position: relative;
}

.chapter__pin {
  height: 100vh;
  height: 100svh;
  overflow: clip;
  position: relative;
  display: grid;
}

.clause {
  /* Beth's calls: every story sentence in the script face (the "nights ran
     long" font), all at ONE size — the "until everything turned to gold"
     scale — so the words never dominate the photographs. Single-weight
     cursive: no tracking, no faux bolds, leading that clears ascenders. */
  font-family: var(--font-script);
  font-weight: 400;
  font-size: var(--step-3);
  color: var(--c-umber);
  line-height: 1.06;
  text-wrap: balance;
  z-index: 3;
}

.clause .ch {
  display: inline-block;
  will-change: transform, opacity;
}

.panel {
  position: absolute;
  overflow: clip;
  will-change: transform;
}

.panel picture,
.cell picture {
  display: block;
  width: 100%;
  height: 100%;
}

.panel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}

/* ---- ch1 · specks — bone, vast, letters far apart ---- */

.chapter--specks {
  height: 260vh;
}

.chapter--specks .clause {
  position: absolute;
  left: 6vw;
  top: 24vh;
  width: 52vw;
  animation: clause-rise 1.5s var(--ease-film) both;
}

/* the law: CSS entrance animates the h1, the GSAP exit scrub animates this
   inner span — they must never share an element or the refresh pass bakes
   the entrance's first frame (opacity 0) into inline styles */
.chapter--specks .clause__inner {
  display: block;
}

/* The opening frame is already alive — a one-shot CSS entrance.
   Kept out of GSAP so the scrub can never fight it (scroll-back stays intact). */
@keyframes clause-rise {
  from {
    opacity: 0;
    transform: translateY(0.4em);
  }
}

@keyframes panel-in {
  from {
    opacity: 0;
  }
}

/* Entrance runs once at load; the GSAP scrub only ever touches the clause
   on the way OUT (0.72+), so the two never fight over a live property. */
.chapter--specks .panel--a img {
  animation: panel-in 1.6s ease-out both;
}

.chapter--specks .panel--a {
  right: 7vw;
  top: 8vh;
  width: 34vw;
  height: 84vh;
}

/* ---- ch2 · figures — three rising columns ---- */

.chapter--figures {
  height: 320vh;
}

.chapter--figures .clause {
  position: absolute;
  left: 50%;
  top: 12vh;
  transform: translateX(-50%);
  white-space: nowrap;
}

.chapter--figures .cols {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(3, 24vw);
  justify-content: center;
  column-gap: 4vw;
  align-items: center;
}

.chapter--figures .panel {
  position: relative;
  height: 64vh;
  top: auto;
}

/* keep both of them inside the tall crops: Zion seats the couple at ~65%
   across the frame, Machu Picchu at ~30% — a centred crop loses one of
   them on narrow panels */
.chapter--figures .cols .panel:nth-child(1) img {
  object-position: 66% 50%;
}

.chapter--figures .cols .panel:nth-child(2) img {
  object-position: 32% 50%;
}

/* ---- ch3 · bodies — the one horizontal run ---- */

.chapter--bodies {
  height: 380vh;
}

.chapter--bodies .clause {
  position: absolute;
  left: 6vw;
  top: 10vh;
  z-index: 1;
}

.chapter--bodies .track {
  position: absolute;
  top: 30vh;
  left: 0;
  display: flex;
  gap: 5vw;
  padding-left: 40vw;
  will-change: transform;
}

.chapter--bodies .track .panel {
  position: relative;
  flex: none;
  width: 30vw;
  height: 58vh;
}

.chapter--bodies .track .panel:nth-child(odd) {
  translate: 0 6vh;
}

/* ---- ch4 · faces — one column, relay of wipes; dusk falls ---- */

.chapter--faces {
  height: 340vh;
}

.chapter--faces .clause {
  position: absolute;
  left: 7vw;
  bottom: 12vh;
  width: 34vw;
  z-index: 3;
}

.chapter--faces .stack {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: min(44vw, 58vh);
  aspect-ratio: 3 / 4;
}

.chapter--faces .stack .panel {
  inset: 0;
  width: 100%;
  height: 100%;
}

/* ---- ch5 · night — dark scatter, one chapter of neon ---- */

.chapter--night {
  height: 320vh;
  color: var(--c-cream);
}

.chapter--night .clause {
  position: absolute;
  left: 50%;
  top: 14vh;
  transform: translateX(-50%);
  color: var(--c-cream);
  white-space: nowrap;
  z-index: 3;
}

.chapter--night .panel {
  box-shadow: 0 30px 80px rgb(0 0 0 / 0.55);
}

.chapter--night .panel--a {
  left: 12vw;
  top: 30vh;
  width: 24vw;
  height: 58vh;
  rotate: -4deg;
}

.chapter--night .panel--b {
  left: 40vw;
  top: 40vh;
  width: 30vw;
  height: 44vh;
  rotate: 2deg;
  z-index: 2;
}

.chapter--night .panel--c {
  right: 8vw;
  top: 24vh;
  width: 20vw;
  height: 52vh;
  rotate: 5deg;
}

/* ---- ch6 · golden — the aspect release ---- */

.chapter--golden {
  height: 280vh;
}

.chapter--golden .panel--hero {
  inset: 0;
  width: 100%;
  height: 100%;
  position: absolute;
}

/* Beth's face sits at ~64% across the landscape frame; portrait crops
   only show a third of the width, so bias the window onto her. Desktop
   crops vertically, where the x offset is a no-op. */
.chapter--golden .panel--hero img {
  object-position: 64% 50%;
}

.chapter--golden .names {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 10vh;
  transform: translateX(-50%);
  font-family: var(--font-script);
  font-size: var(--step-4); /* a notch above the clauses — their names, tamed */
  font-weight: 400;
  color: var(--c-cream);
  white-space: nowrap;
  text-shadow: 0 2px 40px rgb(26 21 18 / 0.5);
}

.chapter--golden .clause {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 12vh;
  transform: translateX(-50%);
  color: var(--c-cream);
  white-space: nowrap;
}

/* ---- ch7 · the ring — iris, then the facts ---- */

.chapter--ring {
  height: 300vh;
}

.chapter--ring .panel--hero {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  will-change: clip-path;
}

.chapter--ring .panel--hero img {
  object-position: 57% 50%; /* centres the ring inside portrait crops */
}

/* the closing line rides above the photograph, gone before the iris */
.chapter--ring .clause {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 12vh;
  transform: translateX(-50%);
  color: var(--c-cream);
  white-space: nowrap;
  text-shadow: 0 2px 30px rgb(26 21 18 / 0.45);
}

/* ---------------- after the film ---------------- */

.details {
  position: relative;
  background: var(--c-ink);
  color: var(--c-cream);
  padding: var(--space-5) 6vw var(--space-4);
}

.crest {
  text-align: center;
  margin-bottom: var(--space-4);
}

.crest__monogram {
  width: clamp(6.5rem, 10vw, 9rem);
  color: var(--c-cream);
  margin-bottom: var(--space-3);
}

.crest__house {
  display: block;
  width: min(88vw, 40rem);
  margin: 0 auto;
  color: var(--c-amber);
}

.crest__place {
  margin-top: var(--space-2);
  font-size: var(--step--1);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--c-amber);
}

.details h2 {
  font-family: var(--font-script);
  font-size: var(--step-3);
  font-weight: 400;
  margin-bottom: var(--space-4);
  text-align: center;
}

.details dl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-3);
}

.details dt {
  font-size: var(--step--1);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--c-amber);
  margin-bottom: var(--space-1);
}

.details dd {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 420;
}

.marquee {
  background: var(--c-ink);
  overflow: clip;
  padding-bottom: var(--space-4);
}

.marquee__track {
  display: flex;
  gap: 2vw;
  width: max-content;
  animation: marquee 42s linear infinite;
}

.marquee__track .cell {
  width: 16vw;
  min-width: 10rem;
  aspect-ratio: 3 / 4;
  overflow: clip;
}

.marquee__track .cell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@keyframes marquee {
  to {
    transform: translateX(-50%);
  }
}

footer {
  background: var(--c-ink);
  color: var(--c-cream);
  padding: var(--space-3) 6vw var(--space-4);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* ---------------- mobile ---------------- */

@media (max-width: 767px) {
  .wordmark { justify-content: flex-start; }
  .wordmark__row { font-size: var(--step-0); }

  /* the top strip can't hold both the wordmark and the cta at phone
     widths — they collide. The cta becomes a bottom-centre float. */
  .wordmark__cta {
    position: fixed;
    top: auto;
    right: auto;
    left: 50%;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 20px);
    translate: -50% 0;
  }

  .chapter--specks { height: 220vh; }
  .chapter--specks .clause { width: 88vw; top: 14vh; }
  .chapter--specks .panel--a { right: -4vw; top: 40vh; width: 72vw; height: 52vh; }

  .chapter--figures { height: 240vh; }
  .chapter--figures .clause { white-space: normal; width: 90vw; text-align: center; }
  .chapter--figures .cols { grid-template-columns: repeat(3, 28vw); column-gap: 3vw; align-items: end; padding-bottom: 16vh; }
  .chapter--figures .panel { height: 44vh; }

  .chapter--bodies { height: 300vh; }
  .chapter--bodies .track { top: 34vh; padding-left: 12vw; gap: 6vw; }
  .chapter--bodies .track .panel { width: 62vw; height: 52vh; }

  .chapter--faces { height: 260vh; }
  .chapter--faces .stack { width: min(80vw, 52vh); }
  .chapter--faces .clause { width: 86vw; left: 7vw; bottom: 8vh; }

  .chapter--night { height: 260vh; }
  .chapter--night .clause { white-space: normal; width: 92vw; text-align: center; }
  .chapter--night .panel--a { left: 6vw; top: 28vh; width: 48vw; height: 44vh; }
  .chapter--night .panel--b { left: 34vw; top: 46vh; width: 54vw; height: 38vh; }
  .chapter--night .panel--c { right: 4vw; top: 20vh; width: 38vw; height: 36vh; }

  .chapter--golden { height: 220vh; }
  .chapter--golden .clause { white-space: normal; width: 92vw; text-align: center; }
  .chapter--ring { height: 240vh; }
  .chapter--ring .clause { white-space: normal; width: 92vw; text-align: center; }

  .marquee__track .cell { width: 38vw; }
}

/* ---------------- reduced motion: the designed static stack ----------------

   THE GROUND IS INFORMATION, NOT DECORATION.

   `.ground` is a static var(--c-bone) #f2eee3 and the ONLY thing that ever
   darkens it is the GSAP scrub at js/option4.js:181-198, which never runs
   here — the reduced branch of boot() returns before a single gsap.* call and
   that early return is what keeps the CSS-vs-GSAP ownership map identical
   across all three tiers. So the three chapters whose clauses are
   var(--c-cream) #f0e9dc rendered ivory-on-ivory: MEASURED 1.04:1, at both
   gate viewports, invisible. Beth and Will rewrote those seven lines
   themselves on 2026-08-16.

   The fix is pure CSS, and the four colours are lifted verbatim from the
   scrub stops so the film's designed palette is preserved exactly. NOT with
   a gsap.set() on the reduced path, and NOT with a fifth `.ground` tween —
   the four that exist already share one property and are made safe only by
   `immediateRender: false` at option4.js:194.

   ⚠️ NO BLANKET `.chapter { background: … }` RULE. `.pigments` is
   `position: fixed; inset: 0; z-index: 0`; an opaque background on every
   chapter would silently delete the watercolour layer from the whole page
   INCLUDING the light half, where Tier A shows it. Only these three chapters
   carry light-on-light type, so only these three get a ground. Asserted both
   ways in tests/gates/tier-c.test.mjs.
   -------------------------------------------------------------------------- */

/* R2 */
@media (prefers-reduced-motion: reduce) {
  .chapter--specks .clause,
  .chapter--specks .panel--a img {
    animation-duration: 0.01s;
    animation-delay: 0s;
  }
  .chapter { height: auto !important; }
  .chapter__pin { height: auto; min-height: 100svh; padding: var(--space-4) 0; }
  .panel, .clause { position: relative !important; inset: auto !important; transform: none !important; translate: none !important; }
  .chapter--specks .panel, .chapter--night .panel { width: min(88vw, 34rem) !important; height: auto !important; margin: var(--space-2) auto; left: auto !important; right: auto !important; top: auto !important; }
  .chapter--bodies .track { position: relative; flex-wrap: wrap; padding: 0 6vw; }
  .chapter--bodies .track .panel { width: min(88vw, 22rem); height: auto; }
  .chapter--faces .stack { position: relative; left: auto; top: auto; translate: none; margin: 0 auto; }
  .clause { padding: 0 6vw; }
  .marquee__track { animation: none; flex-wrap: wrap; width: auto; }
  .chapter--ring .panel--hero, .chapter--golden .panel--hero { position: relative; }

  /* the static equivalent of the ground scrub — see the note above */
  .ground { display: none; }
  .chapter--night { background: var(--c-night); }   /* #0d0e0d */
  .chapter--golden { background: #1f4d3a; }
  .chapter--ring { background: #135442; }           /* the landing green */

  /* A hash target nobody can see is not reachable in any useful sense. Two
     tokens, no new colour: --c-umber reads 8.31:1 on the ivory body, --c-cream
     7.33-16.02:1 on the three dark grounds above — the same per-chapter
     reasoning the grounds themselves use. */
  .chapter:focus-visible { outline: 3px solid var(--c-umber); outline-offset: -6px; }
  .chapter--night:focus-visible,
  .chapter--golden:focus-visible,
  .chapter--ring:focus-visible { outline-color: var(--c-cream); }
}

/* R2 class twin of the @media (prefers-reduced-motion: reduce) block above.
   A URL parameter cannot trigger a media query; ?portal=1&motion=off sets
   html.static (js/option4.js). Edit both or neither.

   REPLICATED, NOT SIMPLIFIED. `animation-duration: 0.01s; animation-delay: 0s`
   is deliberately not `animation: none`: these are `both`-filled one-shot
   entrances, so shortening the duration runs them to their END state, whereas
   `animation: none` reverts to the base computed value and changes the frame. */
html.static .chapter--specks .clause,
html.static .chapter--specks .panel--a img {
  animation-duration: 0.01s;
  animation-delay: 0s;
}
html.static .chapter { height: auto !important; }
html.static .chapter__pin { height: auto; min-height: 100svh; padding: var(--space-4) 0; }
html.static .panel, html.static .clause { position: relative !important; inset: auto !important; transform: none !important; translate: none !important; }
html.static .chapter--specks .panel, html.static .chapter--night .panel { width: min(88vw, 34rem) !important; height: auto !important; margin: var(--space-2) auto; left: auto !important; right: auto !important; top: auto !important; }
html.static .chapter--bodies .track { position: relative; flex-wrap: wrap; padding: 0 6vw; }
html.static .chapter--bodies .track .panel { width: min(88vw, 22rem); height: auto; }
html.static .chapter--faces .stack { position: relative; left: auto; top: auto; translate: none; margin: 0 auto; }
html.static .clause { padding: 0 6vw; }
html.static .marquee__track { animation: none; flex-wrap: wrap; width: auto; }
html.static .chapter--ring .panel--hero, html.static .chapter--golden .panel--hero { position: relative; }

html.static .ground { display: none; }
html.static .chapter--night { background: var(--c-night); }   /* #0d0e0d */
html.static .chapter--golden { background: #1f4d3a; }
html.static .chapter--ring { background: #135442; }           /* the landing green */

html.static .chapter:focus-visible { outline: 3px solid var(--c-umber); outline-offset: -6px; }
html.static .chapter--night:focus-visible,
html.static .chapter--golden:focus-visible,
html.static .chapter--ring:focus-visible { outline-color: var(--c-cream); }
