/* ============================================================
   PMR — site styles
   Landing mechanic ported from ralliedrink.com: a full-viewport
   three-column infinite image marquee driven by wheel/drag
   inertia rather than native page scroll.
   ============================================================ */

:root {
  --bg:          #d9d9d9;   /* page ground */

  /* Every piece of type on the site is #661717 at full opacity — --ink-soft is
     the same value on purpose, so captions and labels don't drift paler. If
     tonal hierarchy is ever wanted back, --ink-soft is the only knob to turn.
     --rule stays translucent: it draws borders, not text. */
  --ink:         #661717;
  --ink-soft:    #661717;
  --rule:        rgba(102, 23, 23, 0.28);

  /* The header rule is its own token, matched to analuisacorrigan.com, whose
     header sets border-bottom: 1px solid rgba(72,72,73,0.91). Same 1px width
     the site already used -- what made theirs read heavier was opacity: 0.91
     against our 0.28. Raising alpha here, not width, is what closes the gap.
     Bump --rule-head-w if a literally thicker line is ever wanted. */
  --rule-head:   rgba(102, 23, 23, 0.9);
  --rule-head-w: 1px;

  --card:        #9fa4a6;   /* popup card grey, sampled from the artwork */
  --accent:      #661717;
  --accent-soft: rgba(102, 23, 23, 0.55);

  --mono:  "Inter", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --serif: "Inter", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;

  /* HEADER 1 (the wordmark) — modelled on analuisacorrigan.com, whose header
     sets Proxima Nova 400 at 15.07px, uppercase, 0.010em tracking, line-height
     1.2, at a 1265px viewport. Proxima Nova is Adobe-only; Inter is already
     loaded here and is near-indistinguishable in uppercase at this size, so
     HEADER 1 costs no extra font download.

     HEADER 2 (project titles) is now IDENTICAL to HEADER 1 — same family,
     weight, size and tracking, all read from these four tokens, so the two
     cannot diverge. The whole site is therefore Inter 400 at one weight; the
     earlier Cormorant Garamond display face and the separate --title-weight /
     --title-track tokens have been removed along with it. */
  --wordmark-family: var(--mono);
  --wordmark-weight: 400;
  --wordmark-size: clamp(13px, 1.2vw, 16px);
  --wordmark-track: 0.01em;
  --wordmark-leading: 1.2;

  /* Small-UI scale: nav, footer, labels, captions, section heads. */
  --ui-size: 10px;

  /* Fixed header height. main reserves this exact value, which is what stops
     the header clipping the first row — keep the two in step. */
  --head-h: 72px;

  /* Footer band height. main reserves exactly this much as margin-bottom, so
     the two must stay in step -- see the .site-foot reveal comment. The
     mockup band is 311px tall at 1440px wide (21.6vw); clamped so it stays a
     band rather than a screen on very wide or very narrow viewports. */
  --foot-h:     clamp(190px, 21.6vw, 300px);

  /* One gutter for the whole site, capped at the reference's 30px. The header
     wordmark, the work-index titles, the project pages and the footer all
     indent by this, so their left edges line up at every width. */
  --gutter: clamp(18px, 4vw, 30px);
  --maxw:   1500px;

  /* Marquee tuning — mirrors the reference implementation */
  --ms-gap: 52px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

::selection { background: var(--accent); color: var(--bg); }

.u-visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Small uppercase mono label — the workhorse UI type */
.label {
  font-family: var(--mono);
  font-size: var(--ui-size);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* ============================================================
   1. LANDING — locked viewport + marquee
   ============================================================ */

/* Only the landing locks scroll; content pages scroll normally. */
body.is-locked { overflow: hidden; height: 100%; }

.ms-wrap {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  display: flex;
  overflow: hidden;
  cursor: ns-resize;
  touch-action: none;
}

.ms-col {
  position: relative;
  overflow: hidden;
  padding: 0 10px;
  transform: translateZ(0);
}
/* Three equal columns, so the wall is symmetrical about the centre line and
   the overlay card sits dead centre over the middle one. Variety now comes
   from the images' own differing heights, not from uneven column widths.
   Must stay in step with WIDE_COLS / NARROW_COLS in marquee.js. */
.ms-col { flex: 1 1 0; min-width: 0; }

.ms-col-inner {
  display: flex;
  flex-direction: column;
  will-change: transform;
}

.ms-col-inner img {
  width: 100%;
  height: auto;
  flex-shrink: 0;
  margin-bottom: var(--ms-gap);
  opacity: 0;
  transition: opacity 0.6s ease;
  background: rgba(22, 22, 15, 0.04);
}
.ms-col-inner img.ms-visible { opacity: 1; }

/* ---- centred overlay card ---- */

.landing-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  z-index: 5;
}

/* Fully vector: a CSS card, the monogram as SVG, and real text. Nothing here
   is a raster, so it stays sharp at any size or pixel density.

   Every inner measurement derives from --card-w. Do NOT switch these to
   percentages: percentage padding resolves against the containing block (the
   viewport), not the card, which silently breaks the proportions. */
.card {
  --card-w: clamp(238px, 22vw, 300px);
  /* Matched to the logo's own line weight: the oval ring measures 3px across at
     the artwork's native 687px, and the mark renders at 76.2% of the card, so
     3/687 * 0.762 = 0.333% of the card width. Expressed as a fraction so the
     rules and the logo stay matched at any card size. */
  --card-line: calc(var(--card-w) * 0.00333);
  pointer-events: auto;
  width: var(--card-w);
  aspect-ratio: 294 / 322;
  padding: calc(var(--card-w) * 0.030);
  background: var(--card);
  animation: card-in 1.1s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Opacity only — deliberately no transform. A translate here would leave the
   card off-centre for the duration of the animation (and permanently if it is
   ever interrupted or throttled), and this card has to sit dead centre. */
@keyframes card-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.card__inner {
  height: 100%;
  border: var(--card-line) solid var(--accent);
  padding: calc(var(--card-w) * 0.045) 0 calc(var(--card-w) * 0.072);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

.card__mark { width: 82%; height: auto; display: block; }

.card__nav {
  width: 78%;
  border: var(--card-line) solid var(--accent);
}

.card__nav a {
  display: block;
  padding: 0.72em 0;
  text-align: center;
  color: var(--accent);
  font-family: var(--mono);
  /* 4.6% of the card matches the lettering size in the source artwork. */
  font-size: calc(var(--card-w) * 0.046);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: color 0.3s ease;
}
.card__nav a + a { border-top: var(--card-line) solid var(--accent); }

/* Hover lightens the lettering only — no fill. The borders use var(--accent)
   rather than currentColor, so they stay solid while the text fades. */
.card__nav a:hover { color: rgba(102, 23, 23, 0.6); }
.card__nav a:focus-visible {
  color: rgba(102, 23, 23, 0.6);
  outline: var(--card-line) solid var(--accent);
  outline-offset: calc(var(--card-line) * -2);
}


/* ---- the opening ---- */

/* The landing opens on a full-bleed ground with the monogram writing itself
   almost the full width of the viewport, shrinking onto the card as it goes;
   when the write finishes the ground fades and the site is behind it.

   The ground is --card on purpose. The mark's own SVG carries an opaque #9FA4A6
   plate, and the card behind it is the same value, so the shrinking mark has no
   visible edge and the handover at the end is a fade of the surround only --
   the monogram itself never changes, because the copy underneath is identical
   and already finished. Change --opening-ground alone to open on another colour;
   the mark's plate will then read as a rectangle against it. */
/* Both animations start paused and are released by opening.js once the monogram
   is in the page, so the shrink cannot get ahead of the write it is framing. */
.opening,
.opening__mark { animation-play-state: paused; }

body.is-writing .opening,
body.is-writing .opening__mark { animation-play-state: running; }

.opening {
  --opening-ground: var(--card);
  position: fixed;
  inset: 0;
  z-index: 10;
  background: var(--opening-ground);
  animation: opening-out 600ms ease 2171ms both;
}
/* Deliberately hit-testable while it is up: WORK and ABOUT are sitting invisible
   underneath, and a click landing on one of them mid-intro would navigate away
   from a page that looks like it is still loading. The visibility: hidden at the
   end of opening-out is what takes it back out of the way. */

@keyframes opening-out {
  from { opacity: 1; }
  99%  { opacity: 0; visibility: visible; }
  to   { opacity: 0; visibility: hidden; }
}

/* --w/--x/--y/--s are measured off the real .card__mark by assets/js/opening.js,
   so the shrink lands on it exactly rather than on a hand-tuned guess. Until
   that runs the mark is parked at the card's own size, which is also the
   no-JS resting state. Translating by -50% of the element's own box puts its
   centre on (--x, --y); scaling about that centre then leaves it there for the
   whole shrink, so the mark grows out of the card rather than drifting onto it. */
.opening__mark {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--w, 230px);
  transform-origin: 50% 50%;
  animation: opening-shrink 2171ms cubic-bezier(0.42, 0, 0.32, 1) both;
}
.opening__mark > svg { display: block; width: 100%; height: auto; }

/* It starts centred in the viewport and drifts onto the card's centre as it
   shrinks, rather than being pinned to the card's centre throughout. The card's
   mark sits high on the screen — WORK and ABOUT are under it — so scaling about
   that point makes the nearest edge the binding constraint, and on a short
   window that costs most of the opening size. The drift is 50–100px over the
   whole shrink and reads as part of the settle. */
@keyframes opening-shrink {
  from { transform: translate(calc(var(--x0, 50vw) - 50%), calc(var(--y0, 50vh) - 50%)) scale(var(--s, 4)); }
  to   { transform: translate(calc(var(--x, 50vw) - 50%), calc(var(--y, 50vh) - 50%)) scale(1); }
}

/* No intro at all under reduced motion: the card is simply there. */
@media (prefers-reduced-motion: reduce) {
  .opening { display: none; }
}


/* ============================================================
   2. CONTENT PAGES
   ============================================================ */

/* Pinned to the top of the viewport. Its height is a known constant
   (--head-h), and main reserves exactly that much, so it cannot clip the
   content beneath it. If the logo size changes, update --head-h to match. */
.site-head {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 20;
  height: var(--head-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--gutter);
  background: var(--bg);
  border-bottom: var(--rule-head-w) solid var(--rule-head);
}

/* Wordmark, not a graphic. Same family, weight and width as the project
   titles; tracking is left slightly open rather than using --title-track,
   because 23 characters at header size get cramped with negative spacing. */
.site-head__mark {
  display: flex;
  align-items: center;
  color: var(--accent);
  font-family: var(--wordmark-family);
  font-weight: var(--wordmark-weight);
  font-size: var(--wordmark-size);
  letter-spacing: var(--wordmark-track);
  line-height: var(--wordmark-leading);
  text-transform: uppercase;
  white-space: nowrap;
}

.site-nav { display: flex; gap: 22px; }
/* Identical to the footer text: Inter 400, 11px, 0.14em, uppercase. */
.site-nav a {
  position: relative;
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 400;
  font-size: var(--ui-size);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -4px;
  width: 100%; height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.site-nav a:hover::after,
.site-nav a[aria-current="page"]::after { transform: scaleX(1); }

/* padding-top clears the fixed header; margin-bottom uncovers the fixed
   footer. The opaque background is load-bearing, not decoration: without it
   the pinned footer would show through the whole page. See .site-foot.

   padding-bottom is the gap between the last block on the page -- the pager,
   or the final work-index row -- and the footer's rule. It replaces the
   margin-top the old in-flow footer carried; without it the pager's bottom
   border lands exactly on that rule. It must be padding, not margin: a margin
   here would collapse into margin-bottom and eat the reveal window. */
main {
  position: relative;
  z-index: 1;
  background: var(--bg);
  padding-top: calc(var(--head-h) + clamp(16px, 3vh, 40px));
  padding-bottom: clamp(72px, 12vh, 140px);
  margin-bottom: var(--foot-h);
}

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ---- page intro ---- */

/* The header sits in flow now, so this no longer needs to clear it. */
.intro { padding: clamp(18px, 4vh, 44px) 0 clamp(36px, 6vh, 64px); }

/* Subtitle — footer font and thickness, like every other non-title element. */
.intro__eyebrow {
  color: var(--accent);
  margin-bottom: 16px;
  font-family: var(--mono);
  font-weight: 400;
  letter-spacing: 0.036em;
}

/* HEADER 2 on the project pages — also identical to HEADER 1. This is the
   page's H1, so if it needs more presence, give it its own font-size here
   rather than changing the shared token. */
.intro__title {
  margin: 0;
  font-family: var(--wordmark-family);
  font-weight: var(--wordmark-weight);
  font-size: var(--wordmark-size);
  line-height: var(--wordmark-leading);
  letter-spacing: var(--wordmark-track);
  text-transform: uppercase;
}

/* The project pages carry no prose. Under the title sits the same subtitle the
   work index shows, then the credits — so the two views of a project agree, and
   there is no second description to keep in step with the first. */
.intro__sub {
  margin: 14px 0 0;
  max-width: 58ch;
  font-family: var(--serif);
  font-size: clamp(15px, 1.35vw, 19px);
  line-height: 1.62;
  color: var(--ink);
}

/* ---- brief ---- */

/* Rows, not a table: Role runs to a couple of lines on some projects and a few
   words on others, and a grid would either clip it or leave the short ones
   sitting in a mostly empty cell. */
.brief { margin: clamp(22px, 3.4vh, 34px) 0 0; }

.brief__row {
  display: grid;
  grid-template-columns: minmax(0, 6.5em) minmax(0, 1fr);
  gap: 8px 18px;
  padding: 11px 0;
  border-top: 1px solid var(--rule);
  max-width: 72ch;
}
.brief__row:last-child { border-bottom: 1px solid var(--rule); }

.brief dt { color: var(--ink-soft); margin: 0; padding-top: 2px; }
.brief dd {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1.55;
}

@media (max-width: 560px) {
  /* Two columns leaves ~4em for a Role that runs to a paragraph. */
  .brief__row { grid-template-columns: 1fr; gap: 4px; }
}

/* ---- figures ---- */

.figure { margin: 0 0 clamp(14px, 2.4vw, 26px); }


.figure img {
  width: 100%;
  height: auto;
  background: rgba(22, 22, 15, 0.05);
}

/* The figures carry no captions — no room labels, no running numbers. The one
   exception is the credit below, on the two floor plans. */

.figure--wide { margin-inline: calc(var(--gutter) * -1); }
.figure--wide img { width: 100%; }

/* Drawings — the two floor plans, the millwork sheets, and the whole
   Sketchbook. Held well inside the content width and centred: these are scans
   of pencil and wash, and CAD line weights, none of which survive being blown
   up to the 1400px a photograph takes.

   No background of its own. The plans and millwork sheets are supplied with the
   background knocked out and build-assets.ps1 flattens them onto --bg, so they
   sit directly on the page; a white plate here would undo that. */
.figure--plan { max-width: 820px; margin-inline: auto; }

/* The one image on the About page. Held to the prose measure so it reads as
   part of the page rather than as a project figure. */
.figure--about { max-width: 720px; margin-top: clamp(36px, 6vh, 64px); }

.figure__credit {
  margin-top: 10px;
  color: var(--ink-soft);
  font-size: var(--ui-size);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* --cols is set inline by build-pages.ps1 on a pair whose two images have
   different aspect ratios: columns in proportion to those ratios make both
   images the same height, so their edges line up without either being cropped.
   Equal columns only align equal ratios, which is why most pairs here — both
   16:9 — need nothing and leave --cols unset. */
.pair {
  display: grid;
  grid-template-columns: var(--cols, 1fr 1fr);
  gap: clamp(14px, 2.4vw, 26px);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  margin: clamp(56px, 10vh, 104px) 0 clamp(20px, 3vw, 34px);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--rule);
}
/* Not a project title, so it takes the footer font rather than the display
   face — the condensed bold is reserved for the quoted project names. */
.section-head h2 {
  margin: 0;
  font-family: var(--mono);
  font-weight: 400;
  font-size: var(--ui-size);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.section-head span { color: var(--accent); }

/* ---- work index ----
   Measured off the Jacquemus collections index at a 1425px viewport:
     gutter 30px  |  text column 271px fixed  |  column gap 18px
     track runs from x=319 to the viewport's right edge, no right gutter
     frames 399x499 (4:5), 5px apart  |  rows 5px apart  |  no rules
   The frames scroll freely (Swiper free-mode) with no snapping.
   `.collections` sits outside `.wrap` so the track can reach the edge without
   negative-margin tricks that risk horizontal overflow.                */

.collections {
  /* Same token as the header and every other page, so the titles' left edge
     lines up with the wordmark. At desktop widths this resolves to the
     reference's 30px, which is what puts the image track at x = 319. */
  --jq-gutter: var(--gutter);
  --jq-col: 271px;
  --jq-colgap: 18px;
  margin: 0 0 clamp(30px, 6vh, 60px);
}

.collection {
  display: grid;
  grid-template-columns: var(--jq-col) 1fr;
  column-gap: var(--jq-colgap);
  align-items: start;
  padding-left: var(--jq-gutter);
  margin-bottom: 5px;
}

/* HEADER 2 — identical to HEADER 1: same family, weight, size and tracking.
   Every value comes from the --wordmark-* tokens, so the two cannot diverge. */
.collection__title {
  margin: 0 0 12px;
  font-family: var(--wordmark-family);
  font-weight: var(--wordmark-weight);
  font-size: var(--wordmark-size);
  line-height: var(--wordmark-leading);
  letter-spacing: var(--wordmark-track);
  text-transform: uppercase;
}

/* SUBTITLE 1 — sits just under HEADER 2 and reads a step smaller than it. */
.collection__meta {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(10px, 0.78vw, 11.5px);
  font-weight: 400;
  letter-spacing: 0.036em;
  line-height: 1.286;
  text-transform: uppercase;
}
.collection__meta span { display: block; }

/* Horizontal behaviour copied from the Le Papier slider: drag with a grab
   cursor at 1:1, and snap to the nearest frame on release (that slider runs
   Swiper with freeMode off, grabCursor on, simulateTouch on, speed 300).
   touch-action is left alone so touch devices keep native horizontal
   scrolling — the drag handler in site.js is mouse-only. */
/* Deliberately NO scroll-snap and NO scroll-behavior: smooth here.
   Both fight the drag handler in site.js: snapping yanks the track mid-pan,
   and `smooth` makes every scrollLeft write animate instead of tracking the
   cursor. Wheel, trackpad and touch scroll freely; the snap-to-frame on
   drag release is animated in JS instead. */
.collection__shots {
  display: flex;
  gap: 5px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.collection__shots::-webkit-scrollbar { display: none; }

.collection__shots img {
  flex: 0 0 auto;
  width: clamp(210px, 28vw, 460px);
  aspect-ratio: 4 / 5;
  height: auto;
  object-fit: cover;
  /* Without this the browser's native image/link drag pre-empts the pan. */
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}

@media (hover: hover) and (pointer: fine) {
  .collection__shots { cursor: grab; }
  .collection__shots.is-dragging { cursor: grabbing; }
}

/* ---- prev / index / next ---- */

.pager {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.pager a {
  display: block;
  background: var(--bg);
  padding: 16px 18px;
  transition: background-color 0.4s ease;
}
.pager a:nth-child(2) { text-align: center; }
.pager a:last-child { text-align: right; }
.pager a:hover { background: var(--card); }
.pager .label { display: block; color: var(--ink-soft); margin-bottom: 7px; }
.pager a:hover .label { color: var(--accent); }
.pager__t { font-family: var(--serif); font-size: clamp(14px, 1.5vw, 19px); }

@media (max-width: 560px) {
  .pager { grid-template-columns: 1fr; }
  .pager a,
  .pager a:nth-child(2),
  .pager a:last-child { text-align: left; }
}

/* ---- about ---- */

.prose { max-width: 62ch; }
.prose p {
  font-family: var(--serif);
  font-size: clamp(15px, 1.35vw, 19px);
  line-height: 1.68;
  margin: 0 0 1.15em;
}
.prose p:last-child { margin-bottom: 0; }

/* ---- footer ---- */

/* Reveal footer, after omedezin.com/projects-kent: the band is pinned to the
   bottom of the viewport and never moves. main carries an opaque background
   and a margin-bottom of exactly --foot-h, so the last --foot-h pixels of
   scroll slide the content up off a stationary footer instead of scrolling
   the footer into view.

   Stacking is explicit rather than relying on paint order: footer 0, main 1,
   header 20. main must stay opaque or the footer shows through the page.

   The top border is the site's one rule weight, shared with the header via
   --rule-head/--rule-head-w, so the line that closes the page is identical to
   the line that opens it. box-sizing is border-box globally, so the border is
   inside --foot-h and main's margin-bottom still matches exactly. */
.site-foot {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 0;
  height: var(--foot-h);
  background: var(--card);
  border-top: var(--rule-head-w) solid var(--rule-head);
  color: var(--accent);
}

/* Three equal columns put the email, monogram and phone at 20% / 50% / 80% of
   the band, which is where the mockup places them. */
.site-foot__panel {
  height: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  align-items: center;
  justify-items: center;
  gap: 16px;
  padding: 0 var(--gutter);
}

.site-foot__link {
  font-family: var(--wordmark-family);
  font-weight: var(--wordmark-weight);
  font-size: var(--wordmark-size);
  letter-spacing: var(--wordmark-track);
  line-height: var(--wordmark-leading);
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.site-foot__link:hover { opacity: 0.62; }

/* Sized by height so it keeps the artwork's 687:503 ratio. At the cap this is
   178px wide, which matches the mockup's stadium against a 1440px band. */
.site-foot__mark {
  display: block;
  height: clamp(74px, 9vw, 130px);
  width: auto;
}

/* Narrow screens: the three-up row has nowhere to go, so stack it. Override
   --foot-h itself rather than letting the band grow with height:auto -- main's
   margin-bottom reads the same token, and a band taller than the token would
   leave its top edge permanently covered by main. */
@media (max-width: 560px) {
  :root { --foot-h: 224px; }
  .site-foot__panel {
    grid-template-columns: 1fr;
    align-content: center;
    gap: 14px;
    padding: 20px var(--gutter);
  }
  .site-foot__mark { order: -1; height: 74px; }
}

/* ============================================================
   3. SCROLL REVEALS
   ============================================================ */

/* Scoped to .js so that without scripting the content is simply visible
   rather than stuck at opacity 0. */
.js .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.js .reveal.is-in { opacity: 1; transform: none; }

/* Slow counter-drift on large figures while they cross the viewport */

/* ============================================================
   4. RESPONSIVE
   ============================================================ */

@media (max-width: 900px) {
  .pair { grid-template-columns: 1fr; }
}

/* Below ~1000px the reference stacks the row and lets the track run full
   width, showing about two frames. */
@media (max-width: 1000px) {
  .collection { grid-template-columns: 1fr; row-gap: 14px; margin-bottom: 22px; }
  .collection__title { margin-bottom: 10px; }
  .collection__shots { margin-left: calc(var(--jq-gutter) * -1); }
  /* 48.7vw = the reference's 398px at an 817px viewport, i.e. two frames. */
  .collection__shots img { width: 48.7vw; }
}

@media (max-width: 680px) {
  /* Third column costs more than it adds on a phone. The remaining two stay
     equal via flex: 1 1 0, so the wall is still centred. */
  .ms-col:nth-child(3) { display: none; }

  :root { --ms-gap: 34px; }

  .site-nav { gap: 15px; }
  .figure--wide { margin-inline: 0; }
}

/* The wordmark is 23 characters at the title size, so on a narrow phone it
   runs into the nav. Scaling the shared token keeps the wordmark and the
   titles the same size as each other while buying back the room. */
/* No narrow-screen override is needed for the header any more. The wordmark
   used to be a large, widely-tracked Garamond that collided with the nav on a
   phone; at 13-16px Inter it clears it with room to spare. */

/* ============================================================
   5. REDUCED MOTION
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .ms-col-inner img { opacity: 1; }
}
