/* ---- tokens.css ---- */
/* Design tokens — abovethehook.com.
   Palette per IA §2, inherited from sessions.abovethehook.com. Nothing
   else lives here. */

:root {
  /* Ground is the near-black navy, not the brand navy: this site is lit
     the way Sessions is, at the end of the day. Brand navy is a mid-tone
     surface — cards, bands, the header once it has scrolled. */
  --ground:    #0B1524;
  --navy:      #1C2E4A;
  --cream:     #F5EFE4;
  --sand:      #EFE7D8;   /* the one warm ground, for Preserve the Hook */
  --rust:      #C14D2E;

  /* The brand rust is 3.81:1 on the ground and 4.20:1 on the cream — both
     under the 4.5 that small text needs for AA. It stays exactly as
     specified for fills, borders, and the focus ring, where the threshold
     is 3:1 and it passes. Text gets a tuned pair instead. Measured values
     carried over from ath-sessions tokens.css. */
  --rust-on-dark:  #D96A45;   /* 5.32 on the ground */
  --rust-on-cream: #B44327;   /* 4.86 on the cream, 4.53 on the sand —
                                 one step darker than it needs on cream so
                                 that one rust clears both light grounds */
  --rust-text: var(--rust-on-dark);

  /* Rust as a FILL under cream text is a third case, and the brand value
     does not carry it: cream on #C14D2E is 4.20:1, under the 4.5 that
     normal text needs. Measured, not guessed. The deep variant is 4.80
     and is what every filled rust control uses. */
  --rust-fill: #B54428;

  /* And a fourth: on the mid-tone navy, which is lighter than the page
     ground, --rust-on-dark falls to 3.96:1. This clears 4.75 there and
     8.45 on the ground, so it is safe on either dark surface. It is not
     for the cream — 1.89 — where --rust-on-cream belongs. */
  --rust-on-mid: #E07E5C;

  /* Text on the ground, by weight of voice. */
  --ink-strong: var(--cream);
  --ink:        rgba(245, 239, 228, .84);
  --ink-muted:  rgba(245, 239, 228, .62);
  --ink-faint:  rgba(245, 239, 228, .55);

  --line:      rgba(245, 239, 228, .16);
  --line-dark: rgba(28, 46, 74, .16);

  /* Type families. Both self-hosted; see typography.css. */
  --display: "Fraunces", Georgia, "Times New Roman", serif;
  --body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Measure and rhythm. */
  --measure: 34rem;
  --gutter:  clamp(1.25rem, 5vw, 3rem);
  --space-block:   2rem;
  --space-section: clamp(4rem, 10vw, 8rem);

  /* Motion. Slow by default — the panels are films, not transitions. */
  --motion-fast: 240ms;
  --motion-med:  600ms;
  --motion-slow: 1200ms;
  --easing: cubic-bezier(0.4, 0.0, 0.2, 1.0);

  --z-header: 20;
  --z-panel-media: 1;
  --z-panel-scrim: 2;
  --z-panel-copy: 3;
  --z-skip-link: 100;
}

/* ---- typography.css ---- */
/* Self-hosted faces and the type scale.
   No Google Fonts CDN — the faces ship from this origin so a cold visit
   is one connection, and so the site does not depend on a third party
   being up. Both are variable, one file each. */

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/Fraunces.woff2") format("woff2");
}

@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/Fraunces-Italic.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/Inter.woff2") format("woff2");
}

/* One line per panel (IA §2), so display type is set large and tight and
   is expected to wrap to two or three lines, never to a paragraph. */
.t-display {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2.25rem, 6.5vw, 5rem);
  line-height: 1.04;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.t-lead {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.5rem, 3.2vw, 2.5rem);
  line-height: 1.16;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* The one-word panel labels in "What we do". */
.t-word {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.75rem, 4vw, 3rem);
  line-height: 1;
  letter-spacing: -0.015em;
}

.t-body {
  font-family: var(--body);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.6;
  max-width: var(--measure);
}

/* Letterspaced small caps for eyebrows, section labels, nav. */
.t-label {
  font-family: var(--body);
  font-weight: 500;
  font-size: 0.75rem;
  line-height: 1.2;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* ---- base.css ---- */
/* Reset, document ground, and the shared primitives. */

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

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 100%;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, video { display: block; max-width: 100%; }

a { color: inherit; text-decoration: none; }

/* Visible focus everywhere, on the rust — 3:1 against the ground, which is
   the threshold a focus indicator has to clear. */
:focus-visible {
  outline: 2px solid var(--rust);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--rust); color: var(--cream); }

/* Skip link. Visible only on keyboard focus (IA §2 restraint, §6 a11y). */
.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -100%;
  z-index: var(--z-skip-link);
  padding: 0.75rem 1.25rem;
  background: var(--cream);
  color: var(--ground);
  font-family: var(--body);
  font-size: 0.875rem;
  transition: top var(--motion-fast) var(--easing);
}
.skip-link:focus { top: 1rem; }

.wrap {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

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

/* Every animation on this site is decorative. Reduced motion turns all of
   it off, with no exceptions (IA §6) — the video panels swap to their
   posters via the same query in panels.css. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- opener.css ---- */
/* The opener — the first screen, and the whole design of the homepage.
   Ported from ath-sessions (site/static/css/home.css §loop, §top row,
   §the four words, §the line at the foot), which is the design lineage
   this site inherits (IA §2).

   One screen, no scroll, nothing under it.

   The lock is opt-in and only where it is honest: below 900px wide or
   700px tall everything scrolls normally, because forcing one screen onto
   a phone would put four words in a cramped row. What a phone gets
   instead is further down — a stack of screens, one per word. */

@media (min-width: 900px) and (min-height: 700px) {
  html:has(> body.one-screen) { height: 100%; overflow: hidden; }
  .one-screen { height: 100dvh; overflow: hidden; display: block; }
}

.loop {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--ground) var(--poster, none) center / cover no-repeat;
}

.loop__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Hover layers stack over the base loop and cross-fade. Built by the
   script on first hover of the word that owns each one, so a visitor who
   reaches for nothing downloads nothing beyond the opening film. */
.loop__video--alt {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--motion-med) var(--easing);
}
.loop__video--alt.is-on { opacity: 1; }

/* An autoplaying loop is exactly what this setting asks not to see. The
   poster is already painted on the container, so dropping the video
   leaves the first frame standing. */
@media (prefers-reduced-motion: reduce) {
  .loop__video { display: none; }
}

/* A band across the middle, under the words.
   The wash below is weakest exactly where the words sit — by design, so
   the film is least obscured at its centre. That works on Sessions,
   whose footage is dark there. This film is a sunset: the sky and the
   skyline land in the same band as the type, and measured against the
   brightest frame of the loop the words came out at 3.67:1, under the
   4.5 AA wants. This band is the smallest correction that fixes it —
   0.55 at the centre, feathered out by a third of the height either
   side, which leaves the sky and the water untouched. Re-measure if the
   footage is ever changed. */
.loop::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom,
    transparent 34%,
    rgba(11, 21, 36, .55) 44%,
    rgba(11, 21, 36, .55) 56%,
    transparent 66%);
}

/* A wash over the footage so the type holds on every frame of it — not
   just on the poster. Checked against the brightest frame, per IA §6. */
.loop::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 100% at 50% 50%,
                    rgba(11, 21, 36, .18) 0%, rgba(11, 21, 36, .52) 100%),
    linear-gradient(to bottom,
                    rgba(11, 21, 36, .46) 0%,
                    rgba(11, 21, 36, .12) 42%,
                    rgba(11, 21, 36, .52) 100%);
}

/* ── top row ─────────────────────────────────────────────────────── */

.home-top {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 26px 30px;
}

.home-mark {
  font-family: var(--body);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cream);
  white-space: nowrap;
}

.home-mark--boxed { border: 1px solid var(--cream); padding: 5px 10px 6px; }

@media (max-width: 720px) {
  .home-top { padding: 18px; }
  .home-mark { font-size: 14px; letter-spacing: .12em; }
}


/* ── the hamburger ───────────────────────────────────────────────── */

.home-menu__toggle {
  background: none;
  border: 0;
  padding: 10px 0 10px 10px;
  cursor: pointer;
  color: var(--cream);
}

.home-menu__bars,
.home-menu__bars::before,
.home-menu__bars::after {
  display: block;
  width: 26px;
  height: 1.5px;
  background: currentColor;
}
.home-menu__bars { position: relative; }
.home-menu__bars::before,
.home-menu__bars::after { content: ""; position: absolute; left: 0; }
.home-menu__bars::before { top: -8px; }
.home-menu__bars::after  { top: 8px; }

/* ── the words in the middle ─────────────────────────────────────── */

.home-centre {
  position: fixed;
  z-index: 2;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  justify-content: center;
}

.home-centre ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px clamp(24px, 4vw, 54px);
  padding: 0 20px;
}

.home-centre__link {
  font-family: var(--body);
  font-size: clamp(13px, 1.2vw, 15px);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(245, 239, 228, .66);
  transition: color .18s var(--easing);
}
.home-centre__link:hover,
.home-centre__link:focus-visible,
.home-centre__link--on { color: var(--cream); }

/* The one rust element (IA §3). A filled chip rather than rust text:
   over this footage rust glyphs top out at 3.67:1 even under a scrim
   heavy enough to look like a bar, whereas cream on the deep fill is
   4.80:1 and the word reads on any frame. */
.home-centre__link--cta {
  background: var(--rust-fill);
  color: var(--cream);
  padding: 8px 14px;
  border-radius: 2px;
}
.home-centre__link--cta:hover,
.home-centre__link--cta:focus-visible {
  background: var(--rust);
  color: var(--cream);
}

/* ── the line at the foot ────────────────────────────────────────── */

.home-bottom {
  position: fixed;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 26px;
  text-align: center;
  font-family: var(--body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .14em;
  line-height: 1.6;
  text-transform: uppercase;
  color: rgba(245, 239, 228, .82);
  padding: 0 20px;
}

@media (max-width: 720px) {
  .home-bottom { bottom: 20px; font-size: 10px; letter-spacing: .12em; }
}

/* ── the menu overlay ────────────────────────────────────────────── */
/* Ships in the markup and starts hidden, so with no JavaScript it is
   simply not there rather than stuck open across the screen. */

.menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--ground);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.menu[hidden] { display: none; }

.menu__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 26px 30px;
}

.menu__close {
  background: none;
  border: 0;
  padding: 10px;
  cursor: pointer;
  color: var(--cream);
}

.menu__x { display: block; position: relative; width: 24px; height: 24px; }
.menu__x::before,
.menu__x::after {
  content: "";
  position: absolute;
  top: 50%; left: 0;
  width: 24px; height: 1.5px;
  background: currentColor;
}
.menu__x::before { transform: rotate(45deg); }
.menu__x::after  { transform: rotate(-45deg); }

.menu__primary { padding: 22px 30px 34px; }
.menu__primary li + li { margin-top: 6px; }

.menu__primary-link {
  font-family: var(--display);
  font-size: clamp(2rem, 7vw, 3.5rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--cream);
  transition: color var(--motion-fast) var(--easing);
}
.menu__primary-link:hover { color: var(--ink-muted); }
.menu__primary-link--on {
  text-decoration: underline;
  text-underline-offset: 8px;
  text-decoration-thickness: 3px;
}

.menu__cta { padding: 0 30px 28px; }

.menu__cta-link {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rust-on-dark);
}
.menu__arrow { transition: transform var(--motion-fast) var(--easing); }
.menu__cta-link:hover .menu__arrow { transform: translateX(5px); }

.menu__cta-note {
  margin-top: 0.6rem;
  font-size: 0.9375rem;
  color: var(--ink-muted);
  max-width: 34ch;
}

.menu__secondary { padding: 28px 30px 0; border-top: 1px solid var(--line); }
.menu__secondary li + li { margin-top: 14px; }
.menu__secondary a {
  font-family: var(--body);
  font-size: 0.9375rem;
  letter-spacing: .04em;
  color: var(--ink-muted);
}
.menu__secondary a:hover { color: var(--cream); }

.menu__foot {
  margin-top: auto;
  padding: 28px 30px;
  color: var(--ink-faint);
  font-size: 0.75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* ── the homepage on a phone ─────────────────────────────────────── */
/* Four words centred in one frame is a cramped row on a phone, so the
   same words become a stack of screens you swipe through — one per
   section — with the list fixed along the bottom and the word you have
   reached grown. Ported from ath-sessions home.css. */

.home-panels { display: none; }

@media (max-width: 720px) {
  .home-panels { display: block; }

  /* The snap goes on the document rather than a scroller inside it: a
     nested scroller swallows the swipe and pins the address bar open, so
     100dvh stops meaning the screen. */
  html:has(> body.home--loop) {
    scroll-snap-type: y mandatory;
    scroll-behavior: smooth;
  }

  .home-panel {
    position: relative;
    height: 100dvh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    overflow: hidden;
  }

  /* The first frame is the homepage as it stands: the fixed loop showing
     through it, the wordmark over it, nothing of its own. */
  .home-panel--home { background: none; }

  .home-panel__shot {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* The same wash the loop carries, so the words read on any frame. */
  .home-panel::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom,
                rgba(11, 21, 36, .42) 0%,
                rgba(11, 21, 36, .15) 40%,
                rgba(11, 21, 36, .72) 100%);
  }
  .home-panel--home::after { display: none; }

  /* The words: fixed in the corner rather than centred in one frame, so
     the whole menu is on screen from the first screen and stays there the
     whole way down. */
  .home-centre {
    display: flex;
    position: fixed;
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 3;
    transform: none;
    justify-content: flex-start;
    /* 64px of this was the foot line's slot. With no line the rail
       only needs to clear the edge and the home indicator. */
    padding: 0 0 calc(28px + env(safe-area-inset-bottom, 0px));
    /* The bar is only a frame for the list; taps go through it to the
       screen behind. */
    pointer-events: none;
  }

  .home-centre ul {
    flex-direction: column;
    align-items: flex-start;
    gap: 9px;
    padding: 0 20px;
    pointer-events: auto;
  }

  .home-centre__link {
    display: inline-block;
    font-size: 17px;
    letter-spacing: .02em;
    line-height: 1.05;
    color: rgba(245, 239, 228, .45);
    /* Size rather than scale, so the row it sits in actually makes room
       for it and the words below are pushed clear instead of overlapped. */
    transition: font-size .24s cubic-bezier(.2, .7, .3, 1), color .24s;
  }

  /* Restated inside the query: the plain colour above is declared later
     in this file than the desktop --cta rule and at the same specificity,
     so without this the one rust word turns grey on a phone. */
  /* The chip keeps its fill on a phone; only the plain words restate. */
  .home-centre__link--cta { color: var(--cream); padding: 6px 12px; }

  .home-centre__link.is-on { font-size: 27px; color: var(--cream); }

  /* The top row and the foot line stay put over the whole stack.
     Scoped to the homepage: these were unscoped, and every other page
     that carries a top row — /work/ — got a floating header with its
     content running underneath it. */
  .home--loop .home-top { position: fixed; top: 0; left: 0; right: 0; }
  .home--loop .home-bottom { bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }
}

@media (max-width: 720px) and (prefers-reduced-motion: reduce) {
  .home-centre__link { transition: none; }
  html:has(> body.home--loop) { scroll-behavior: auto; }
}

/* ---- panels.css ---- */
/* The full-bleed panel — the composition unit for the whole site.
   IA §2: edge-to-edge visual panels, one line of copy each, motion by
   default with stills as fallback.

   Structure a panel renders as:

     <section class="panel">
       <div class="panel__media">           poster <img>, optional <video>
       <div class="panel__scrim">           gradient for text contrast
       <div class="panel__copy">            the one line

   The poster image is always present and always what loads first — it is
   what counts for LCP (IA §6). The video is an enhancement layered over
   it, and every path that removes the video leaves a complete panel. */

.panel {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 70svh;
  overflow: hidden;
  isolation: isolate;
}

.panel--tall  { min-height: 92svh; }
.panel--short { min-height: 52svh; }

/* Panels sit edge to edge regardless of the page wrapper. */
.panel__media {
  position: absolute;
  inset: 0;
  z-index: var(--z-panel-media);
}

/* A <picture> is an inline box of automatic height, so an <img> inside
   one has nothing for its height: 100% to resolve against and collapses.
   Give the wrapper the frame the image is meant to fill. */
.panel__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.panel__media img,
.panel__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The video stacks on top of its poster and fades in once it can actually
   play. Until then — and forever, if it never loads — the poster is what
   is on screen. No spinner, no black frame. */
.panel__media video {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--motion-med) var(--easing);
}
.panel__media video[data-playing] { opacity: 1; }

/* Text over video needs a scrim at every breakpoint, checked against the
   worst frame of the loop rather than the poster (IA §6). Two stops: a
   floor under the copy, and a light wash over the whole panel so a bright
   frame cannot wash out the type at the top edge. */
.panel__scrim {
  position: absolute;
  inset: 0;
  z-index: var(--z-panel-scrim);
  background:
    linear-gradient(to top,
      color-mix(in srgb, var(--ground) 92%, transparent) 0%,
      color-mix(in srgb, var(--ground) 62%, transparent) 32%,
      transparent 68%),
    color-mix(in srgb, var(--ground) 18%, transparent);
}

.panel__copy {
  position: relative;
  z-index: var(--z-panel-copy);
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding: var(--gutter);
  padding-block-end: clamp(2.5rem, 7vw, 5rem);
  color: var(--ink-strong);
}

.panel__eyebrow {
  color: var(--rust-text);
  margin-bottom: 1rem;
}

.panel__line {
  max-width: 20ch;
  margin-bottom: 1.5rem;
}

/* One line per panel means the supporting sentence is short and is allowed
   to sit under the display line — never a paragraph. */
.panel__note {
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--ink);
  max-width: 42ch;
}

/* ── actions ───────────────────────────────────────────────────────── */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  font-family: var(--body);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-strong);
  transition: background var(--motion-fast) var(--easing),
              border-color var(--motion-fast) var(--easing),
              color var(--motion-fast) var(--easing);
}
.btn:hover { border-color: var(--cream); }

.btn--primary {
  background: var(--rust-fill);
  border-color: var(--rust-fill);
  color: var(--cream);
}
.btn--primary:hover {
  background: transparent;
  color: var(--rust-text);
  border-color: var(--rust);
}

/* ── motion policy ─────────────────────────────────────────────────── */
/* Reduced motion shows stills throughout, no exceptions (IA §6). The rule
   hides the element rather than pausing it, so nothing decodes either. */

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

/* Mobile serves the poster by default. The script only attaches a source
   on a wifi-class connection, but this is the belt to that braces: a
   narrow viewport never shows motion it was not explicitly given. */
@media (max-width: 640px) {
  .panel__media video:not([data-motion-ok]) { display: none; }
}

/* The quieter half of a pair of actions. Same type as the button so the
   two sit level, no box, so only one of them reads as the ask. */
.btn-plain {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 0;
  font-family: var(--body);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--cream);
}

.btn-plain:hover { text-decoration: underline; text-underline-offset: 5px; }

.btn-plain:focus-visible {
  outline: 2px solid var(--rust-on-dark);
  outline-offset: 3px;
}

/* ---- sections.css ---- */
/* The homepage sections that are not plain panels.
   Panels stay edge-to-edge; these are the composed sections between them. */

.section {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding: var(--space-section) var(--gutter);
}

.section__eyebrow {
  color: var(--band-accent, var(--rust-text));
  margin-bottom: 1.5rem;
}

.section__eyebrow--standalone {
  max-width: 78rem;
  margin-inline: auto;
  padding: var(--space-section) var(--gutter) 0;
}

.section__lead {
  color: var(--band-strong, var(--ink-strong));
  max-width: 24ch;
  margin-bottom: 1rem;
}

.section__note {
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 46ch;
}

/* ── what we do ──────────────────────────────────────────────────────
   Five cells. Wide screens give the first two a full row and the last
   three a row of their own, so the group reads as one composition rather
   than a grid with an orphan. */

.cap-grid {
  display: grid;
  gap: 2px;
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 900px) {
  .cap-grid { grid-template-columns: repeat(6, 1fr); }
  .cap:nth-child(1), .cap:nth-child(2) { grid-column: span 3; }
  .cap:nth-child(3), .cap:nth-child(4), .cap:nth-child(5) { grid-column: span 2; }
}

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

.cap {
  position: relative;
  min-height: 22rem;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
}

.cap__media { position: absolute; inset: 0; z-index: var(--z-panel-media); }
.cap__media img,
.cap__media video {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--motion-slow) var(--easing);
}
.cap__media video {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--motion-med) var(--easing);
}
.cap__media video[data-playing] { opacity: 1; }
.cap:hover .cap__media img { transform: scale(1.03); }

.cap__scrim {
  position: absolute; inset: 0;
  z-index: var(--z-panel-scrim);
  background: linear-gradient(to top,
    color-mix(in srgb, var(--ground) 90%, transparent) 0%,
    color-mix(in srgb, var(--ground) 45%, transparent) 45%,
    color-mix(in srgb, var(--ground) 15%, transparent) 100%);
}

.cap__copy {
  position: relative;
  z-index: var(--z-panel-copy);
  padding: clamp(1.25rem, 3vw, 2rem);
  color: var(--ink-strong);
}

.cap__line {
  margin-top: 0.5rem;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--ink);
  max-width: 30ch;
}

/* ── the rooms ───────────────────────────────────────────────────────
   Stills, warmer than the rest of the page. The house does not need to
   move to make its point. */

/* The rooms belong to the studio panel above them, so they sit directly
   under it. The section's usual top padding would read as a break between
   two unrelated things. */
.section--rooms { padding-top: 2px; max-width: none; padding-inline: 0; }

.room-grid {
  display: grid;
  gap: 2px;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.room { position: relative; }
.room img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.room__name {
  position: absolute;
  left: 1rem; bottom: 1rem;
  color: var(--cream);
  text-shadow: 0 1px 12px rgba(11, 21, 36, .9);
}

/* ── publishing ──────────────────────────────────────────────────────
   Three cards, concrete claims, no metrics. */

.pub-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  margin-top: var(--space-block);
}

.pubcard a {
  display: block;
  height: 100%;
  border: 1px solid var(--line);
  border-radius: 2px;
  overflow: hidden;
  background: color-mix(in srgb, var(--navy) 45%, transparent);
  transition: border-color var(--motion-fast) var(--easing),
              transform var(--motion-fast) var(--easing);
}
.pubcard a:hover {
  border-color: var(--rust);
  transform: translateY(-2px);
}

.pubcard img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

.pubcard__body { padding: 1.25rem 1.25rem 1.5rem; }

.pubcard__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.25rem;
  line-height: 1.15;
  color: var(--ink-strong);
  margin-bottom: 0.5rem;
}

.pubcard__claim {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

/* ── preserve the hook ───────────────────────────────────────────────
   A change in register. Cream ground, archival, slower. It is the only
   light band on the page, and that is the whole point — the section
   should not look like the sales pitch around it. */

.section--preserve {
  max-width: none;
  width: 100%;
  padding: 0;
  background: var(--cream);
  color: #2A2118;
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .section--preserve { grid-template-columns: 1fr 1fr; align-items: stretch; }
}

.preserve__media img {
  width: 100%; height: 100%;
  min-height: 20rem;
  object-fit: cover;
  filter: saturate(0.85);
}

.preserve__copy {
  padding: var(--space-section) var(--gutter);
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 34rem;
}

.preserve__title { color: #1A1208; margin-bottom: 1rem; }

.preserve__line {
  font-size: 1.125rem;
  line-height: 1.6;
  color: #3D3020;
  max-width: 40ch;
}

/* On the cream, the brand rust drops to 4.20:1 — under what small text
   needs. The tuned deep variant is used here instead. */
.section--preserve .btn {
  border-color: #B54428;
  color: #B54428;
}
.section--preserve .btn:hover {
  background: #B54428;
  color: var(--cream);
}

.preserve__secondary {
  align-self: center;
  font-size: 0.9375rem;
  color: #3D3020;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.preserve__secondary:hover { color: #B54428; }

/* ── shop strip ──────────────────────────────────────────────────────
   Small, on purpose. */

.section--shop {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: var(--space-block);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.shop__line {
  font-family: var(--display);
  font-size: 1.25rem;
  color: var(--ink);
}

.shop__cta {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rust-text);
}
.shop__cta:hover { text-decoration: underline; text-underline-offset: 4px; }

.signup__field {
  flex: 1 1 16rem;
  padding: 0.85rem 1rem;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--ink-strong);
  font-family: var(--body);
  font-size: 1rem;
}
.signup__field::placeholder { color: var(--ink-faint); }
.signup__field:focus { border-color: var(--rust); }

/* Honeypot. Hidden from people and from assistive technology, left in the
   DOM for anything filling every field it finds. */
.signup__trap { position: absolute; left: -9999px; }

/* ── prose ───────────────────────────────────────────────────────────
   Interior pages carry real paragraphs. The homepage does not — one line
   per panel is the rule there (IA §2), and this is the exception the
   interior routes are allowed. */

.prose__heading {
  color: var(--band-strong, var(--ink-strong));
  margin-bottom: 1.5rem;
  max-width: 24ch;
}

/* The sentence under a heading, before the body proper. */
.prose__lead {
  font-size: 1rem;
  line-height: 1.7;
  color: var(--band-ink, var(--ink));
  max-width: 54ch;
  margin: 0 0 1.25rem;
}

.prose p {
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--band-ink, var(--ink));
  max-width: var(--measure);
}
.prose p + p { margin-top: 1.25rem; }

/* ── named lists ─────────────────────────────────────────────────── */

.item-list {
  display: grid;
  gap: 1px;
  margin-top: var(--space-block);
  border-top: 1px solid var(--band-line, var(--line));
}

.item {
  display: grid;
  gap: 0.35rem 2rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--band-line, var(--line));
}

@media (min-width: 760px) {
  .item { grid-template-columns: 16rem 1fr; align-items: baseline; }
}

.item__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.125rem;
  color: var(--band-strong, var(--ink-strong));
}

.item__line {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--band-muted, var(--ink-muted));
  max-width: 52ch;
}

/* ── forms ───────────────────────────────────────────────────────────
   Every field has a real, visible label. Placeholders are not labels:
   they disappear on focus, are read inconsistently by assistive
   technology, and leave a half-filled form unidentifiable. */

.inquiry {
  display: grid;
  gap: 1.5rem;
  max-width: 40rem;
  margin-top: var(--space-block);
}

.form__expectation { margin-bottom: 0.5rem; }

.field { display: grid; gap: 0.5rem; }

.field--choice { border: 0; padding: 0; margin: 0; }

.field__label {
  font-family: var(--body);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-strong);
  padding: 0;
}

.field__req { color: var(--rust-text); }

.field__hint {
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--ink-faint);
}

.field__input {
  width: 100%;
  padding: 0.85rem 1rem;
  background: color-mix(in srgb, var(--navy) 30%, transparent);
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--ink-strong);
  font-family: var(--body);
  font-size: 1rem;
  line-height: 1.4;
}
.field__input:focus { border-color: var(--rust); }

textarea.field__input { resize: vertical; min-height: 6rem; }

/* The native select arrow is invisible on a dark control in several
   browsers, so the chevron is drawn here and the native one removed. */
select.field__input {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-muted) 50%),
    linear-gradient(135deg, var(--ink-muted) 50%, transparent 50%);
  background-position:
    calc(100% - 20px) calc(50% + 2px),
    calc(100% - 14px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.75rem;
}
select.field__input option { background: var(--ground); color: var(--cream); }

.choices { display: grid; gap: 0.6rem; margin-top: 0.25rem; }

.choice {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 1rem;
  color: var(--ink);
  cursor: pointer;
}
.choice input { accent-color: var(--rust); width: 1.05rem; height: 1.05rem; }

.inquiry .btn { justify-self: start; }

}

/* ---- work.css ---- */
/* The portfolio rail and the player sheet.
   Ported from ath-sessions home.css (§marquee, §tiles, §player sheet),
   which is where this composition comes from. */

/* The backdrop: the still, blurred, behind everything. It is the only
   thing on the page that is not type or photograph, and it stops the
   tiles floating on flat navy. */
.bleed {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--ground) var(--backdrop, none) center / cover no-repeat;
}
.bleed::after {
  content: "";
  position: absolute;
  inset: 0;
  backdrop-filter: blur(26px) saturate(0.9);
  background: rgba(11, 21, 36, .72);
}

/* ── the rail ────────────────────────────────────────────────────── */

.marquee {
  --tile: clamp(280px, 30vw, 560px);
  display: flex;
  gap: 14px;
  padding: 0 30px 0 24px;
  margin: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.marquee::-webkit-scrollbar { display: none; }

@media (max-width: 720px) { .marquee { padding: 0 18px; gap: 10px; } }

.marquee__item {
  flex: 0 0 var(--tile);
  scroll-snap-align: start;
}

/* The frame is 16:9, matching the films themselves.
   Sessions' tiles are square because its stills are photographs, and a
   photograph survives a centre crop. These are film covers with the
   title lettered across the full width, so squaring them cut the words
   off at both ends — "SAIL4TH250" arrived as "L4TH250". At the frame's
   own ratio nothing is cropped at all. */
.tile { display: block; color: var(--cream); }

.tile__frame {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--navy);
}

.tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .5s cubic-bezier(.2, .7, .3, 1);
}
a.tile:hover img,
a.tile:focus-visible img { transform: scale(1.03); }

/* The covers letter their own titles across the middle. Ours used to be
   printed over that, which put two sets of type in the same place; it
   sits under the frame now, and the date is not printed at all. All the
   picture carries is the state, in the middle — so the gradient only has
   to keep that legible. */
.tile__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(11, 21, 36, .5) 0%, transparent 24%),
    linear-gradient(to top, rgba(11, 21, 36, .72) 0%, transparent 38%);
}

.tile__state {
  position: absolute;
  z-index: 1;
  font-family: var(--body);
  font-size: 13px;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-weight: 700;
  line-height: 1.24;
}

/* Under the frame, on the page's own ground. */
.tile__foot {
  display: block;
  padding: 12px 2px 0;
}

.tile__title {
  font-family: var(--body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.35;
  color: var(--cream);
  transition: color .18s var(--easing);
}
a.tile:hover .tile__title { color: var(--rust-on-dark); }

/* PLAY on a film that exists; the honest state on one that does not. */
.tile__state {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  letter-spacing: .16em;
  opacity: .92;
  transition: opacity .22s;
}
a.tile:hover .tile__state,
a.tile:focus-visible .tile__state { opacity: 1; }
.tile--upcoming .tile__state { opacity: .78; font-weight: 500; }

@media (max-width: 720px) {
  .tile__state { font-size: 12px; }
  .tile__title { font-size: 12px; }
  .tile__foot { padding-top: 10px; }
}

.marquee__note {
  padding: 14px 30px 0;
  font-family: var(--body);
  font-size: 12px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
@media (max-width: 720px) { .marquee__note { padding: 12px 18px 0; } }

/* The page scrolls: two rails do not fit one screen, and pretending they
   do just cut the shorts off the bottom. It only needs room to breathe
   under the top row and after the last rail. */
.work-body > main { padding-block: clamp(1.5rem, 5vh, 3rem) var(--space-section); }

/* ── the player sheet ────────────────────────────────────────────── */

.player-sheet {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  transform: translateY(100%);
  transition: transform .38s cubic-bezier(.2, .7, .3, 1);
}
.player-sheet[hidden] { display: none; }
.player-sheet.is-open { transform: translateY(0); }

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

/* The bar sits over the film rather than taking height from it. Taking
   the height is what pushes the picture off full width: a 16:9 film in a
   stage shorter than 16:9 fits by height and leaves bars down each side. */
.player-sheet__bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 18px 24px;
  background: linear-gradient(to bottom,
              rgba(11, 21, 36, .82) 0%, rgba(11, 21, 36, 0) 100%);
}

.player-sheet__meta { min-width: 0; }

/* See player.js: YouTube names the film itself, in this same corner. */
.player-sheet--embed .player-sheet__meta { display: none; }

/* Over one of our own films the bar floats, because a bare <video> has
   nothing in its corners to clash with. An embed does: YouTube puts its
   title top left and its captions and settings top right, which is
   exactly where Share and Close were landing. So for an embed the bar
   stops overlaying and takes its own height, and the film sits under it.
   The film stays full width — it is the stage that gets shorter — which
   is the property the overlay was protecting in the first place. */
.player-sheet--embed .player-sheet__bar {
  position: relative;
  background: var(--ground);
  justify-content: flex-end;
  padding: 12px 20px;
}
/* The stage starts exactly where the bar ends, whatever the bar's height
   — the sheet is already a flex column, so taking the stage out of
   absolute positioning is enough. A hardcoded offset was three pixels
   short of the real bar and slid the film underneath it. */
.player-sheet--embed .player-sheet__stage {
  position: relative;
  inset: auto;
  flex: 1 1 auto;
  min-height: 0;
}

/* One of our own films fills the width and accepts a crop, because a
   bare <video> has nothing at its edges worth keeping. An embed does:
   YouTube's scrubber and controls live along its bottom edge, and its
   title along the top. So an embed fits inside the stage instead —
   letterboxed at the sides on a wide window rather than clipped top and
   bottom, which is what pushed the film under the bar. */
.player-sheet--embed .player-sheet__video {
  width: auto;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
}

@media (max-width: 720px) {
  .player-sheet--embed .player-sheet__bar { padding: 10px 14px; }
}

.player-sheet__title {
  display: block;
  font-family: var(--body);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cream);
}

.player-sheet__artist {
  display: block;
  margin-top: 3px;
  font-family: var(--body);
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.player-sheet__actions { display: flex; gap: 10px; flex: 0 0 auto; }

.player-sheet__btn {
  background: none;
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--cream);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: 9px 16px;
  cursor: pointer;
  transition: border-color .18s, background .18s;
  white-space: nowrap;
}
.player-sheet__btn:hover {
  border-color: var(--ink-faint);
  background: rgba(245, 239, 228, .06);
}

.player-sheet__stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #000;
}

/* Full width, always. The height follows from the ratio; a window shorter
   than 16:9 crops evenly rather than shrinking the picture off the edges. */
.player-sheet__video {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  max-width: none;
  border: 0;
  display: block;
  background: #000;
}

@media (max-width: 720px) {
  .player-sheet__video { height: 100%; aspect-ratio: auto; object-fit: cover; }
  .player-sheet__bar { padding: 14px 16px; gap: 12px; }
  .player-sheet__title { font-size: 13px; }
  .player-sheet__btn { padding: 8px 12px; letter-spacing: .1em; }
}

/* The page behind does not scroll while the sheet is up. */
.has-sheet, .has-sheet body { overflow: hidden; }

/* Shown only when nothing could copy the link for you. A share button
   that appears to do nothing is worse than one that hands you the URL. */
.player-sheet__link {
  width: min(320px, 42vw);
  background: rgba(245, 239, 228, .06);
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--cream);
  font: inherit;
  font-size: 12px;
  padding: 9px 12px;
}

/* ── a single piece, on its own page ─────────────────────────────── */

.piece {
  max-width: 60rem;
  margin-inline: auto;
  padding: var(--space-section) var(--gutter);
}
.piece__eyebrow { color: var(--rust-text); margin-bottom: 1rem; }
.piece__title { color: var(--ink-strong); }
.piece__client {
  font-family: var(--body);
  font-size: 0.875rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 0.5rem;
}
.piece__video {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  margin-top: var(--space-block);
}
.piece__pending {
  margin-top: var(--space-block);
  padding: 3rem var(--gutter);
  border: 1px solid var(--line);
  text-align: center;
  color: var(--ink-muted);
}
.piece__summary {
  margin-top: var(--space-block);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
  max-width: var(--measure);
}
.piece__back { margin-top: var(--space-block); }
.piece__back a {
  font-family: var(--body);
  font-size: 0.8125rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rust-text);
}

@media (max-width: 720px) {
}

/* An embedded film on a piece page sits in the same box as one of ours. */
iframe.piece__video { border: 0; }

/* The titles here are real headlines, not song names, so a tile has to
   hold three lines without pushing its own foot off the bottom. */
.tile__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── the shorts rail ─────────────────────────────────────────────── */
/* Same rail, upright tiles. A short is shot 9:16 and shown 9:16; the
   cover is the first frame, so nothing here carries lettering that a
   crop would cut. */

.rail__heading {
  padding: 0 30px 12px 24px;
  color: var(--rust-text);
}
@media (max-width: 720px) { .rail__heading { padding: 0 18px 10px; } }

#shorts { margin-top: clamp(2rem, 5vh, 3.5rem); }

.marquee--shorts { --tile: clamp(150px, 15vw, 220px); }

.tile__frame--short { aspect-ratio: 9 / 16; }

/* The state sits over a face as often as not, so it needs its own floor
   rather than relying on the frame gradient alone. */
.marquee--shorts .tile__state {
  padding: 5px 12px;
  background: rgba(11, 21, 36, .55);
  border-radius: 2px;
}

.marquee--shorts .tile__title {
  font-size: 12px;
  letter-spacing: .04em;
  -webkit-line-clamp: 2;
}

/* An upright film in an upright stage: full height, and as wide as that
   makes it. The 16:9 rule would letterbox it into a strip. */
.player-sheet--portrait .player-sheet__video {
  width: auto;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 9 / 16;
}

/* ---- editorial.css ---- */
/* The long-form editorial language — bands, display type set large,
   margin labels, full-bleed evidence, framed blocks, one big figure.

   Structure after the Adjective & Co Whalebone case study; palette and
   typefaces are ours. Three grounds alternate down the page so each
   movement is visibly a movement. */

.band {
  --band-ink: var(--ink);
  --band-strong: var(--ink-strong);
  --band-muted: var(--ink-muted);
  --band-accent: var(--rust-on-dark);
  --band-line: var(--line);
  padding-block: clamp(4rem, 12vh, 9rem);
  background: var(--ground);
  color: var(--band-ink);
}

/* The mid-tone. Used where a band should read as a surface rather than
   as the page itself. It is lighter than the page ground, so the accent
   has to lift with it — the standard on-dark rust measures 3.96 here. */
.band--navy {
  background: var(--navy);
  --band-accent: var(--rust-on-mid);
  --band-line: rgba(245, 239, 228, .2);
}

/* The one light ground. Everything on it needs its own values: the brand
   rust is 4.20:1 against cream, under what small text wants, so the deep
   variant carries text and links here. */
.band--cream {
  background: var(--cream);
  --band-ink: #3D3020;
  --band-strong: #1A1208;
  --band-muted: #6B5B45;
  --band-accent: var(--rust-on-cream);
  --band-line: rgba(26, 18, 8, .18);
}

/* The warm ground. Preserve the Hook is the one part of the site that is
   not for sale, and it is the only thing set on this — the change of
   ground is doing the work a heading would otherwise have to. */
.band--sand {
  background: var(--sand);
  --band-ink: #3D3020;
  --band-strong: #1A1208;
  --band-muted: #6B5B45;
  --band-accent: var(--rust-on-cream);
  --band-line: rgba(26, 18, 8, .18);
}

.band__in {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Out in the margin on a wide screen, above the line on a narrow one —
   the label names the movement without taking a line of its own. */
.band__label {
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--band-accent);
  margin-bottom: 1.5rem;
}

/* 100rem, not 1100px. The band is 78rem wide and centred, so at 1100px
   the margin either side is 30px and the label — 138px at its longest —
   hung most of itself off the left edge of the screen. The margin has to
   be able to hold the widest label before anything is put in it:
   78rem of content plus two margins of 138px + 8px of offset. Below
   that the label keeps the narrow treatment and sits above the line,
   which was always the fallback and reads perfectly well. */
@media (min-width: 100rem) {
  .band__in { position: relative; }
  .band__label {
    position: absolute;
    left: calc(var(--gutter) * -1 + 0.5rem);
    top: 0.6rem;
    transform: translateX(-100%);
    margin: 0;
    white-space: nowrap;
  }
}

/* Set to fill the band. The whole page rests on the difference between
   this and the sentence under it. */
.band__line {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2rem, 5.2vw, 4.25rem);
  line-height: 1.04;
  letter-spacing: -0.025em;
  color: var(--band-strong);
  max-width: 20ch;
  text-wrap: balance;
}

/* The opener, and nothing else on the page. */
.band__line--huge {
  font-size: clamp(2.75rem, 11vw, 9.5rem);
  line-height: 0.94;
  letter-spacing: -0.04em;
  max-width: 14ch;
}

.band__note {
  margin-top: clamp(1.5rem, 3vh, 2.5rem);
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--band-ink);
  max-width: 46ch;
}

.band--cream .btn { border-color: var(--band-accent); color: var(--band-accent); }
.band--cream .btn--primary { background: var(--band-accent); color: var(--cream); }
.band--cream .btn--primary:hover { background: transparent; color: var(--band-accent); }

/* ── evidence ────────────────────────────────────────────────────── */

.bleed-band { display: block; }
.bleed-band img {
  width: 100%;
  height: clamp(14rem, 42vh, 30rem);
  object-fit: cover;
  display: block;
}
.bleed-band--tall img { height: clamp(20rem, 72vh, 46rem); }

/* ── the figure ──────────────────────────────────────────────────── */

.band--figure { padding-block: clamp(3rem, 9vh, 7rem); }

/* Large enough to stop being a number and start being a picture of one.
   Tabular figures so it does not wobble if it ever animates. */
.figure__value {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(5rem, 22vw, 18rem);
  line-height: 0.82;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
  color: var(--band-strong);
}

.figure__caption {
  margin-top: clamp(1rem, 3vh, 2rem);
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--band-ink);
  max-width: 34ch;
}

/* ── framed blocks ───────────────────────────────────────────────── */

.band--framed { padding-bottom: clamp(3rem, 9vh, 7rem); }

.framed-set {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  margin-top: var(--space-block);
}

@media (min-width: 900px) {
  .framed-set { grid-template-columns: repeat(3, 1fr); }
}

/* The bordered document, with its own header bar. */
.framed {
  border: 1px solid var(--band-line);
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--navy) 28%, transparent);
}
.band--cream .framed { background: rgba(255, 255, 255, .38); }

.framed__bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--band-line);
}

.framed__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  color: var(--band-strong);
}

.framed__kind {
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--band-muted);
  white-space: nowrap;
}

.framed__body { padding: 1.25rem 1.1rem 1.4rem; flex: 1 1 auto; }

.framed__claim {
  font-family: var(--body);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--band-ink);
}

.framed__link {
  display: inline-block;
  margin-top: 1rem;
  font-family: var(--body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--band-accent);
}
.framed__link:hover { text-decoration: underline; text-underline-offset: 4px; }

.framed__media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

/* ── the masthead ────────────────────────────────────────────────── */
/* The title, filling the frame. Driven off the viewport rather than a
   fixed scale, because filling the width is the entire point of it. */

.band--masthead { padding-block: clamp(3rem, 14vh, 10rem) clamp(2.5rem, 8vh, 6rem); }

.masthead { display: block; }

.masthead__line {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(3rem, 15.5vw, 15rem);
  line-height: 0.86;
  letter-spacing: -0.045em;
  color: var(--band-strong);
  text-transform: uppercase;
}

.masthead__note {
  margin-top: clamp(1.5rem, 4vh, 2.75rem);
  font-family: var(--display);
  font-size: clamp(1.125rem, 2.2vw, 1.6rem);
  line-height: 1.3;
  color: var(--band-ink);
  max-width: 30ch;
}

.masthead__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2.5rem;
  margin-top: clamp(1.75rem, 5vh, 3rem);
  padding-top: 1.25rem;
  border-top: 1px solid var(--band-line);
}

.masthead__meta li { display: flex; flex-direction: column; gap: 0.3rem; }

.masthead__meta-k {
  font-family: var(--body);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--band-accent);
}

.masthead__meta-v {
  font-family: var(--body);
  font-size: 0.9375rem;
  color: var(--band-strong);
}

/* ── plates ──────────────────────────────────────────────────────── */
/* Deliberately unequal. A grid of identical thumbnails reads as a
   catalogue; varying the width and the side is what makes a sequence of
   photographs read as a magazine. */

.band--plates { padding-block: clamp(2rem, 6vh, 4rem); }

.plates {
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 5vh, 4.5rem);
}

.plate { display: block; }
.plate img { width: 100%; display: block; object-fit: cover; }

.plate--full  { width: 100%; }
.plate--wide  { width: min(100% - 2 * var(--gutter), 72rem); }
.plate--half  { width: min(100% - 2 * var(--gutter), 34rem); }
.plate--third { width: min(100% - 2 * var(--gutter), 22rem); }

.plate--centre { margin-inline: auto; }
.plate--left   { margin-right: auto; margin-left: var(--gutter); }
.plate--right  { margin-left: auto; margin-right: var(--gutter); }
.plate--full.plate--left,
.plate--full.plate--right { margin-inline: 0; }

.plate__caption {
  margin-top: 0.75rem;
  font-family: var(--body);
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--band-muted);
  max-width: 44ch;
}
.plate--full .plate__caption { padding-inline: var(--gutter); }

/* ── the run of issues ───────────────────────────────────────────── */

.band--issues { padding-bottom: clamp(3rem, 9vh, 7rem); }

.issue-set {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: var(--space-block);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.issue__cover img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  display: block;
  border: 1px solid var(--band-line);
}

.issue__body { padding-top: 1rem; }

.issue__no {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.issue__season { color: var(--band-accent); }

.issue__state {
  color: var(--band-muted);
  font-weight: 500;
  letter-spacing: .12em;
}

.issue__theme {
  margin-top: 0.6rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.375rem;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--band-strong);
}

.issue__tagline {
  margin-top: 0.5rem;
  font-family: var(--body);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--band-ink);
}

/* ── the footnote ────────────────────────────────────────────────── */
/* Small, and last. Any higher up the page and the lookbook turns back
   into a rate card. */

.band--footnote { padding-block: clamp(2.5rem, 7vh, 4.5rem); }

.footnote {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  border-top: 1px solid var(--band-line);
  padding-top: 1.75rem;
}

.footnote__mark {
  font-family: var(--display);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--band-accent);
  flex: 0 0 auto;
}

.footnote__text {
  font-family: var(--body);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--band-muted);
  max-width: 56ch;
}

.footnote__link {
  display: inline-block;
  margin-top: 0.6rem;
  font-family: var(--body);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--band-accent);
}
.footnote__link:hover { text-decoration: underline; text-underline-offset: 4px; }

/* ── an embedded film ────────────────────────────────────────────── */
/* The still stands in for the player until someone asks for it. See
   editorial.live() for why this is a facade rather than an iframe. */

.band--live { padding-block: clamp(2rem, 6vh, 4rem); }

.live {
  width: min(100% - 2 * var(--gutter), 72rem);
  margin-inline: auto;
}

.live__frame {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  background: #000;
  cursor: pointer;
  overflow: hidden;
}

.live__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .5s cubic-bezier(.2, .7, .3, 1),
              filter .3s var(--easing);
}
.live__frame:hover img { transform: scale(1.02); filter: brightness(1.06); }

/* The iframe replaces the button's contents on click. */
.live__frame iframe { width: 100%; height: 100%; border: 0; display: block; }

.live__badge {
  position: absolute;
  top: 1rem;
  left: 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.7rem;
  background: var(--rust-fill);
  color: var(--cream);
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  border-radius: 2px;
}
.live__badge::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--cream);
  animation: live-pulse 2.4s ease-in-out infinite;
}
@keyframes live-pulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }
@media (prefers-reduced-motion: reduce) {
  .live__badge::before { animation: none; }
}

.live__cue {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 1.5rem;
  /* .62 leaves the cream at exactly 4.50:1 against a white frame — on
     the line, and the line is not a place to sit when the background is
     a photograph nobody has seen yet. .72 gives 6.42 in that worst
     case. */
  background: rgba(11, 21, 36, .72);
  border: 1px solid rgba(245, 239, 228, .5);
  border-radius: 2px;
  color: var(--cream);
  font-family: var(--body);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  transition: background .2s var(--easing);
}
.live__frame:hover .live__cue { background: rgba(11, 21, 36, .86); }

.live__triangle {
  width: 0;
  height: 0;
  border-left: 10px solid currentColor;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
}

.live__caption {
  margin-top: 0.85rem;
  font-family: var(--body);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--band-muted);
  max-width: 52ch;
}

/* ── a rail that travels sideways while the page holds still ─────── */
/* Base state is a plain horizontal scroller. That is what this is with
   no JavaScript, on a phone, and for anyone who has asked for less
   motion — swipeable, complete, nothing missing. The script adds the
   pinning on top, because pinning is the only part that needs a
   measurement. */

.band--hscroll { padding-bottom: clamp(2rem, 6vh, 4rem); }

.hrail__pin {
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.hrail__pin::-webkit-scrollbar { display: none; }

.hrail {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2.5rem);
  width: max-content;
  padding-inline: var(--gutter);
}

.hrail__item { flex: 0 0 auto; }

.hrail__item img {
  display: block;
  height: clamp(16rem, 46vh, 34rem);
  width: auto;
  max-width: none;
}
.hrail__item--wide img { height: clamp(12rem, 34vh, 26rem); }

.hrail__caption {
  margin-top: 0.75rem;
  font-family: var(--body);
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--band-muted);
  max-width: 28ch;
}

/* Pinned: the section becomes tall, the frame sticks to the top of the
   viewport, and the row is moved across by the script. The height is set
   in JS because it depends on how far the row actually overflows. */
.band--hscroll.is-pinned { padding-bottom: 0; }

.is-pinned .hrail__pin {
  position: sticky;
  top: 0;
  height: 100dvh;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.is-pinned .hrail { will-change: transform; }

/* Nothing here is pinned on a phone: one screen of height is not enough
   to give the row anywhere to travel, and hijacking a swipe is worse
   than letting people swipe. */
@media (max-width: 899px) {
  .band--hscroll.is-pinned { height: auto !important; padding-bottom: clamp(2rem, 6vh, 4rem); }
  .is-pinned .hrail__pin {
    position: static;
    height: auto;
    overflow-x: auto;
  }
  .is-pinned .hrail { transform: none !important; }
}

/* ── the enquiry, one question at a time ─────────────────────────── */
/* Base state is an ordinary stacked form with one submit at the bottom.
   That is what it is with no JavaScript, and it works. The step buttons
   are hidden here rather than absent, so the script only has to reveal
   them. */

.quiz { margin-top: var(--space-block); max-width: 44rem; }

.quiz__steps { display: grid; gap: var(--space-section); }

.quiz__count {
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--band-muted);
  margin-bottom: 1rem;
}
.quiz__count span { color: var(--band-accent); }

.quiz__q {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.5rem, 3.4vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--band-strong);
  max-width: 20ch;
  text-wrap: balance;
}
.quiz__q label { cursor: pointer; }

.quiz__hint {
  margin-top: 0.75rem;
  font-family: var(--body);
  font-size: 0.9375rem;
  color: var(--band-muted);
  max-width: 44ch;
}

.quiz__input {
  width: 100%;
  margin-top: 1.5rem;
  padding: 0.9rem 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--band-line);
  border-radius: 0;
  color: var(--band-strong);
  font-family: var(--display);
  font-size: clamp(1.125rem, 2vw, 1.5rem);
  line-height: 1.4;
}
.quiz__input:focus {
  outline: none;
  border-bottom-color: var(--band-accent);
}
textarea.quiz__input { resize: vertical; }

/* Chips, not a stack of radio rows: the options read as a set you pick
   from rather than a list you work down. */
.quiz__choices {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.75rem;
}

/* The whole chip is the target, not a 16px circle. The native control
   is hidden but present, so this stays a real radio group for the
   keyboard and for assistive technology. */
.quiz__choice {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--band-line);
  border-radius: 2px;
  cursor: pointer;
  font-family: var(--body);
  font-size: 0.9375rem;
  color: var(--band-ink);
  transition: border-color var(--motion-fast) var(--easing),
              background var(--motion-fast) var(--easing),
              color var(--motion-fast) var(--easing);
}
.quiz__choice input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.quiz__key {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--band-muted);
  border: 1px solid var(--band-line);
  border-radius: 2px;
  padding: 0.1rem 0.32rem;
}
.quiz__choice:hover { border-color: var(--band-accent); }
/* The native control is invisible, so its focus has to show on the chip. */
.quiz__choice:has(input:focus-visible) {
  outline: 2px solid var(--rust);
  outline-offset: 2px;
}
.quiz__choice:has(input:checked) {
  border-color: var(--band-accent);
  background: var(--band-accent);
  color: var(--cream);
}
.quiz__choice:has(input:checked) .quiz__key {
  color: var(--cream);
  border-color: rgba(245, 239, 228, .5);
}

/* What is wrong, said in place. The browser's own bubble points at a
   control the visitor can see but disappears the moment they look away;
   this stays until it is fixed. */
.quiz__error {
  margin-top: 1rem;
  min-height: 1.25rem;
  font-family: var(--body);
  font-size: 0.875rem;
  color: var(--band-accent);
}
.quiz__error:empty { min-height: 0; }

.quiz__nav { display: none; gap: 0.75rem; margin-top: 2rem; }

/* How far in you are, along the bottom edge. Only meaningful once the
   steps exist, so it is hidden until the script takes over. */
.quiz__bar { display: none; }
.quiz.is-quiz .quiz__bar {
  display: block;
  height: 2px;
  margin-top: 2.5rem;
  background: var(--band-line);
}
.quiz__bar span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--band-accent);
  transition: width var(--motion-med) var(--easing);
}
@media (prefers-reduced-motion: reduce) {
  .quiz__bar span { transition: none; }
}

.quiz__back {
  background: none;
  border: 1px solid var(--band-line);
  border-radius: 2px;
  color: var(--band-muted);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 0.85rem 1.25rem;
  cursor: pointer;
}
.quiz__back:hover { color: var(--band-strong); border-color: var(--band-accent); }

.quiz__send { margin-top: 2rem; }

/* Stepped. One question in view, its own navigation under it, and the
   submit only on the last one. */
.quiz.is-quiz .quiz__steps { gap: 0; }
.quiz.is-quiz .quiz__step { display: none; }
.quiz.is-quiz .quiz__step.is-current { display: block; }
.quiz.is-quiz .quiz__nav { display: flex; }
.quiz.is-quiz .quiz__send { display: none; }
.quiz.is-quiz .quiz__step.is-last .quiz__send-slot { display: block; }
.quiz.is-quiz.is-last .quiz__send { display: inline-flex; }
.quiz.is-quiz.is-first .quiz__back { display: none; }
.quiz.is-quiz.is-last .quiz__nav [data-quiz-next] { display: none; }

/* A step that has just come into view. Never on first paint — the page
   should not animate itself at someone before they have done anything. */
.quiz.is-quiz .quiz__step.is-current.is-entering {
  animation: quiz-in var(--motion-fast) var(--easing) both;
}
@keyframes quiz-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .quiz.is-quiz .quiz__step.is-current.is-entering { animation: none; }
}

/* ---- split.css ---- */
/* The split band and the centred closing band.
 *
 * Lifted out of editorial.css, which had reached the size where
 * finding anything in it is the problem.
 */
/* ── the split: copy one side, a photograph the other ─────────────── */

.split { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; }

@media (min-width: 860px) {
  .split { grid-template-columns: 1fr 1fr; }
}

/* The rust rule marks the one section that is not selling anything. */
.split--accent {
  border-left: 3px solid var(--band-accent);
  padding-left: clamp(1.25rem, 3vw, 2.25rem);
}

.split__h {
  font-family: var(--display);
  font-size: clamp(1.4rem, 2.6vw, 1.6rem);
  line-height: 1.2;
  color: var(--band-strong);
  margin: 0 0 0.9rem;
}

.split__copy p {
  font-size: 1rem;
  line-height: 1.7;
  max-width: 56ch;
  color: var(--band-ink);
  margin: 0 0 1rem;
}

.split__link {
  display: inline-block;
  margin-top: 0.4rem;
  color: var(--band-accent);
  font-weight: 500;
}

.split__link:hover { text-decoration: underline; text-underline-offset: 4px; }

.split__shot img { display: block; width: 100%; height: auto; }

/* ── a centred closing band ───────────────────────────────────────── */

.band--centred .band__in { text-align: center; }
.band--centred .band__line { margin-inline: auto; max-width: 22ch; }
.band--centred .band__note { margin-inline: auto; }
.band--centred .actions { justify-content: center; }

.band__second { margin: 1.1rem 0 0; }

.band__second a {
  font-size: 0.875rem;
  color: var(--band-muted);
}

.band__second a:hover { color: var(--band-strong); }

/* ---- about.css ---- */
/* The About page.
 *
 * No new components: the portrait is a panel, the origin is prose, the
 * list is the item list, the close is a statement band. What is here is
 * the handful of variants those need to match the spec — a cinema crop,
 * a stacked row, a name grid — plus the placeholder that stands in for
 * a fact nobody but Eric has.
 */

/* ── the type scale this page asks for ────────────────────────────── */

/* Smaller than the display sizes the rest of the site uses. This page is
   read rather than glanced at: the portrait names two people, it does not
   make an argument, and the closing line is a sentence, not a headline. */
.about .panel--cinema .panel__line { font-size: clamp(2rem, 5vw, 3.4rem); }
.about .prose__heading { font-size: clamp(1.6rem, 3.5vw, 2.2rem); }
.about .prose p { font-size: 1.0625rem; line-height: 1.7; max-width: 60ch; }
.about .band--centred .band__line { font-size: clamp(1.8rem, 4vw, 2.6rem); }
.about .split__h { font-size: 1.5rem; }

/* ── the portrait ─────────────────────────────────────────────────── */

/* 21:9 on a desktop, 4:5 on a phone. A face at the edge of a 21:9 frame
   is the first thing a narrow screen would crop off, so the aspect
   changes rather than the picture being squeezed into it. */
.panel--cinema { min-height: 0; aspect-ratio: 21 / 9; }

/* The switch is at 900px, not at the phone breakpoint. A 21:9 frame on a
   768px tablet is a 329px strip, and object-fit would take the crop out
   of the top and bottom of the frame — which is where the faces are.
   Two crops as specified; the tablet gets the one that keeps them. */
@media (max-width: 900px) {
  .panel--cinema { aspect-ratio: 4 / 5; }
}

/* The place, under the names: not a heading, not body, a caption. */
.panel--cinema .panel__note {
  font-size: 0.9375rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-top: 0.6rem;
}

/* ── the list of what has been built ──────────────────────────────── */

/* The name over the sentence rather than beside it. The two-column row
   is right for capabilities, where the name is a label; here the name is
   the thing and the sentence is what it is. */
.item-list--stack .item { grid-template-columns: 1fr; }

.item-list--stack .item__name {
  font-family: var(--display);
  font-size: 1.15rem;
}

.item-list--stack .item__line {
  font-size: 0.9375rem;
  opacity: 0.7;
  max-width: 60ch;
}

/* ── the collaborators ────────────────────────────────────────────── */

.item-list--grid {
  display: grid;
  gap: 1.5rem 2rem;
  border-top: 0;
}

.item-list--grid .item {
  grid-template-columns: 1fr;
  border-bottom: 0;
  padding: 0;
}

.item-list--grid .item__name {
  font-family: var(--display);
  font-size: 1.05rem;
}

.item-list--grid .item__line { font-size: 0.875rem; opacity: 0.65; }

@media (min-width: 620px) { .item-list--grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 940px) { .item-list--grid { grid-template-columns: repeat(3, 1fr); } }

/* ── a fact that is still missing ─────────────────────────────────── */

/* Deliberately ugly. This is scaffolding, and scaffolding that looks
   like the finished building is how it ends up left in place. The
   production build refuses to run while one of these is standing.
   Contrast is still AA — it has to be readable to be useful. */
.needs {
  border: 2px dashed #8A6D1F;
  background: #2A2410;
  padding: clamp(1.25rem, 3vw, 2rem);
  margin-block: var(--space-block);
}

.needs__flag {
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #E8C766;
  margin: 0 0 0.6rem;
}

.needs__what {
  font-family: var(--body);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--cream);
  margin: 0;
}

.needs__detail {
  font-size: 0.9375rem;
  line-height: 1.65;
  color: rgba(245, 239, 228, 0.78);
  max-width: 62ch;
  margin: 0.7rem 0 0;
}

/* ---- drawer.css ---- */
/* The side panel.
 *
 * Two states, and the plain one is the default. Unenhanced, .drawer is a
 * band like any other and sits in the flow at the foot of the page — the
 * jump link reaches it and the form works. Everything below .is-enhanced
 * is what the script turns on once it can also close the thing again.
 */

.drawer {
  display: block;
  background: var(--ground);
  color: var(--cream);
  padding: var(--space-section) 0;
}

.drawer__panel {
  width: min(46rem, 92vw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.drawer__h {
  font-family: var(--display);
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  line-height: 1.05;
  margin: 0 0 0.6em;
}

.drawer__intro,
.drawer__note {
  color: var(--ink-muted);
  max-width: 42ch;
  margin: 0 0 1.6rem;
}

.drawer__note {
  margin: 1.6rem 0 0;
  font-size: 0.85rem;
}

/* Options ------------------------------------------------------------ */

.drawer__group {
  border: 0;
  margin: 0 0 2rem;
  padding: 0;
}

.drawer__group legend {
  padding: 0;
  margin-bottom: 0.9rem;
  color: var(--rust-on-dark);
}

.tier {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 0.85rem;
  align-items: start;
  padding: 1rem 1.1rem;
  margin-bottom: 0.6rem;
  border: 1px solid var(--line);
  cursor: pointer;
}

.tier:hover { border-color: rgba(245, 239, 228, 0.4); }

/* The radio stays a real radio — it is the thing being operated, and a
   custom control here would buy nothing but a worse focus ring. */
.tier input {
  grid-row: 1 / span 4;
  margin: 0.25rem 0 0;
  accent-color: var(--rust-fill);
  width: 1.05rem;
  height: 1.05rem;
}

.tier:focus-within {
  border-color: var(--rust-on-dark);
  outline: 2px solid var(--rust-on-dark);
  outline-offset: 2px;
}

.tier:has(input:checked) {
  border-color: var(--rust-on-dark);
  background: rgba(217, 106, 69, 0.09);
}

.tier__name {
  font-weight: 600;
  letter-spacing: 0.01em;
}

.tier__price {
  grid-column: 2;
  color: var(--rust-on-dark);
  font-size: 0.9rem;
  margin-top: 0.15rem;
}

.tier__summary {
  grid-column: 2;
  color: var(--ink-muted);
  font-size: 0.9rem;
  margin-top: 0.2rem;
}

.tier__list {
  grid-column: 2;
  margin: 0.6rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.tier__list li {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: 0.28rem;
}

.tier__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.45rem;
  height: 1px;
  background: var(--rust-on-dark);
}

.tier--plain { padding-bottom: 1rem; }

/* Fields -------------------------------------------------------------- */

.drawer__field {
  margin: 0 0 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.drawer__field label {
  font-size: 0.78rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--cream);
}

.drawer__req { color: var(--rust-on-dark); }

.drawer__opt {
  color: var(--ink-muted);
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: 0.75rem;
}

.drawer__hint {
  font-size: 0.82rem;
  color: var(--ink-muted);
  text-transform: none;
  letter-spacing: 0;
}

.drawer__input {
  font: inherit;
  color: var(--cream);
  background: rgba(245, 239, 228, 0.06);
  border: 1px solid var(--line);
  padding: 0.7rem 0.8rem;
  width: 100%;
  border-radius: 0;
}

.drawer__input:focus-visible {
  outline: 2px solid var(--rust-on-dark);
  outline-offset: 1px;
  border-color: transparent;
}

.drawer__send { margin-top: 0.6rem; }

/* Enhanced ------------------------------------------------------------ */

.drawer.is-enhanced {
  position: fixed;
  inset: 0;
  z-index: 90;
  padding: 0;
  background: none;
  visibility: hidden;
  pointer-events: none;
}

.drawer.is-enhanced .drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(11, 21, 36, 0.66);
  opacity: 0;
}

.drawer.is-enhanced .drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(34rem, 100vw);
  margin: 0;
  padding: 4.4rem var(--gutter) var(--space-section);
  background: var(--ground);
  border-left: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateX(100%);
}

/* Transitions arrive a frame after the panel has been put away, never in
 * the same class change. Animating *into* the closed state means the
 * first thing the panel does is travel off-canvas while it is invisible,
 * and a transition on a hidden element is not guaranteed to advance —
 * it stalls at the start, leaving the panel sitting open and unseen.
 */
.drawer.is-ready .drawer__scrim { transition: opacity 260ms ease; }

.drawer.is-ready .drawer__panel {
  transition: transform 300ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.drawer.is-enhanced.is-open {
  visibility: visible;
  pointer-events: auto;
}

.drawer.is-enhanced.is-open .drawer__scrim { opacity: 1; }
.drawer.is-enhanced.is-open .drawer__panel { transform: translateX(0); }

.drawer__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 2;
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--cream);
  background: none;
  border: 1px solid var(--line);
  cursor: pointer;
}

.drawer__close:hover { border-color: var(--cream); }

.drawer__close:focus-visible {
  outline: 2px solid var(--rust-on-dark);
  outline-offset: 2px;
}

@media (max-width: 40rem) {
  .drawer.is-enhanced .drawer__panel {
    width: 100vw;
    border-left: 0;
  }
}

/* Sliding is decoration. Appearing is the function. */
@media (prefers-reduced-motion: reduce) {
  .drawer.is-ready .drawer__scrim,
  .drawer.is-ready .drawer__panel { transition: none; }
}
