/* ==========================================================================
   Chef Fatmata Binta — site styles
   Built on one component: the tile. Translucent, no border, 5px grid gap.
   The gap is the rule line. Never add borders.
   ========================================================================== */

/* The brand palette, from the presentation:
     sage #768972, rust #A24A02 (primary) · amber #FCAC2A, deep green #002F00,
     brown #86613A (secondary) · white and black (neutral).
   The ground is the deck's cream. Contrast, worked out by hand:
     deep green on cream 14.4:1 (all body text)      rust on cream 5.8:1, cream on rust 5.8:1
     brown on cream 5.4:1 (quiet labels)             deep green on amber 7.9:1 (hover fills)
     cream on deep green 14.4:1                      cream at 72% on deep green 8.1:1
   Sage is 3.6:1 on cream and 4.0:1 on deep green, so it is only ever a fill,
   a shape or a rule — never small text. Amber is only ever a fill. */
:root {
  /* grounds */
  --fonio:  #FFFBF0;   /* the deck's cream */
  --paper:  #FFFFFF;
  --ink:    #002F00;   /* deep green: text on cream */
  --ember:  #002F00;   /* deep green: dark surfaces */
  --ash:    #0F3A0F;
  --bone:   #FFFBF0;   /* text on dark surfaces */

  /* accents */
  --mat:   #A24A02;    /* rust: the one UI accent */
  --amber: #FCAC2A;    /* hover fills, always with deep-green text */
  --sage:  #768972;    /* shapes and rules only */
  --brown: #86613A;
  --muted-dark:  #B8C2AD;   /* cream at 72% on deep green */
  --muted-light: #86613A;   /* brown */


  /* The mouse is always the brand's triangle: rust while moving, amber over
     anything you can press. The hotspot is the tip of its cap. */
  --cur:       url("../img/patterns/cursor-rust.png") 16 1, auto;
  --cur-point: url("../img/patterns/cursor-amber.png") 16 1, pointer;

  /* type */
  --mono: "Azeret Mono", ui-monospace, SFMono-Regular, monospace;
  --disp: "Azeret Mono", ui-monospace, SFMono-Regular, monospace;
  --body: "Azeret Mono", ui-monospace, SFMono-Regular, monospace;

  /* system */
  --gap: 5px;
  --pad: clamp(20px, 5vw, 80px);
  --measure: 60ch;
  --ease: cubic-bezier(0.19, 1, 0.22, 1);
  --nav-closed:  110px;
  --nav-open-w:  min(760px, 92vw);
  --nav-open-h:  250px;
}

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  /* A warp and weft laid into the ground. Barely there — around 2.5% ink —
     but it stops the page reading as flat colour and makes every section
     feel like it is sitting on something woven. */
  background-color: var(--fonio);
  background-image:
    repeating-linear-gradient(90deg, rgba(0, 47, 0,0.030) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(0deg,  rgba(0, 47, 0,0.022) 0 1px, transparent 1px 7px);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.7;
  letter-spacing: -0.012em;    /* Azeret sets a touch wide by default */
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a { color: inherit; text-decoration: none; }
a:not(.tile):not(.nav-item):hover { color: var(--mat); }

:focus-visible {
  outline: 2px solid var(--mat);
  outline-offset: 3px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--bone); color: var(--ink);
  padding: 10px 16px; font-family: var(--mono); font-size: 12px;
}
.skip:focus { left: 8px; top: 8px; }

/* --------------------------------------------------------------- type --- */

/* Azeret Mono carries real weights, so hierarchy comes from weight as well
   as size. Its squarer, tighter fit takes negative tracking at display size —
   which is what keeps a mono headline from reading as unstyled <pre>. */
h1, h2, h3 { font-family: var(--disp); margin: 0; text-wrap: balance; }
h1 { font-weight: 500; font-size: clamp(28px, 4vw, 54px); line-height: 1.14; letter-spacing: -0.045em; }
h2 { font-weight: 500; font-size: clamp(24px, 3.1vw, 40px); line-height: 1.18; letter-spacing: -0.038em; }
h3 { font-weight: 500; font-size: clamp(16px, 1.5vw, 20px); line-height: 1.35; letter-spacing: -0.02em; }

p { margin: 0; max-width: var(--measure); font-weight: 400; }
.lede { font-size: clamp(17px, 1.7vw, 21px); line-height: 1.55; letter-spacing: -0.02em; max-width: 54ch; }

.label {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted-dark);
}
.section--light .label { color: var(--muted-light); }

.mono { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }
.num  { color: var(--mat); font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- tile --- */

.tile {
  background: rgba(0, 26, 0, 0.5);
  backdrop-filter: blur(4px) grayscale(0.2);
  -webkit-backdrop-filter: blur(4px) grayscale(0.2);
  color: var(--bone);
  padding: 9px 12px;
  border: 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  line-height: 1.4;
}
.section--light .tile {
  background: rgba(255, 255, 255, 0.9);
  color: var(--ink);
}
.tile--solid  { background: rgba(118, 137, 114, 0.2); backdrop-filter: none; -webkit-backdrop-filter: none; }
.tile--active { background: var(--mat); color: var(--fonio); backdrop-filter: none; -webkit-backdrop-filter: none; }

/* ---------------------------------------------------------- structure --- */

.section { padding: clamp(72px, 11vw, 160px) var(--pad); position: relative; scroll-margin-top: clamp(120px, 16vh, 170px); }

/* The fringe off her mark, used as the site's only divider. Two-pixel ticks
   on an eleven-pixel repeat, matching the tassels in the logo. */
.section + .section::before,
.gallery + .section::before,
.section + .gallery::before {
  content: "";
  position: absolute; top: 0; left: var(--pad); right: var(--pad);
  height: 10px;
  background: repeating-linear-gradient(90deg, var(--mat) 0 2px, transparent 2px 13px);
  opacity: 0.55;
}
.gallery { position: relative; }
.section--light { background: var(--fonio); color: var(--ink); }
.section--dark  { background: var(--ember); color: var(--bone); }

.section__head { display: flex; flex-direction: column; gap: 16px; margin-bottom: clamp(28px, 4vw, 48px); }
.stack   { display: flex; flex-direction: column; gap: clamp(24px, 3vw, 40px); }
.stack-s { display: flex; flex-direction: column; gap: 16px; }


/* ---------------------------------------------------------------- nav --- */

.nav {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 100;
  width: var(--nav-closed);
  height: var(--nav-closed);
  border-radius: 0;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px) grayscale(0.2);
  -webkit-backdrop-filter: blur(10px) grayscale(0.2);
  color: var(--ink);
  display: grid;
  place-items: center;
  overflow: hidden;
  /* the unroll: width leads, height settles behind it */
  transition:
    width   0.78s var(--ease),
    height  0.44s var(--ease) 0.16s,
    top     0.8s var(--ease),
    left    0.8s var(--ease),
    opacity 0.3s ease,
    transform 0.8s var(--ease);
}

/* travelled: parked top-centre once past the hero */
.nav.is-parked { top: 28px; transform: translate(-50%, 0); }

/* interior pages have no hero, so the nav is parked from the first paint —
   no flash of centred nav before JS runs */
.page .nav { top: 28px; transform: translate(-50%, 0); }

/* Quieter while scrolling down, back at rest or on scroll up — but it
   NEVER leaves the viewport and NEVER stops being clickable. This is the
   site's only navigation control; taking it off-screen would mean there's
   nothing left to click to bring it back. Hover or focus always wins. */
.nav.is-hidden { opacity: 0.4; }
.nav.is-hidden:hover,
.nav.is-hidden:focus-within,
.nav.is-open.is-hidden { opacity: 1; }  /* never fade an open menu */

/* opened */
.nav.is-open { width: var(--nav-open-w); height: var(--nav-open-h); }

.nav__toggle {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  background: none; border: 0; padding: 0; margin: 0;
  color: inherit; cursor: var(--cur-point);
  display: grid; place-items: center; gap: 4px;
  border-radius: 0;
  font: inherit;
}
.nav__toggle-inner {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  max-width: 76%;              /* keep the label inside the circle's chord */
}
.nav__mark {
  display: block;
  width: auto;
  height: clamp(46px, 5.5vw, 58px);
  object-fit: contain;
}
.nav__word {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  line-height: 1.3;
  text-align: center;
  text-wrap: balance;          /* a multi-word label wraps evenly, not 3+1 */
}

.nav.is-open .nav__toggle { opacity: 0; pointer-events: none; }

/* The open surface. .nav__panel owns position/opacity for the whole
   thing; .nav__grid is just the item layout. */
.nav__panel {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 24px 18px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.3s ease 0.1s;
}
.nav.is-open .nav__panel { opacity: 1; pointer-events: auto; }

.nav__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
  margin: 0; padding: 0; list-style: none;
}
.nav__grid li { display: flex; }

.nav-item {
  display: grid; place-items: center;
  height: 48px;
  background: none;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-align: center;
  border: 0;
  cursor: var(--cur-point);
  width: 100%;
  transition: color 0.2s ease;
  padding: 0 10px;
  position: relative;
}
.nav-item::after {
  content: "";
  position: absolute; left: 50%; bottom: 10px;
  width: 0; height: 1px;
  background: currentColor;
  transform: translateX(-50%);
  transition: width 0.32s var(--ease);
}
.nav-item:hover, .nav-item:focus-visible { color: var(--mat); }
.nav-item:hover::after, .nav-item:focus-visible::after { width: 44%; }
.nav-item[aria-current] { color: var(--mat); }
.nav-item[aria-current]::after { width: 44%; }

/* a rectangle wants full-bleed rows, not tapered ones */
.nav__grid li > * { width: 100%; }


.nav__close {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
  background: none;
  color: var(--muted-light);
}

/* --------------------------------------------------------------- hero --- */

.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: 0; background: var(--ember); }
.hero__media img,
.hero__media video { width: 100%; height: 100%; object-fit: cover; object-position: center 40%; display: block; }
/* Just enough scrim to hold the circle and the scroll cue — the photograph
   carries the whole first screen now, so it stays as open as it can. */
.hero__media::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(58% 48% at 50% 50%, rgba(0, 26, 0,0.42) 0%, transparent 72%),
    linear-gradient(180deg, transparent 62%, rgba(0, 26, 0,0.62) 100%);
}

.hero__cue {
  position: absolute; left: 50%; bottom: 34px;
  transform: translateX(-50%);
  z-index: 2;
  color: rgba(255, 251, 240, 0.72);
  display: flex; align-items: center; gap: 8px;
}
.hero__cue::after {
  content: ""; width: 1px; height: 34px;
  background: currentColor;
  transform-origin: top;
  animation: cue 2.4s var(--ease) infinite;
}
@keyframes cue {
  0%   { transform: scaleY(0);   opacity: 0; }
  35%  { transform: scaleY(1);   opacity: 1; }
  70%  { transform: scaleY(1);   opacity: 1; }
  100% { transform: scaleY(0);   opacity: 0; transform-origin: bottom; }
}

/* screen two: the statement */
/* Two columns: the words, and a plate seen from directly above. The disc
   answers the circle in the nav and the mat the guests sit on — it is the
   one shape this site owns, so the opening statement is built around it
   rather than around an empty right-hand margin. */
.statement { min-height: 94svh; display: flex; align-items: center; overflow: hidden; }
.statement__inner {
  display: block;
  width: 100%;
}
.statement__text { display: flex; flex-direction: column; gap: clamp(20px, 2.6vw, 34px); align-items: flex-start; }
.statement__eyebrow { margin: 0; }
.statement h1 {
  max-width: 20ch;
  font-size: clamp(30px, 4.6vw, 64px);
  line-height: 1.1;
  letter-spacing: -0.045em;
}

/* the credentials as a ledger, not a row of floating pills */
.creds { margin: 0; display: grid; gap: 0; width: 100%; max-width: 40rem; }
.creds__row {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: 16px;
  align-items: baseline;
  padding: 13px 0;
  border-top: 1px solid rgba(0, 47, 0, 0.16);
}
.creds__row:last-child { border-bottom: 1px solid rgba(0, 47, 0, 0.16); }
.creds dt { margin: 0; }
.creds dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.02em;
  line-height: 1.4;
}

.tile.hero__cta {
  width: fit-content;
  background: var(--mat);
  color: var(--fonio);
  padding: 12px 18px;
  letter-spacing: 0.14em;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* ------------------------------------------------------------- plates --- */

.plates { display: flex; flex-direction: column; gap: var(--gap); }
.plate {
  position: relative; display: block;
  height: clamp(300px, 42vw, 460px);
  overflow: hidden;
}
.plate img { width: 100%; height: 100%; object-fit: cover; }
.plate::after {
  content: ""; position: absolute; inset: 0;
  background: var(--ember); opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}
.plate:hover::after, .plate:focus-visible::after { opacity: 0.18; }
.plate:hover .plate__meta .tile:nth-child(2),
.plate:focus-visible .plate__meta .tile:nth-child(2) { background: var(--mat); }

.plate__meta {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: grid; grid-template-columns: 58px minmax(0, 230px) 1fr; gap: var(--gap);
}
.plate__meta .tile { background: rgba(0, 26, 0,0.5); color: var(--bone); }

/* ---------------------------------------------------------- editions --- */

.editions { display: block; }
.editions > summary {
  list-style: none;
  cursor: var(--cur-point);
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
}
.editions > summary::-webkit-details-marker { display: none; }
.editions > summary::after {
  content: "+";
  font-family: var(--mono); font-size: 17px; line-height: 1;
  transition: transform 0.25s var(--ease);
}
.editions[open] > summary::after { transform: rotate(45deg); }
.editions > summary:hover { background: var(--mat); color: var(--fonio); }
.editions__body { padding-top: var(--gap); }

/* -------------------------------------------------------------- faq --- */

.faq { display: flex; flex-direction: column; }
.faq__item { border-bottom: 1px solid rgba(0, 47, 0, 0.14); }
.faq__item:first-child { border-top: 1px solid rgba(0, 47, 0, 0.14); }
.faq__item > summary {
  list-style: none;
  cursor: var(--cur-point);
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 20px 2px;
  font-weight: 500; font-size: clamp(16px, 1.5vw, 20px); line-height: 1.35; letter-spacing: -0.02em;
  transition: color 0.24s ease;
}
.faq__item > summary::-webkit-details-marker { display: none; }
.faq__item > summary::after {
  content: "+";
  flex-shrink: 0;
  font-family: var(--mono); font-size: 20px; line-height: 1;
  transition: transform 0.25s var(--ease);
}
.faq__item[open] > summary::after { transform: rotate(45deg); }
.faq__item > summary:hover,
.faq__item > summary:focus-visible { color: var(--mat); }
.faq__body { padding: 0 2px clamp(20px, 3vw, 28px); }
.faq__body p { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .faq__item > summary::after { transition: none; }
}

.index { display: grid; grid-template-columns: 96px minmax(0,1.2fr) minmax(0,1.6fr) 110px; gap: var(--gap); }
.index__h { font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; }
.index__full { grid-column: 1 / -1; }

/* A flagged fact — something inferred from a photograph rather than
   confirmed by Binta directly. Same colour as the mat itself, so it reads
   as "worth a second look," not as an error. */
.tbc {
  font-family: var(--mono); font-size: 12px; line-height: 1.5;
  padding: 10px 12px;
  background: rgba(162, 74, 2, 0.09);
  color: var(--mat);
}
.tbc-inline { color: var(--mat); font-style: normal; }

/* ------------------------------------------------------- page header --- */

.page-head {
  padding: clamp(110px, 15vh, 190px) var(--pad) clamp(40px, 6vw, 72px);
}
.page-head h1 { max-width: 16ch; }
.page-head__inner { display: flex; flex-direction: column; gap: 18px; }

/* ------------------------------------------------------------ gallery --- */
/* Horizontal, one or two frames in view. Default is a plain swipeable
   scroller — works with no JS and under reduced motion. JS upgrades it to
   a pinned track, where scrolling the page moves the gallery sideways.
   The round dishes used to be absolutely positioned over a photo strip,
   which landed them on people's faces and collided with the nav. */

.gallery { position: relative; }

.gallery__pin {
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.gallery__track {
  display: flex;
  align-items: center;   /* every item's own natural height, all centred on
                            one shared line — stops caption-length differences
                            or a round item's own sizing from ever nudging
                            images off a shared vertical position */
  gap: clamp(16px, 3vw, 52px);
  padding: 4px var(--pad) 20px;
  width: max-content;
}

/* The frame is the one thing every item — round or rectangular — shares:
   identical width AND height, always. Round items used to size themselves
   from aspect-ratio:1 against their OWN width instead, which landed a few
   pixels taller than the rectangular frames next to them — the strip never
   quite lined up. Sizing both off the same two variables makes that
   impossible regardless of what's added here later. */
.gallery__item {
  --sq: min(72vw, 58vh, 520px);   /* every photo in an essay is this square */
  --frame-w: var(--sq);
  --frame-h: var(--sq);
  flex: 0 0 auto;
  width: var(--frame-w);
  display: flex;
  flex-direction: column;
  gap: 14px;
  scroll-snap-align: center;
}
.gallery__frame { height: var(--frame-h); overflow: hidden; }
.gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;  /* overridden per-photo via inline style where the subject isn't centred */
  background: var(--paper);
}

.gallery__cap { font-family: var(--body); font-size: 12px; line-height: 1.5; letter-spacing: -0.005em; color: var(--muted-light); max-width: 34ch; }

/* pinned: the page scroll drives the track sideways */
/* The stage is only as tall as its photos and pins with its middle at the middle
   of the screen (its top at 50% less half its measured height, --pin-h, set by
   site.js), so there is no empty screen before or after the essay. No transform:
   shifting it up would also shift it up over the text above it before it pins. */
.gallery.is-pinned .gallery__pin {
  position: sticky;
  top: max(0px, calc(50% - var(--pin-h, 0px) / 2));
  padding-block: 8px;
  display: flex;
  align-items: center;
  overflow: hidden;
  scroll-snap-type: none;
}
.gallery.is-pinned .gallery__track { will-change: transform; }

.gallery__hint {
  display: flex; align-items: baseline; gap: 14px;
  padding: 0 var(--pad) 20px;
}
.gallery.is-pinned .gallery__hint {
  position: absolute; top: clamp(90px, 13vh, 140px); left: 0; z-index: 2;
}
.gallery.is-pinned .gallery__arrow { display: none; }

/* --------------------------------------------------------------- misc --- */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--gap); }
.stat { display: flex; flex-direction: column; gap: 6px; padding: 18px 20px; }
.stat__n { font-family: var(--disp); font-size: clamp(26px, 3vw, 40px); line-height: 1.1; color: var(--mat); font-variant-numeric: tabular-nums; }

.routes { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--gap); }
.route { display: flex; flex-direction: column; gap: 8px; padding: 22px 24px; min-height: 148px; }
.route:hover { background: var(--mat); color: var(--fonio); }
.route:hover .label { color: var(--bone); }

.timeline { display: grid; gap: var(--gap); }
.timeline__row { display: grid; grid-template-columns: 96px 1fr; gap: var(--gap); }

.pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--gap); align-items: start; }
.pair img { width: 100%; height: clamp(320px, 40vw, 560px); object-fit: cover; }

footer { padding: clamp(48px, 6vw, 80px) var(--pad); background: var(--fonio); color: var(--ink); }
.footer__brand { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }
.footer__mark { display: block; }
.footer__mark img {
  height: clamp(56px, 7vw, 84px);
  width: auto;
  transition: opacity 0.3s ease;
}
.footer__mark:hover img { opacity: 0.66; }
.footer__id {
  font-family: var(--disp);
  font-size: clamp(14px, 1.4vw, 17px);
  letter-spacing: 0.01em;
  line-height: 1.5;
  color: var(--ink);
  max-width: 26ch;
}
.footer__grid { display: flex; flex-wrap: wrap; gap: clamp(24px, 4vw, 48px); align-items: flex-end; justify-content: space-between; }
.footer__links { display: flex; flex-wrap: wrap; gap: var(--gap); }
.footer__credit {
  margin: clamp(24px, 4vw, 40px) 0 0;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
  color: rgba(0, 47, 0, 0.5);
}
.footer__credit a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.footer__credit a:hover, .footer__credit a:focus-visible { color: var(--ink); }

/* --------------------------------------------------------------- fade --- */

.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------- responsive --- */

@media (max-width: 900px) {
  .statement { min-height: 0; }
  .index { grid-template-columns: 72px 1fr; }
  .index__h:nth-child(3), .index__h:nth-child(4) { display: none; }
  .index .index__venue, .index .index__seats { grid-column: 2; }
}

@media (max-width: 680px) {
  body { font-size: 15px; line-height: 1.7; }
  :root { --nav-closed: 92px; --nav-open-w: min(360px, 90vw); --nav-open-h: 330px; }
  .creds__row { grid-template-columns: 1fr; gap: 4px; padding: 12px 0; }
  .plate__meta { grid-template-columns: 44px 1fr; }
  .plate__meta .plate__desc { grid-column: 1 / -1; }
  .nav.is-parked, .page .nav { top: 14px; }
  .nav__grid { grid-template-columns: repeat(2, 1fr); }
  .nav-item { height: 40px; font-size: 11px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
}

/* ==========================================================================
   Motion
   Every rule below is switched off by the prefers-reduced-motion block at
   the end of this file. Nothing here is load-bearing for reading the page.
   ========================================================================== */

.progress {
  position: fixed; top: 0; left: 0; z-index: 150;
  height: 2px; width: 0;
  background: var(--mat);
  transition: width 0.1s linear;
}

/* ---- page load: the mat unrolls -------------------------------------- */
/* The hero image wipes open from the centre outward, like a mat being laid
   down. Once per page load. */

.js .hero__media {
  clip-path: inset(0 50% 0 50%);
  transition: clip-path 1000ms cubic-bezier(0.16, 1, 0.3, 1);
}
html.is-ready .hero__media { clip-path: inset(0 0 0 0); }

.js .hero__cue { opacity: 0; transition: opacity 0.6s ease 1.1s; }
html.is-ready .hero__cue { opacity: 1; }

/* interior pages: the head slides up rather than wiping */
.js .page-head__inner > * {
  opacity: 0; transform: translateY(18px);
  transition: opacity 0.6s ease, transform 0.6s var(--ease);
}
html.is-ready .page-head__inner > * { opacity: 1; transform: none; }
html.is-ready .page-head__inner > *:nth-child(2) { transition-delay: 0.1s; }

/* ---- leaving a page ----------------------------------------------------
   Superseded by the mat-wipe (further down this file). A plain opacity
   fade on <html> would fade the WIPE OVERLAY too, since it's a descendant
   of <html> — the one thing covering the screen fading to invisible while
   trying to cover it. Kept as nothing but a comment marker so a future
   edit doesn't reintroduce that exact conflict by accident. */

/* ---- the circle menu opens tile by tile ------------------------------- */
.js .nav__grid li {
  opacity: 0;
  transform: scale(0.92);
  transition: opacity 0.28s ease, transform 0.42s var(--ease);
}
.nav.is-open .nav__grid li { opacity: 1; transform: none; }
.nav.is-open .nav__grid li:nth-child(1) { transition-delay: 0.12s; }
.nav.is-open .nav__grid li:nth-child(2) { transition-delay: 0.16s; }
.nav.is-open .nav__grid li:nth-child(3) { transition-delay: 0.20s; }
.nav.is-open .nav__grid li:nth-child(4) { transition-delay: 0.24s; }
.nav.is-open .nav__grid li:nth-child(5) { transition-delay: 0.28s; }
.nav.is-open .nav__grid li:nth-child(6) { transition-delay: 0.32s; }
.nav.is-open .nav__grid li:nth-child(7) { transition-delay: 0.36s; }
.nav.is-open .nav__grid li:nth-child(8) { transition-delay: 0.40s; }

/* ---- parallax --------------------------------------------------------- */
.plate img { will-change: transform; }

/* ---- gallery: the centred frame carries the emphasis ------------------ */
.gallery__item img {
  transition: opacity 0.5s ease;
  opacity: 0.72;
}
.gallery__item.is-focus img { opacity: 1; }

/* ---- text reveals ----------------------------------------------------- */
.js .rise { opacity: 0; transform: translateY(22px); transition: opacity 0.75s ease, transform 0.75s var(--ease); }
.rise.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js .hero__media { clip-path: none; }
  .js .page-head__inner > *,
  .js .nav__grid li,
  .js .rise,
  .js .reveal { opacity: 1; transform: none; }
  .gallery__item img { opacity: 1; filter: none; }
  .progress { display: none; }
  .hero__cue { opacity: 1; }
  .hero__cue::after { animation: none; }
}

/* ==========================================================================
   Interaction
   One language for everything clickable: a wipe fills the tile from the left,
   the label inverts, and pressing it settles by a pixel. Nothing bounces.
   ========================================================================== */

.hero__cta,
.route,
.footer__links .tile,
.editions > summary {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  cursor: var(--cur-point);
  transition: color 0.24s ease, transform 0.12s ease;
}

.hero__cta::before,
.route::before,
.footer__links .tile::before,
.editions > summary::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: var(--mat);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.34s var(--ease);
}

.hero__cta:hover::before,       .hero__cta:focus-visible::before,
.route:hover::before,           .route:focus-visible::before,
.footer__links .tile:hover::before, .footer__links .tile:focus-visible::before,
.editions > summary:hover::before, .editions > summary:focus-visible::before {
  transform: scaleX(1);
}

.hero__cta:hover, .hero__cta:focus-visible,
.route:hover, .route:focus-visible,
.footer__links .tile:hover, .footer__links .tile:focus-visible,
.editions > summary:hover, .editions > summary:focus-visible {
  color: var(--fonio);
}
.route:hover .label, .route:focus-visible .label { color: var(--fonio); }

.hero__cta:active,
.route:active,
.footer__links .tile:active,
.editions > summary:active { transform: translateY(1px); }

/* the wipe replaces the old flat-fill hovers */
.route:hover { background: rgba(255, 255, 255, 0.9); }
.editions > summary:hover { background: rgba(0, 47, 0, 0.08); }

/* an arrow that leans on hover — only where something opens or navigates */
.hero__cta::after,
.route::after {
  content: "→";
  display: inline-block;
  margin-left: 8px;
  transition: transform 0.3s var(--ease);
}
.route::after { position: absolute; right: 22px; bottom: 20px; }
.hero__cta:hover::after,
.route:hover::after,
.hero__cta:focus-visible::after,
.route:focus-visible::after { transform: translateX(5px); }

/* plates: the whole card is the target, so say so */
.plate { cursor: var(--cur-point); }

/* the gallery is draggable when it isn't pinned */
.gallery:not(.is-pinned) .gallery__pin { cursor: var(--cur-point); }
.gallery:not(.is-pinned) .gallery__pin:active { cursor: var(--cur-point); }

.tile.hero__cta::before { background: var(--mat); }
.tile.hero__cta:hover, .tile.hero__cta:focus-visible { color: var(--ember); }

@media (prefers-reduced-motion: reduce) {
  .hero__cta::before, .route::before, .footer__links .tile::before,
  .editions > summary::before { transition: none; }
  .nav-item::after { transition: none; }
  .hero__cta::after, .route::after { transition: none; }
}

/* ==========================================================================
   The circle, alive
   It is the only navigation on the site, so it has to invite the click
   without becoming a toy. One idle signal, one hover response, one opening.
   ========================================================================== */

/* a ring breathes outward from the closed circle — box-shadow isn't clipped
   by the circle's own overflow, so no extra element is needed */
@keyframes navBreathe {
  0%   { box-shadow: 0 0 0 0    rgba(162, 74, 2, 0.30); }
  62%  { box-shadow: 0 0 0 18px rgba(162, 74, 2, 0);    }
  100% { box-shadow: 0 0 0 0    rgba(162, 74, 2, 0);    }
}
.js .nav:not(.is-open):not(.is-hidden) { animation: navBreathe 4s var(--ease) infinite 1.6s; }

/* hover: the contents lift rather than the circle, so the centring
   transform on .nav is left alone */
.nav__toggle-inner {
  transition: transform 0.42s var(--ease);
}
.nav:not(.is-open):hover .nav__toggle-inner,
.nav:not(.is-open):focus-within .nav__toggle-inner { transform: scale(1.08); }

.nav__mark { transition: transform 0.5s var(--ease), color 0.3s ease; }
.nav:not(.is-open):hover .nav__mark { transform: rotate(-8deg); color: var(--mat); }

.nav__word { transition: letter-spacing 0.42s var(--ease), opacity 0.3s ease; }
.nav:not(.is-open):hover .nav__word { letter-spacing: 0.12em; }

/* the toggle fades as the circle opens, so the label doesn't sit under the menu */
.nav__toggle { transition: opacity 0.22s ease; }

/* opening: the menu turns very slightly as it settles */
.js .nav__panel {
  transform: translateY(-8px) scale(0.985);
  transition: opacity 0.3s ease 0.1s, transform 0.55s var(--ease) 0.08s;
}
.js .nav.is-open .nav__panel { transform: none; }

/* a hairline ring sits inside the open circle, echoing the plate rim */
/* Her mark is a mat with a fringe top and bottom. The open panel wears the
   same fringe, drawn inside the edges so the circle's overflow doesn't clip
   it. This is the one ornament on the site and it is hers. */
.nav.is-open { box-shadow: inset 0 0 0 1px rgba(0, 47, 0, 0.12); }
.nav__panel::before, .nav__panel::after {
  content: "";
  position: absolute; left: 14px; right: 14px;
  height: 9px;
  background: repeating-linear-gradient(90deg,
    var(--mat) 0 2px, transparent 2px 13px);
  opacity: 0;
  transition: opacity 0.3s ease 0.34s;
}
.nav__panel::before { top: 7px; }
.nav__panel::after  { bottom: 7px; }
.nav.is-open .nav__panel::before,
.nav.is-open .nav__panel::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .js .nav:not(.is-open):not(.is-hidden) { animation: none; }
  .nav__toggle-inner, .nav__mark, .nav__word { transition: none; }
  .nav:not(.is-open):hover .nav__toggle-inner,
  .nav:not(.is-open):hover .nav__mark { transform: none; }
  .js .nav__panel { transform: none; }
}

/* ==========================================================================
   The statement — one sequence
   Places arrive, the sentence lays down a word at a time, the ledger rules
   draw themselves, then the button. All of it .js-gated and switched off
   under reduced motion, where the section renders complete and still.
   ========================================================================== */

.js .statement .w { display: inline-block; overflow: hidden; vertical-align: bottom; }
.js .statement .w > i {
  display: inline-block; font-style: normal;
  transform: translateY(112%);
  transition: transform 0.82s var(--ease);
}
.js .statement.is-in .w > i { transform: none; }

.statement__eyebrow .p--sep { margin: 0 0.55em; opacity: 0.5; }
.js .statement__eyebrow .p {
  display: inline-block;
  opacity: 0; transform: translateY(6px);
  transition: opacity 0.5s ease, transform 0.5s var(--ease);
}
.js .statement.is-in .statement__eyebrow .p { opacity: 1; transform: none; }

.creds__row { position: relative; }
.js .creds__row { border-top-color: transparent; }
.js .creds__row:last-child { border-bottom-color: transparent; }
.js .creds__row::before,
.js .creds__row:last-child::after {
  content: ""; position: absolute; left: 0; right: 0; height: 1px;
  background: rgba(0, 47, 0, 0.16);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.7s var(--ease) var(--d, 0s);
}
.js .creds__row::before { top: -1px; }
.js .creds__row:last-child::after { bottom: -1px; }
.js .statement.is-in .creds__row::before,
.js .statement.is-in .creds__row:last-child::after { transform: none; }

.js .creds__row > * {
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.5s ease calc(var(--d, 0s) + 0.14s),
              transform 0.5s var(--ease) calc(var(--d, 0s) + 0.14s);
}
.js .statement.is-in .creds__row > * { opacity: 1; transform: none; }

.js .statement .hero__cta {
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.5s ease, transform 0.5s var(--ease), color 0.24s ease;
}
.js .statement.is-in .hero__cta { opacity: 1; transform: none; }

/* ==========================================================================
   Gallery — captions on request
   The description is there when someone wants it, not sitting under every
   frame by default.
   ========================================================================== */

.gallery__frame { position: relative; }


.gallery__cap-wrap { display: flex; flex-direction: column; gap: 6px; }
.js .gallery__cap-wrap {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.45s var(--ease), opacity 0.35s ease;
}
.js .gallery__cap-wrap > * { overflow: hidden; }
/* One rule for both contexts — .gallery__item (the pinned photo essays) and
   .gem (the Gallery page masonry) are both plain <figure> elements with the
   same internal frame+caption structure, so keying off the tag itself
   means this never needs to be written twice again for a third context.
   No separate toggle button any more: the photo itself is tabIndex=0'd in
   site.js specifically so :focus-within has something to fire from on Tab,
   which is what makes this reachable by keyboard at all, not just mouse. */
.js figure:hover .gallery__cap-wrap,
.js figure:focus-within .gallery__cap-wrap {
  grid-template-rows: 1fr;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .js .statement .w > i,
  .js .statement__eyebrow .p,
  .js .creds__row > *,
  .js .statement .hero__cta { opacity: 1; transform: none; transition: none; }
  .js .creds__row::before,
  .js .creds__row:last-child::after { transform: none; transition: none; }
  .js .gallery__cap-wrap { transition: none; }
}


/* ==========================================================================
   Plate labels — on approach, not by default
   These strips are wayfinding: they name where each photograph leads. They
   slide up from the bottom edge on hover or keyboard focus, tile by tile.
   Coarse pointers have no hover, so there they stay visible — four unlabelled
   photographs is not a navigation.
   ========================================================================== */

.js .plate__meta .tile {
  opacity: 0;
  transform: translateY(100%);
  transition: opacity 0.36s ease, transform 0.52s var(--ease);
}
.js .plate:hover .plate__meta .tile,
.js .plate:focus-visible .plate__meta .tile {
  opacity: 1;
  transform: none;
}
.js .plate__meta .tile:nth-child(1) { transition-delay: 0s; }
.js .plate__meta .tile:nth-child(2) { transition-delay: 0.06s; }
.js .plate__meta .tile:nth-child(3) { transition-delay: 0.12s; }

/* a quiet mark that the image is a door, before you reach it */
.js .plate__hint {
  position: absolute; left: var(--gap); bottom: var(--gap);
  z-index: 2;
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.2em; text-transform: uppercase;
  padding: 9px 13px;
  background: rgba(0, 26, 0, 0.5);
  backdrop-filter: blur(4px) grayscale(0.2);
  -webkit-backdrop-filter: blur(4px) grayscale(0.2);
  color: var(--bone);
  opacity: 1;
  transition: opacity 0.3s ease;
}
.js .plate:hover .plate__hint,
.js .plate:focus-visible .plate__hint { opacity: 0; }

@media (pointer: coarse) {
  .js .plate__meta .tile { opacity: 1; transform: none; }
  .js .plate__hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js .plate__meta .tile { opacity: 1; transform: none; transition: none; }
  .js .plate__hint { display: none; }
}

/* ==========================================================================
   Booking
   ========================================================================== */

.notice {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px;
  max-width: 62ch;
  padding: 16px 18px;
  background: rgba(162, 74, 2, 0.09);
  font-size: 14px; line-height: 1.6;
}
.notice .label { color: var(--mat); }
.notice--quiet { background: rgba(0, 47, 0, 0.05); }
.notice--quiet .label { color: var(--muted-light); }

.booking { display: flex; flex-direction: column; gap: clamp(32px, 4vw, 52px); align-items: flex-start; width: 100%; }
.booking__step { border: 0; margin: 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: 16px; }
.booking__step legend { padding: 0; margin-bottom: 4px; }

/* --- the editions, as selectable rows --- */
.events { display: grid; gap: var(--gap); max-width: 62rem; }
.event {
  display: grid;
  grid-template-columns: 1fr 12rem 8rem;
  gap: 20px;
  align-items: center;
  padding: 18px 20px;
  background: rgba(255, 255, 255, 0.9);
  cursor: var(--cur-point);
  position: relative;
  transition: background 0.22s ease, color 0.22s ease;
}
.event:hover { background: rgba(255, 255, 255, 1); }
.event input { position: absolute; opacity: 0; pointer-events: none; }
.event__body { display: flex; flex-direction: column; gap: 4px; }
.event__city { font-size: 15px; letter-spacing: -0.02em; }
.event__venue { font-family: var(--mono); font-size: 12px; color: var(--muted-light); }
.event__when { font-family: var(--mono); font-size: 13px; }
.event__seats { text-align: right; }

.event::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--mat);
  transform: scaleY(0); transform-origin: center;
  transition: transform 0.28s var(--ease);
}
.event:has(input:checked)::before { transform: scaleY(1); }
.event:has(input:checked) { background: var(--mat); color: var(--fonio); }
.event:has(input:checked) .event__venue,
.event:has(input:checked) .event__seats { color: var(--fonio); opacity: 0.82; }
.event:has(input:checked) .event__when { color: var(--fonio); }
.event:has(input:focus-visible) { outline: 2px solid var(--mat); outline-offset: 3px; }

.event--full { opacity: 0.55; cursor: var(--cur); }
.event--full:hover { background: rgba(255, 255, 255, 0.9); }

/* --- the fields --- */
.fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px 20px; max-width: 62rem; }
.field { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.field--wide { grid-column: 1 / -1; }
.field label {
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted-light);
}
.field input, .field textarea {
  font-family: var(--mono); font-size: 14px;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.9);
  border: 0;
  border-bottom: 1px solid rgba(0, 47, 0, 0.22);
  padding: 12px 14px;
  width: 100%;
  transition: border-color 0.22s ease, background 0.22s ease;
}
.field textarea { resize: vertical; line-height: 1.6; }
.field input:focus, .field textarea:focus {
  outline: none;
  border-bottom-color: var(--mat);
  background: #FFFFFF;
}
.field input:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--mat); outline-offset: 2px; }

.field__hint { font-family: var(--mono); font-size: 11px; color: var(--muted-light); margin: 0; }
.field__error { font-family: var(--mono); font-size: 12px; color: var(--mat); margin: 0; }
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-bottom-color: var(--mat); border-bottom-width: 2px; }

.booking__submit { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.booking__status {
  margin: 0; max-width: 62ch;
  padding: 14px 16px;
  background: rgba(162, 74, 2, 0.09);
  font-family: var(--mono); font-size: 13px; line-height: 1.6;
}
.booking__status[hidden] { display: none; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.textlink { color: var(--mat); text-decoration: underline; text-underline-offset: 3px; }
.textlink:hover { text-decoration-thickness: 2px; }
.booking__done { display: flex; flex-direction: column; gap: 20px; align-items: flex-start; max-width: 62ch; }

@media (max-width: 900px) {
  .event { grid-template-columns: 1fr; gap: 10px; }
  .event__seats { text-align: left; }
  .fields { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 680px) {
  .fields { grid-template-columns: 1fr; }
}

/* ==========================================================================
   The entrance — the mat unrolls, then rolls up into the nav
   Home page only. Ships `hidden` in the markup: it does not exist for a
   crawler, a no-JS visitor, or a screen reader — JS unhides it only when
   safe to run. Never blocks the page: a hard timeout force-closes it, a
   click or keypress skips it, and it never fires twice in one session.
   ========================================================================== */

.intro {
  position: fixed; inset: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
  background: var(--fonio);
  cursor: var(--cur-point);
  opacity: 0;
  /* Inert by default. It only becomes clickable while genuinely showing the
     full unrolled mat — `is-shown` is added once and never removed, so once
     `is-rolling` starts, this full-viewport div (same colour as the page
     itself, so invisible by then) would otherwise sit on top of the real
     nav at the exact moment its click matters most and silently eat the
     click. :not() makes that impossible regardless of which classes pile up
     or what order the rules appear in the file. */
  pointer-events: none;
  transition: opacity 0.5s ease;
}
.intro.is-shown { opacity: 1; }
.intro.is-shown:not(.is-rolling):not(.is-leaving) { pointer-events: auto; }
.intro.is-leaving { opacity: 0; pointer-events: none; transition: opacity 0.5s ease 0.05s; }

.intro__skip {
  position: absolute; left: 50%; bottom: clamp(28px, 6vh, 56px);
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.5s ease 0.5s;
}
.intro.is-shown .intro__skip { opacity: 0.55; }
.intro.is-rolling .intro__skip,
.intro.is-leaving .intro__skip { opacity: 0; }

/* The mat itself. It opens at the exact fixed point the real nav sits at
   (dead centre of the viewport) so the roll-up lands with nothing to jump. */
.intro__mat {
  position: relative;
  width: min(760px, 86vw);
  height: min(420px, 56vh);
  background: rgba(255, 255, 255, 0.96);
  box-shadow: 0 40px 110px rgba(0, 47, 0, 0.22), 0 4px 24px rgba(0, 47, 0, 0.1);
  display: flex; align-items: center; justify-content: center;
  transform: scale(0.92) rotate(-1.6deg);
  opacity: 0;
  overflow: hidden;
  transition:
    opacity 0.7s ease,
    transform 1s cubic-bezier(0.16, 1, 0.3, 1),
    width  0.9s cubic-bezier(0.65, 0, 0.24, 1) 0.05s,
    height 0.9s cubic-bezier(0.65, 0, 0.24, 1) 0.05s,
    box-shadow 0.6s ease;
}
.intro.is-shown .intro__mat { opacity: 1; transform: none; }

/* the fringe, off her own mark, at drama scale */
.intro__mat::before, .intro__mat::after {
  content: "";
  position: absolute; left: 5%; right: 5%;
  height: 16px;
  background: repeating-linear-gradient(90deg, var(--mat) 0 3px, transparent 3px 20px);
  opacity: 0.85;
  transition: opacity 0.5s ease, height 0.6s ease;
}
.intro__mat::before { top: 14px; }
.intro__mat::after  { bottom: 14px; }
.intro.is-rolling .intro__mat::before,
.intro.is-rolling .intro__mat::after { opacity: 0; height: 0; }

/* a woven ground inside the mat, matching the page's own texture at a
   larger, more visible scale — this is the same motif, not a new one */
.intro__mat::before { box-shadow: none; }
.intro__mat {
  background-image:
    repeating-linear-gradient(90deg, rgba(0, 47, 0,0.05) 0 1px, transparent 1px 11px),
    repeating-linear-gradient(0deg,  rgba(0, 47, 0,0.035) 0 1px, transparent 1px 11px),
    linear-gradient(rgba(255, 255, 255,0.96), rgba(255, 255, 255,0.96));
}

/* Both images are absolutely centred over the mat and stay centred through
   every state — only the scale factor in the translate changes, never the
   position itself, or they would drift off-centre mid-transition. */
.intro__logo, .intro__mark {
  position: absolute;
  top: 50%; left: 50%;
  transition: opacity 0.6s ease, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.intro__logo {
  width: min(360px, 52vw); height: auto;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.88);
}
.intro.is-shown .intro__logo { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.intro.is-rolling .intro__logo {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.5);
  transition-duration: 0.4s;
}

.intro__mark {
  width: auto; height: 44px;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.6);
}
.intro.is-rolling .intro__mark {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  transition-delay: 0.25s;
}

/* the roll-up: the mat collapses to exactly the closed nav's footprint */
.intro.is-rolling .intro__mat {
  width: var(--nav-closed);
  height: var(--nav-closed);
  transform: rotate(0deg);
  box-shadow: none;
  border-radius: 0;
}

@media (prefers-reduced-motion: reduce) {
  .intro { display: none !important; }
}

@media (max-width: 680px) {
  .intro__mat { width: min(340px, 88vw); height: min(280px, 42vh); }
  .intro__logo { width: min(220px, 60vw); }
}

/* ==========================================================================
   Breadcrumb — every interior page, "Home / Current"
   ========================================================================== */

.crumb {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 11px; letter-spacing: 0.06em;
  color: var(--muted-light);
  margin-bottom: 4px;
}
.crumb a { transition: color 0.2s ease; }
.crumb a:hover, .crumb a:focus-visible { color: var(--mat); }
.crumb [aria-current="page"] { color: var(--ink); }
.crumb__sep { opacity: 0.5; }

/* ==========================================================================
   Sub-navigation — jump tabs for a page with more than one section
   (currently: About / Fonio). Scrollspy sets aria-current in site.js.
   ========================================================================== */

.subnav {
  display: flex; gap: 4px;
  padding: 0 var(--pad);
  margin-top: -1px;
}
.subnav__tab {
  padding: 12px 18px;
  font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted-light);
  border-bottom: 2px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.subnav__tab:hover, .subnav__tab:focus-visible { color: var(--ink); }
.subnav__tab[aria-current="true"] { color: var(--mat); border-color: var(--mat); }

/* ==========================================================================
   Scroll to top
   ========================================================================== */

.totop {
  position: fixed; right: clamp(16px, 3vw, 28px); bottom: clamp(16px, 3vw, 28px);
  z-index: 90;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px) grayscale(0.2);
  -webkit-backdrop-filter: blur(10px) grayscale(0.2);
  color: var(--ink);
  border: 0; cursor: var(--cur-point);
  font-size: 16px;
  opacity: 0; transform: translateY(10px);
  /* visibility is delayed on the way OUT only, so a keyboard user can never
     Tab onto an invisible button — opacity/pointer-events alone would let
     that happen, since pointer-events:none blocks a click but not focus. */
  visibility: hidden;
  transition: opacity 0.3s ease, transform 0.3s var(--ease),
              background 0.22s ease, color 0.22s ease,
              visibility 0s linear 0.3s;
  pointer-events: none;
}
.totop.is-visible {
  opacity: 1; transform: none; pointer-events: auto;
  visibility: visible;
  transition: opacity 0.3s ease, transform 0.3s var(--ease),
              background 0.22s ease, color 0.22s ease,
              visibility 0s linear 0s;
}
.totop:hover, .totop:focus-visible { background: var(--mat); color: var(--fonio); }

@media (prefers-reduced-motion: reduce) {
  .totop { transition: opacity 0.15s ease, visibility 0s linear 0.15s; }
  .totop.is-visible { transition: opacity 0.15s ease, visibility 0s linear 0s; }
}

/* ==========================================================================
   Gallery — the masonry browse
   CSS multi-column rather than grid: it lets each photo keep its own
   natural aspect ratio (this page is a browse, not a cropped, uniform wall)
   while still flowing into tidy columns.
   ========================================================================== */

.gem-grid { column-count: 3; column-gap: var(--gap); }
.gem { break-inside: avoid; margin: 0 0 var(--gap); display: block; }
.gem .gallery__frame img { width: 100%; height: auto; display: block; }
/* padding only — this used to also force display:flex, which pinned the
   caption permanently visible and made the Info button on this page do
   nothing at all when clicked (it toggled a class with no rule watching
   for it). It now inherits the same hover-reveal behaviour as everywhere
   else instead of fighting it. */
.gem .gallery__cap-wrap { padding: 10px 2px 0; }

@media (max-width: 900px) { .gem-grid { column-count: 2; } }
@media (max-width: 560px) { .gem-grid { column-count: 1; } }

/* ==========================================================================
   Lightbox — click a photo, see it at full resolution
   ========================================================================== */

.lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: grid; place-items: center;
  padding: clamp(16px, 5vw, 64px);
  background: rgba(0, 26, 0, 0.92);
  opacity: 0; visibility: hidden;
  transition: opacity 0.28s ease, visibility 0s linear 0.28s;
  /* cursor: the branded version further down this file supersedes this —
     kept only as the fallback for browsers that reject the data-URI form */
  cursor: var(--cur-point);
}
.lightbox.is-open {
  opacity: 1; visibility: visible;
  transition: opacity 0.28s ease, visibility 0s linear 0s;
}
.lightbox__img {
  /* Percentage max-width/height don't work here: the parent <picture> has no
     size of its own to be a percentage OF — it sizes to the image, the image
     was told to size to it, and that circular reference resolves to nothing,
     so a 2560px photo was rendering at full native size and overflowing the
     screen. Viewport units sidestep the whole problem — they're never
     ambiguous. Subtracts roughly the lightbox's own padding so the image
     never touches the edges. */
  display: block;
  max-width: calc(100vw - clamp(32px, 10vw, 128px));
  max-height: calc(100vh - clamp(32px, 10vw, 128px));
  width: auto; height: auto;
  object-fit: contain;
  cursor: var(--cur);
  background: var(--ash);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.lightbox__img.is-loaded { opacity: 1; }
/* Not the faint scrim-only tint this started as — that assumed the dark
   scrim would always be what's directly behind these buttons, but they sit
   at fixed screen positions and a large bright photo (a white plate, pale
   fabric) can end up right behind one, at which point a 14%-opacity white
   tint reads as nothing at all. Same frosted, near-opaque tile as .totop
   and .sound instead — it reads against any photo because the tile itself
   is the dominant thing there, not whatever shows through it. */
.lightbox__close,
.lightbox__nav {
  position: fixed;
  z-index: 301;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px) grayscale(0.2);
  -webkit-backdrop-filter: blur(10px) grayscale(0.2);
  color: var(--ink);
  border: 0; cursor: var(--cur-point);
  font-size: 22px; line-height: 1;
  transition: background 0.2s ease, color 0.2s ease;
}
.lightbox__close { top: clamp(14px, 3vw, 26px); right: clamp(14px, 3vw, 26px); }
.lightbox__close:hover, .lightbox__close:focus-visible,
.lightbox__nav:hover, .lightbox__nav:focus-visible { background: var(--mat); color: var(--fonio); }
.lightbox__cap {
  position: fixed; left: 0; right: 0; bottom: clamp(14px, 3vw, 26px);
  display: flex; flex-direction: column; gap: 10px;
  align-items: center; text-align: center;
  max-width: 60ch; margin: 0 auto;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.04em;
  color: var(--muted-dark);
  padding: 0 20px;
}
/* the fringe from her own mark, carried into the one place on the site
   that otherwise has no trace of it — a full-screen photo on a plain
   scrim. Small, but it's the difference between a generic lightbox and
   one that still feels like hers even at full bleed. */
.lightbox__cap::before {
  content: "";
  width: 64px; height: 7px;
  background: repeating-linear-gradient(90deg, var(--mat) 0 2px, transparent 2px 11px);
  opacity: 0.7;
}

/* every zoomable photo gets a cursor + a quiet hover cue, so the ability to
   click it is discoverable without adding a permanent visible control */
img[data-zoom-full] { cursor: var(--cur-point); }

@media (prefers-reduced-motion: reduce) {
  .lightbox { transition: opacity 0.15s ease, visibility 0s linear 0.15s; }
  .lightbox.is-open { transition: opacity 0.15s ease, visibility 0s linear 0s; }
  .lightbox__img { transition: none; }
}

/* ==========================================================================
   Sound toggle — off by default, always a click away
   Mirrors .totop (same size, same visibility rules) but sits bottom-LEFT so
   the two never collide. Browsers refuse audible autoplay without a fresh
   user gesture on the page — see the JS comment for how that's handled
   honestly rather than faked.
   ========================================================================== */

.sound {
  position: fixed; left: clamp(16px, 3vw, 28px); bottom: clamp(16px, 3vw, 28px);
  z-index: 90;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px) grayscale(0.2);
  -webkit-backdrop-filter: blur(10px) grayscale(0.2);
  color: var(--ink);
  border: 0; cursor: var(--cur-point);
  transition: background 0.22s ease, color 0.22s ease;
}
.sound:hover, .sound:focus-visible { background: var(--mat); color: var(--fonio); }
.sound svg { width: 22px; height: 22px; }
.sound__off { display: block; }
.sound__on  { display: none; }
.sound[aria-pressed="true"] .sound__off { display: none; }
.sound[aria-pressed="true"] .sound__on  { display: block; }

/* ==========================================================================
   Gallery interactivity — hover zoom, contained by the frame's own overflow
   ========================================================================== */

.gallery__frame img { transition: transform 0.5s var(--ease); }
.gallery__frame:hover img,
.gallery__frame:focus-within img { transform: scale(1.045); }

/* the lightbox as a carousel: prev/next either side of the image — sizing,
   colour and hover state already come from the shared rule above; this is
   only what's unique to being a SIDE control rather than a corner one */
.lightbox__nav { top: 50%; transform: translateY(-50%); }
.lightbox__nav--prev { left: clamp(10px, 2.4vw, 24px); }
.lightbox__nav--next { right: clamp(10px, 2.4vw, 24px); }
.lightbox__nav[hidden] { display: none; }

.lightbox__cap-eyebrow {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--mat);
}
.lightbox__cap-text { color: var(--bone); }
.lightbox__count {
  position: fixed; top: clamp(14px, 3vw, 26px); left: clamp(14px, 3vw, 26px);
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em;
  color: var(--muted-dark);
}

@media (max-width: 680px) {
  .lightbox__nav { width: 40px; height: 40px; font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .gallery__frame img { transition: none; }
}

/* ==========================================================================
   Craft pass — the small things
   Custom cursor, text selection, scrollbar, tactile press states. None of
   this changes the identity; all of it is the identity applied one layer
   deeper than most sites bother to go.
   ========================================================================== */

/* A tiny branded cursor — her mat colour, not the browser's stock zoom
   icon — for the one gesture repeated across nearly every page: click a
   photo to see it full-size. Built as a plain SVG data URI so it costs
   nothing to load and never needs a network request. */
img[data-zoom-full] {
  cursor: var(--cur-point);
}
.lightbox {
  cursor: var(--cur-point);
}
.lightbox__img, .lightbox__close, .lightbox__nav { cursor: var(--cur); }  /* the branded cursor is for the BACKDROP's "click to close" affordance only — controls and the photo itself keep sensible native cursors */
.lightbox__close, .lightbox__nav { cursor: var(--cur-point); }

/* the mat colour, not the browser default blue, the moment anyone selects
   a word of her story */
::selection { background: var(--mat); color: var(--fonio); }

/* a thin scrollbar in the site's own palette rather than the OS default —
   WebKit and Firefox both covered, no dark-mode branch needed since the
   whole site deliberately runs on one ground */
html { scrollbar-width: thin; scrollbar-color: var(--mat) var(--fonio); }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--fonio); }
::-webkit-scrollbar-thumb {
  background-color: var(--mat);
  border: 3px solid var(--fonio);
  border-radius: 0;   /* the site rule holds even here: no rounded corners anywhere */
}
::-webkit-scrollbar-thumb:hover { background-color: var(--ink); }

/* tactile feedback — every clickable surface settles by a couple of
   pixels on press, so pressing something always FEELS like pressing it,
   not just a colour change after the fact */
.tile, .nav-item, .nav__toggle, .totop, .sound,
.lightbox__close, .lightbox__nav, .subnav__tab,
.route, .plate, .editions > summary,
.event {
  transition: transform 0.15s ease, background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.tile:active, .nav-item:active, .nav__toggle:active, .totop:active, .sound:active,
.lightbox__close:active, .lightbox__nav:active, .subnav__tab:active,
.route:active, .plate:active, .editions > summary:active,
.event:active {
  transform: scale(0.96);
}

@media (prefers-reduced-motion: reduce) {
  .tile, .nav-item, .nav__toggle, .totop, .sound,
  .lightbox__close, .lightbox__nav, .subnav__tab,
  .route, .plate, .editions > summary, .event {
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
  }
  .tile:active, .nav-item:active, .nav__toggle:active, .totop:active, .sound:active,
  .lightbox__close:active, .lightbox__nav:active, .subnav__tab:active,
  .route:active, .plate:active, .editions > summary:active,
  .event:active { transform: none; }
}

/* ==========================================================================
   The mat-wipe page transition
   Leaving: a dark bar with her fringe on its leading edge sweeps in from
   the right, covering the old page. Arriving: it's already covering (set
   before first paint, so there's no flash of raw content), then a pure CSS
   animation sweeps it out to the left, revealing the new page. Both
   directions move the same way — right to left — so it reads as one
   continuous gesture rather than two different effects stitched together.

   The arrival reveal is intentionally NOT driven by a second JS-toggled
   class: if it were, a script error on the new page would leave a visitor
   staring at a solid screen forever, with literally nothing they could
   click to get past it. An animation with a fixed duration can't get
   stuck — it runs to completion regardless of anything else on the page.
   ========================================================================== */

.page-wipe {
  position: fixed; inset: 0; z-index: 450;
  background: var(--ember);
  transform: translateX(100%);
  pointer-events: none;
}
.page-wipe::after {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 0; width: 12px;
  background: repeating-linear-gradient(0deg, var(--mat) 0 2px, transparent 2px 13px);
}
.js .page-wipe { transition: transform 0.42s cubic-bezier(0.76, 0, 0.24, 1); }
html.is-leaving .page-wipe { transform: translateX(0); }

@keyframes pageWipeReveal {
  0%   { transform: translateX(0); }
  38%  { transform: translateX(0); }   /* hold a beat once fully covered, so arrival never feels instant/jarring */
  100% { transform: translateX(-100%); }
}
html.is-covering .page-wipe {
  animation: pageWipeReveal 0.68s cubic-bezier(0.76, 0, 0.24, 1) forwards;
}

@media (prefers-reduced-motion: reduce) {
  .page-wipe { transition: none; }
  html.is-leaving .page-wipe,
  html.is-covering .page-wipe { transform: translateX(100%); animation: none; }
}



/* ==========================================================================
   Home: the chef, the mat, and the closing invitation
   ========================================================================== */

.section__head .lede { margin: 0; }
.section__head .tile.hero__cta { align-self: flex-start; }

.trio { display: grid; gap: var(--gap); }
.trio > picture { display: block; min-width: 0; overflow: hidden; }
.trio img { width: 100%; height: 100%; object-fit: cover; display: block; }
.trio--chef  { grid-template-columns: 1.5fr 1fr 1fr; grid-auto-rows: clamp(320px, 46vw, 620px); }
.trio--village { grid-template-columns: 1fr 1fr 1fr; grid-auto-rows: clamp(280px, 34vw, 460px); }

.mat-band .section__head { margin-bottom: clamp(28px, 4vw, 48px); }

.reserve {
  position: relative;
  min-height: 78svh;
  display: flex; align-items: flex-end;
  overflow: hidden;
  background: var(--ember);
  color: var(--bone);
}
.reserve__media { position: absolute; inset: 0; }
.reserve__media picture { display: block; width: 100%; height: 100%; }
.reserve__img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 62%; display: block; }
.reserve__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0, 26, 0,0.18) 0%, rgba(0, 26, 0,0.82) 100%);
}
.reserve__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 18px; align-items: flex-start;
  padding: clamp(72px, 11vw, 160px) var(--pad);
  max-width: 60rem;
}
.reserve .label, .reserve .lede { color: var(--bone); }
.reserve h2 { font-size: clamp(30px, 4.6vw, 64px); line-height: 1.08; }

@media (max-width: 720px) {
  .trio--chef { grid-template-columns: 1fr 1fr; grid-auto-rows: clamp(200px, 52vw, 320px); }
  .trio--chef > :first-child { grid-column: 1 / -1; }
  .trio--village { grid-template-columns: 1fr; grid-auto-rows: clamp(240px, 80vw, 420px); }
}


/* ==========================================================================
   Films with sound: a play icon at the foot of a page, and its viewer
   ========================================================================== */

.film--split { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: clamp(28px, 5vw, 80px); align-items: center; }
.film--split .section__head { margin-bottom: 0; }

.film-card { position: relative; display: block; overflow: hidden; background: var(--ember); color: var(--fonio); }
.film-card--wide { width: 560px; aspect-ratio: 16 / 9; }
.film-card--tall { width: 260px; aspect-ratio: 9 / 16; }
.film-card img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.6s var(--ease); }
.film-card::after { content: ""; position: absolute; inset: 0; background: rgba(0, 26, 0, 0.28); transition: background 0.3s ease; }
.film-card__play {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 72px; height: 72px; margin: -36px 0 0 -36px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92); color: var(--ink);
  transition: transform 0.3s var(--ease), background 0.3s ease, color 0.3s ease;
}
.film-card__play svg { width: 30px; height: 30px; margin-left: 3px; }
.film-card__label { position: absolute; left: 0; bottom: 0; z-index: 1; }
.film-card:hover img, .film-card:focus-visible img { transform: scale(1.04); }
.film-card:hover::after, .film-card:focus-visible::after { background: rgba(0, 26, 0, 0.12); }
.film-card:hover .film-card__play, .film-card:focus-visible .film-card__play { background: var(--mat); color: var(--fonio); transform: scale(1.08); }
.film-card:hover { color: var(--fonio); }

.viewer { border: 0; padding: 0; background: #000; max-width: none; max-height: none; overflow: visible; }
.viewer::backdrop { background: rgba(0, 26, 0, 0.9); }
.viewer video { display: block; background: #000; max-width: 92vw; max-height: 86svh; }
.viewer--wide video { width: min(92vw, calc(86svh * 16 / 9)); }
.viewer--tall video { height: min(86svh, calc(92vw * 16 / 9)); }
.viewer__close { position: absolute; top: 0; right: 0; z-index: 2; cursor: var(--cur-point); }

@media (max-width: 820px) {
  .film--split { grid-template-columns: 1fr; }
  .film-card--wide { width: 100%; }
  .film-card--tall { width: min(220px, 60vw); }
}
@media (prefers-reduced-motion: reduce) {
  .film-card img, .film-card__play { transition: none; }
  .film-card:hover img, .film-card:focus-visible img { transform: none; }
}

/* ==========================================================================
   Clips set into a page
   ========================================================================== */

.motion { display: flex; flex-direction: column; gap: clamp(20px, 3vw, 36px); }
.motion > .section__head { margin-bottom: 0; }
.motion__label { margin: 0; }

.reel { position: relative; margin: 0; overflow: hidden; background: var(--ember); }
.reel video { display: block; width: 100%; height: 100%; object-fit: cover; cursor: var(--cur-point); }
.reel--v { aspect-ratio: 9 / 16; }
.reel--l { aspect-ratio: 16 / 9; }
.reel__cap { position: absolute; left: 0; bottom: 0; margin: 0; max-width: calc(100% - 10px); pointer-events: none; }

/* A plain grid: every clip is on screen at once, nothing scrolls sideways.
   As many columns as fit at a sensible size, so five clips fill five columns
   and six fill six on a wide screen, then three, then two on a phone. */
.reels--strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--gap);
}
.reels--pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap); }
@media (max-width: 1099px) { .reels--strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 599px)  { .reels--strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

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


/* The hero playlist: two stacked slots that cross-fade. */
.hero__slot { position: absolute; inset: 0; opacity: 0; overflow: hidden; transition: opacity 0.9s ease; }
.hero__slot.is-active { opacity: 1; }
.hero__slot video { position: relative; z-index: 1; }
.hero__slot--v::before {
  content: ""; position: absolute; inset: -60px;
  background: var(--bg) center / cover no-repeat;
  filter: blur(34px) brightness(0.72) saturate(1.1);
}
.hero__media .hero__slot--v video { object-fit: contain; object-position: center; }
@media (prefers-reduced-motion: reduce) { .hero__slot { transition: none; } }


/* The home gallery of dinner clips, played as a progress bar. The gallery
   pins to the screen while you scroll a tall track; a bar along the bottom
   fills as you go, each clip pops in when its turn comes, and then each pops
   off again, in its own direction, until the photographs take over. All of it
   is CSS scroll-driven animation on one timeline (the tall track), so it runs
   on the compositor and reverses when you scroll back up.

   Only where it can work: a browser that has scroll-driven animations and no
   reduced-motion setting, on any screen width (phones get three across, two
   rows). Anywhere else the clips are simply a grid of clips, as on the other pages.
   The stage is only as tall as its clips and pins with its middle at the middle
   of the screen, so there is no empty screen before or after. The ranges are
   shares of `cover`, the whole journey of the 340svh track through the screen:
   the stage pins at about 17.5% and lets go at about 82.5%, the clips pop in from
   6% (as the stage arrives) to 33%, hold, and have all gone by 80%. Change the
   track height or the stage and these need to move with it. */

.reels--reveal .reel:nth-child(1) { --i: 0; --dx: -34vw; --dy: -14vh; --rot: -18deg; }
.reels--reveal .reel:nth-child(2) { --i: 1; --dx: -21vw; --dy: -24vh; --rot:  -9deg; }
.reels--reveal .reel:nth-child(3) { --i: 2; --dx:  -8vw; --dy: -32vh; --rot:  -4deg; }
.reels--reveal .reel:nth-child(4) { --i: 3; --dx:   8vw; --dy: -28vh; --rot:   5deg; }
.reels--reveal .reel:nth-child(5) { --i: 4; --dx:  22vw; --dy: -20vh; --rot:  11deg; }
.reels--reveal .reel:nth-child(6) { --i: 5; --dx:  36vw; --dy: -13vh; --rot:  19deg; }

@keyframes clip-pop-in {
  from { opacity: 0; transform: translateY(44px) scale(0.5); }
  to   { opacity: 1; transform: none; }
}
@keyframes clip-pop-off {
  0%   { opacity: 1; transform: none; }
  60%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--rot)) scale(0.5); }
}
@keyframes reel-progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.reel-progress { display: none; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reel-reveal { height: 220svh; view-timeline: --strip block; }
    .reel-stage {
      position: sticky; top: max(0px, calc(50% - var(--stage-h, 0px) / 2));
      display: flex; flex-direction: column; gap: 28px;
      margin-inline: calc(var(--pad) * -1); padding-inline: 16px;
    }
    .reels--reveal {
      grid-auto-flow: row; grid-auto-columns: auto;
      grid-template-columns: repeat(6, minmax(0, 1fr));
      overflow: visible; padding: 0; margin: 0;
    }
    .reels--reveal .reel {
      will-change: transform, opacity;
      animation-name: clip-pop-in, clip-pop-off;
      animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1), cubic-bezier(0.55, 0, 1, 0.45);
      animation-fill-mode: both, forwards;
      animation-timeline: --strip, --strip;
      animation-range:
        cover calc(6% + var(--i) * 4%) cover calc(13% + var(--i) * 4%),
        cover calc(55% + var(--i) * 3.2%) cover calc(64% + var(--i) * 3.2%);
    }
    .reel-progress {
      display: block; height: 3px; margin-inline: 0;
      background: rgba(0, 47, 0, 0.14);
      position: relative; overflow: hidden;
    }
    .reel-progress::after {
      content: ""; position: absolute; inset: 0;
      background: var(--mat); transform-origin: left; transform: scaleX(0);
      animation: reel-progress linear both;
      animation-timeline: --strip;
      animation-range: cover 17.5% cover 82.5%;
    }

    /* Phones: three across, two rows, so all six fit on one screen. Each clip
       scatters outward from where it sits (left ones left, right ones right,
       the top row up, the bottom row down) and the captions step aside. */
    @media (max-width: 899px) {
      .reel-stage { padding-inline: 8px; gap: 20px; }
      .reels--reveal { grid-template-columns: repeat(3, minmax(0, 1fr)); }
      .reels--reveal .reel__cap { display: none; }
      .reels--reveal .reel:nth-child(1) { --dx: -60vw; --dy: -30vh; --rot: -16deg; }
      .reels--reveal .reel:nth-child(2) { --dx:   0vw; --dy: -42vh; --rot:   4deg; }
      .reels--reveal .reel:nth-child(3) { --dx:  60vw; --dy: -30vh; --rot:  16deg; }
      .reels--reveal .reel:nth-child(4) { --dx: -60vw; --dy:  30vh; --rot:  14deg; }
      .reels--reveal .reel:nth-child(5) { --dx:   0vw; --dy:  42vh; --rot:  -5deg; }
      .reels--reveal .reel:nth-child(6) { --dx:  60vw; --dy:  30vh; --rot: -14deg; }
    }
  }
}


/* ==========================================================================
   The brand palette in use
   ========================================================================== */

html { cursor: var(--cur); }
a, button, summary, label, select, [role="button"], .tile, .film-card, .plate, .nav-item,
input[type="radio"], input[type="checkbox"], input[type="submit"] { cursor: var(--cur-point); }
input[type="text"], input[type="email"], input[type="number"], textarea { cursor: text; }

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

/* clickable things fill with amber, and the label stays deep green (7.9:1) */
.hero__cta::before, .route::before, .footer__links .tile::before, .editions > summary::before,
.tile.hero__cta::before { background: var(--amber); }
.hero__cta:hover, .hero__cta:focus-visible,
.route:hover, .route:focus-visible,
.footer__links .tile:hover, .footer__links .tile:focus-visible,
.editions > summary:hover, .editions > summary:focus-visible,
.tile.hero__cta:hover, .tile.hero__cta:focus-visible { color: var(--ink); }
.route:hover .label, .route:focus-visible .label { color: var(--ink); }
.totop:hover, .totop:focus-visible { background: var(--amber); color: var(--ink); }

/* Dividers: the brand's line band, repeated along the page at its own
   proportions (a tile, never stretched). One pattern, used only here. */
.section + .section::before,
.gallery + .section::before,
.section + .gallery::before {
  height: 22px; opacity: 1;
  background: url("../img/patterns/tile-lines.webp") left center / auto 22px repeat-x;
}

/* The brand's circle marks each section's label; its triangle is the way back to the top. */
.section__head > .label::before, .page-head__inner > .label::before {
  content: ""; display: inline-block;
  width: 20px; height: 20px; margin-right: 10px; vertical-align: -6px;
  background: url("../img/patterns/flower-amber.webp") center / contain no-repeat;
}
.totop span {
  display: block; width: 24px; height: 26px; font-size: 0;
  background: url("../img/patterns/tri-rust.webp") center / contain no-repeat;
}
.totop:hover span, .totop:focus-visible span { background-image: url("../img/patterns/tri-green.webp"); }

/* One brand ornament under each page's title, at its natural shape. */
.page-head__orn { display: block; align-self: flex-start; height: clamp(44px, 5.5vw, 68px); width: auto; max-width: 100%; margin-top: 6px; }


/* Home's one suggestion: the next page in the menu, as a section of its own. */
.next-page {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: center;
}
.next-page__photo { display: block; overflow: hidden; }
.next-page__photo picture { display: block; }
.next-page__photo img {
  width: 100%; height: clamp(300px, 40vw, 560px);
  object-fit: cover; display: block;
  transition: transform 0.7s var(--ease);
}
.next-page__photo:hover img { transform: scale(1.03); }
.next-page__text { display: flex; flex-direction: column; gap: 18px; align-items: flex-start; }
.next-page__text h2 { font-size: clamp(30px, 4.4vw, 60px); line-height: 1.06; }
.next-page__text .lede { margin: 0; }
@media (max-width: 820px) {
  .next-page { grid-template-columns: 1fr; }
  .next-page__photo img { height: clamp(240px, 62vw, 380px); }
}
@media (prefers-reduced-motion: reduce) {
  .next-page__photo img { transition: none; }
  .next-page__photo:hover img { transform: none; }
}


.actions { display: flex; flex-wrap: wrap; gap: var(--gap); align-items: flex-start; }

/* ==========================================================================
   Stories: the list, and the template every story uses
   ========================================================================== */

.story-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: var(--gap); }
.story-card {
  display: flex; flex-direction: column; gap: 10px; padding-bottom: 20px;
  background: rgba(255, 255, 255, 0.9);
}
.story-card__photo { display: block; aspect-ratio: 3 / 2; overflow: hidden; background: var(--paper); }
.story-card__photo picture { display: block; height: 100%; }
.story-card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.6s var(--ease); }
.story-card:hover .story-card__photo img { transform: scale(1.03); }
.story-card__meta { display: flex; flex-wrap: wrap; gap: 4px 14px; padding: 6px 20px 0; }
.story-card h3 { margin: 0; padding: 0 20px; font-size: 20px; line-height: 1.25; letter-spacing: -0.02em; }
.story-card p { margin: 0; padding: 0 20px; font-size: 14px; color: var(--muted-light); }

.story__inner { max-width: 70ch; display: flex; flex-direction: column; gap: clamp(20px, 3vw, 32px); }
.story__meta { display: flex; flex-wrap: wrap; gap: 4px 18px; }
.story__photo { margin: 0; }
.story__photo img { width: 100%; height: auto; max-height: 72vh; object-fit: cover; }
.story__body p { font-size: 17px; line-height: 1.75; max-width: none; }
.story__quote { position: relative; margin: 0; padding: 6px 0 6px 26px; }
.story__quote::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--mat); }
.story__quote p { max-width: none; font-size: clamp(20px, 2.4vw, 28px); line-height: 1.35; letter-spacing: -0.02em; }
.story__quote footer { margin-top: 10px; }
.story__actions { display: flex; flex-wrap: wrap; gap: var(--gap); }

@media (prefers-reduced-motion: reduce) {
  .story-card__photo img { transition: none; }
  .story-card:hover .story-card__photo img { transform: none; }
}


/* ==========================================================================
   Press coverage: logo cards on the Stories page, and the strip on About
   ========================================================================== */

.story-card--press { padding-bottom: 20px; }
.story-card__logo { display: grid; place-items: center; height: 120px; padding: 20px; background: #fff; }
.story-card__logo img { max-width: 70%; max-height: 64px; width: auto; height: auto; object-fit: contain; }
.story-card--press:hover { background: var(--amber); }
.story-card--press:hover .label, .story-card--press:hover h3 { color: var(--ink); }
.story-card--press h3 { font-size: 17px; }

.press .actions { margin-top: 6px; }
.marquee { overflow: hidden; margin-inline: calc(var(--pad) * -1); }
.marquee + .marquee { margin-top: var(--gap); }
.marquee__track { display: flex; width: max-content; animation: marquee 130s linear infinite; }
.marquee--reverse .marquee__track { animation-direction: reverse; }
.marquee:hover .marquee__track, .marquee:focus-within .marquee__track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }

.press-tile {
  flex: 0 0 auto; width: clamp(230px, 22vw, 300px); margin-right: var(--gap);
  display: flex; flex-direction: column; gap: 8px; padding-bottom: 18px;
  background: rgba(255, 255, 255, 0.92);
  transition: background 0.25s ease;
}
.press-tile__logo { display: grid; place-items: center; height: 104px; padding: 18px; background: #fff; }
.press-tile__logo img { max-width: 70%; max-height: 60px; width: auto; height: auto; object-fit: contain; }
.press-tile__pub, .press-tile__title { padding: 0 18px; }
.press-tile__title {
  font-size: 13px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 4.35em;
}
.press-tile:hover, .press-tile:focus-visible { background: var(--amber); }
.press-tile:hover .press-tile__pub, .press-tile:focus-visible .press-tile__pub { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee__track { animation: none; }
  .marquee__track [aria-hidden="true"] { display: none; }
}

/* The scattered clips end up well outside the screen; clip them at the section's
   edge so they cannot widen the page (`clip`, unlike `hidden`, keeps the pinned
   stage sticky). */
.mat-band { overflow-x: clip; }

.event__details { font-family: var(--mono); font-size: 12px; line-height: 1.5; color: var(--muted-light); max-width: 52ch; }
.event:has(input:checked) .event__details { color: var(--fonio); opacity: 0.9; }
.booking__empty[hidden] { display: none; }


/* A dated list: year, what, and a line about it (awards, previous dinners). */
.ledger { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gap); }
.ledger__row { display: grid; grid-template-columns: 6.5rem minmax(0, 1.2fr) minmax(0, 1.8fr); gap: var(--gap); }
.ledger__row .tile { padding: 14px 18px; }
.ledger__main { font-size: 15px; letter-spacing: -0.01em; }
.ledger__note { font-size: 13px; color: var(--muted-light); }
@media (max-width: 720px) {
  .ledger__row { grid-template-columns: 4.5rem minmax(0, 1fr); }
  .ledger__note { grid-column: 1 / -1; }
}

/* ==========================================================================
   About: a slowly, continuously crossfading set of photos, replacing the
   single static portrait beside the bio. Pure CSS — one shared @keyframes,
   staggered per photo by --i (set inline in build.py), running on an
   animation-duration long enough to give every photo its own turn once per
   loop. The percentages below assume the 29 photos currently in the list
   (portrait_rotator_html() in build.py) — if that list's length changes,
   TOTAL below and these percentages should move with it: each photo gets
   TOTAL/29 seconds, and the keyframe spends about a third of that fading in,
   most of it held, and a third fading out (deliberately overlapping the next
   photo's own fade-in, for a dissolve rather than a hard cut).
   ========================================================================== */

.portrait-rotator {
  position: relative;
  width: 100%;
  height: clamp(320px, 40vw, 560px);
  overflow: hidden;
  background: var(--paper);
}
.portrait-rotator__frame {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 0.35s ease;
}
.portrait-rotator__frame.is-active { opacity: 1; }
.portrait-rotator__frame picture,
.portrait-rotator__frame img { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (prefers-reduced-motion: reduce) {
  .portrait-rotator__frame { transition: none; }
}

.portrait-rotator__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.2s ease;
}
.portrait-rotator__arrow svg { width: 1.1rem; height: 1.1rem; }
.portrait-rotator__arrow:hover,
.portrait-rotator__arrow:focus-visible { background: #fff; }
.portrait-rotator__arrow--prev { left: 0.75rem; }
.portrait-rotator__arrow--next { right: 0.75rem; }

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