/* ==========================================================================
   V2B Montana — v2bmontana.com
   Section 02: Hero

   Sections not yet designed: navigation, Why V2B, What Happens, agenda,
   supporter wall, Who It's For, FAQ, closing CTA, footer.

   ---------------------------------------------------------------------------
   ON THE SCRIM

   Every opacity in this file is derived from a luminance sample of the two
   hero JPEGs, not chosen by eye. The photograph is near-monochrome and splits
   into two very different grounds:

     - the still water, left ~33% of the landscape crop, sits at sRGB 133-170
       and clears 4.5:1 against off-white type at alpha 0.14-0.44
     - the frosted forest, everything right of it, peaks at sRGB 211-254 and
       needs alpha 0.48-0.63 for the same result

   That gap drives both layouts. At 1280px and up the photograph is full bleed
   and type is set over it, so the scrim is shaped to that split: low across
   the water, peaking over the treeline where the headline's tail crosses the
   brightest frost, gone by 70%. Below 1280px the portrait crop interleaves
   water and forest down every horizontal band — no cheap zone anywhere — so
   type moves off the photograph entirely onto solid ink and the only gradient
   left is the dissolve between them.

   The JPEGs are never edited; everything is composited in CSS, so the masters
   stay reusable and re-croppable.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Ground */
  --ink:        #0a1522;   /* dark ground */
  --ink-soft:   #0a1522;   /* text on the gold fill — 5.22:1 */
  --scrim:      10, 21, 34;

  /* Hero field. The flyer's navy. The hero gradient runs from solid navy on
     the left to FULLY TRANSPARENT on the right, so the half of the frame that
     carries no type keeps the photograph untouched. Always fade to
     rgba(var(--navy-rgb), 0) and never to `transparent`, which is
     rgba(0,0,0,0) and interpolates through black — a grey bruise mid-ramp. */
  --navy:       #011c4a;
  --navy-rgb:   1, 28, 74;

  /* Light */
  --paper:      #ffffff;
  --paper-hi:   #ffffff;   /* display type on dark */
  --paper-lede: #E1E4E9;   /* body over photograph — solid, never translucent */
  --stone:      #C9CFDA;   /* eyebrow and hairlines over photograph */

  /* Accent. Gold is 5.22:1 on the dark ground, so it carries normal text
     there — but only 3.52:1 on white, which is large-text and border duty
     ONLY. See the audit in the commit message before putting it on paper.
     --gold-lift exists so hover and focus still register: at a single gold
     the primary CTA's hover fill equalled its resting fill. */
  --gold:       #a28455;
  --gold-lift:  #ba9969;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text",
          "Segoe UI Variable Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Motion. Slower out than in: hover settles, press answers. */
  --ease:     cubic-bezier(0.2, 0.7, 0.3, 1);
  --ease-out: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in:  cubic-bezier(0.4, 0, 1, 1);

  /* Nav height, derived from the same tokens the header is built from so it
     cannot drift. max() because different things set the height at different
     widths: the logo above 900px (48px), the RSVP button at 375 (42px). Add
     the two paddings and the 1px bottom rule. Measures 85 at 1440, 67 at 375
     — the header itself measures 83 and 67. */
  --nav-h: calc(max(clamp(2.4rem, 3.2vw, 3rem), 42px)
                + 2 * clamp(0.75rem, 1.6vw, 1.125rem) + 1px);
  /* Where an anchored element's top should land: below the nav, plus air. */
  --anchor-offset: calc(var(--nav-h) + clamp(1rem, 2.5vw, 1.75rem));

  --gutter: clamp(1.25rem, 5vw, 6rem);
  --media-h: clamp(18rem, 46svh, 28rem);   /* photo band below 1280px */
}

/* --------------------------------------------------------------------------
   Reset — minimal
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: var(--anchor-offset);
}

/* EVERY anchor target, not just sections — :where() keeps specificity at zero
   so it never fights anything, and it covers ids added later without needing a
   new rule. A flat 5rem was 80px against an 83px desktop nav, so section tops
   landed underneath it. */
:where([id]) { scroll-margin-top: var(--anchor-offset); }
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, p, dl, dd { margin: 0; }

/* Any author `display` beats the UA sheet's [hidden] { display: none }, so an
   element with display:grid stays visible when JS sets .hidden = true. That is
   what broke search filtering on /admin: rows were marked hidden and kept
   rendering. This is the reset; do not patch it per-component. */
[hidden] { display: none !important; }
img { display: block; max-width: 100%; }

/* --------------------------------------------------------------------------
   Focus
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--gold-lift);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Visually hidden but announced — carries "(opens in a new tab)". */
/* Keeps a phrase intact so a line can only break between phrases. Used on the
   closing headline: "Veteran-owned." and "Community-supported." contain hyphens,
   and a hyphen is a break opportunity, so without this the line breaks inside a
   word rather than at a full stop. */
.u-nowrap { white-space: nowrap; }

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--paper);
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* --------------------------------------------------------------------------
   Navigation

   Sticky, and transparent while the hero is on screen so the photograph runs
   under it; solid once a sentinel just below the header scrolls out of view.
   nav.js toggles .is-stuck via IntersectionObserver — no scroll handler.

   Below 900px the link list collapses behind a disclosure button. It ships
   EXPANDED (aria-expanded="true"); nav.js collapses it on load. With JS off
   the links stay visible and usable rather than hiding behind a dead button.
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2rem);
  padding: clamp(0.75rem, 1.6vw, 1.125rem) var(--gutter);
  background: transparent;
  transition: background-color 200ms var(--ease-out),
              border-color 200ms var(--ease-out);
  border-bottom: 1px solid transparent;
}
/* Solid once past the hero's top. Also the state the header is in at every
   width below 1280, where the hero is a photo band rather than a full bleed. */
.site-header.is-stuck {
  background: rgba(10, 21, 34, 0.94);
  border-bottom-color: rgba(255, 255, 255, 0.12);
  backdrop-filter: saturate(140%) blur(10px);
}
@media (max-width: 1279px) {
  .site-header { background: var(--ink); }
}

.header-sentinel { height: 1px; margin-top: -1px; }

/* inline-flex + flex:none, not display:block. As a block-level flex item the
   link was taking the full line (335px at 375) instead of shrink-wrapping the
   logo, which pushed the toggle and CTA onto a second row and made the header
   105px tall. */
.site-header__home {
  display: inline-flex;
  flex: none;
  line-height: 0;
  margin-right: auto;
}

/* Logo.
   The masters are exported on a 1920x960 (2:1) artboard, but the artwork only
   fills the middle of it — measured at 82.5% of the width and 54.5% of the
   height, so 8.8% transparent padding left and right and 22.8% top and bottom.
   Sized naively the element is nearly twice as tall as the ink.

   So the box is set to the artwork's true 660:218 ratio and the SVG is covered
   into it, cropping the dead space away. Without this the img falls back to its
   width="1920" attribute and takes the whole header line.

   If the masters are ever re-exported tightly cropped, drop the aspect-ratio
   and object-fit — with no padding to remove, cover would eat the artwork. */
.logo {
  display: block;
  width: auto;
  aspect-ratio: 660 / 218;
  object-fit: cover;
}
/* On the wrapping mobile row the auto margin absorbs every spare pixel, so the
   logo's margin box fills the line and shoves the toggle and CTA onto a second
   row — a 105px header instead of 62px. Push from the right instead. */
@media (max-width: 899px) {
  .site-header__home { margin-right: 0; }
  .nav__toggle { margin-left: auto; }
}
.site-header .logo { height: clamp(2.4rem, 3.2vw, 3rem); }

.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 2rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav__link {
  display: inline-block;
  padding-block: 0.4375rem;
  color: var(--paper-lede);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  transition: color 160ms var(--ease-out);
}
.nav__link:hover { color: var(--paper-hi); }
.nav__link[aria-current="true"] { color: var(--paper-hi); }

.nav__toggle { display: none; }

/* ---- below 900px: disclosure ---- */
@media (max-width: 899px) {
  .site-header { flex-wrap: wrap; row-gap: 0; }

  .nav__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    order: 2;
    min-height: 42px;
    padding: 0.5rem 0.75rem;
    border: 0;
    background: none;
    color: var(--paper);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
  }
  .nav__toggle-bars {
    display: block;
    width: 18px;
    height: 10px;
    position: relative;
  }
  .nav__toggle-bars i {
    position: absolute;
    left: 0;
    width: 100%;
    height: 1.5px;
    background: currentColor;
    transition: transform 200ms var(--ease), opacity 160ms var(--ease-out);
  }
  .nav__toggle-bars i:first-child { top: 0; }
  .nav__toggle-bars i:last-child  { bottom: 0; }
  .nav__toggle[aria-expanded="true"] .nav__toggle-bars i:first-child {
    transform: translateY(4.25px) rotate(45deg);
  }
  .nav__toggle[aria-expanded="true"] .nav__toggle-bars i:last-child {
    transform: translateY(-4.25px) rotate(-45deg);
  }

  .nav__cta { order: 3; margin-left: 0.25rem; }

  .nav {
    order: 4;
    flex-basis: 100%;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    border-top: 1px solid rgba(255, 255, 255, 0.12);
  }
  .nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-block: 0.5rem 0.75rem;
  }
  .nav__link {
    padding-block: 0.8125rem;
    font-size: 1rem;
  }
  .nav__list li + li .nav__link { border-top: 1px solid rgba(255, 255, 255, 0.08); }
}

@media (max-width: 380px) {
  /* At 375 the logo, MENU and RSVP together are tight. The word goes; the
     bars keep an accessible name from .nav__toggle-text being visually hidden. */
  .nav__toggle-text {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .nav__toggle { padding-inline: 0.5rem; }
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0.875rem 1.75rem;
  border: 0;
  border-radius: 0;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;   /* keeps "RSVP — it's free" on one line */
  cursor: pointer;
}

/* The RSVP control — squared, with one 45-degree corner clipped bottom-right.

   RESERVED FOR THIS CONTROL. The chamfer lives on .btn--cta, not .btn, so it
   cannot spread by accident. One CTA shape is a detail; the same cut repeated
   on cards, the supporter wall or section dividers becomes a tactical motif,
   which the design brief rules out.

   Drawn by two clipped pseudo-elements rather than clip-path on the element,
   because clipping the element clips its focus ring too. This way the ring
   stays intact and the rim follows the chamfer, so the shape carries its own
   WCAG 1.4.11 boundary. It also means shadows must be cast with drop-shadow on
   the rim — box-shadow on a clip-path element is clipped away entirely. */
.btn--cta {
  --cut: 14px;
  position: relative;
  isolation: isolate;
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding-inline: 1.875rem;
}
.btn--cta::before,
.btn--cta::after {
  content: "";
  position: absolute;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)),
                     calc(100% - var(--cut)) 100%, 0 100%);
}
.btn--cta::before { inset: 0;   z-index: -2; }   /* rim    */
.btn--cta::after  { inset: 1px; z-index: -1; }   /* face   */

/* Primary — filled bronze, treated as a machined key.

   Gradient runs 135deg, along the chamfer's own angle, so the implied light
   source and the cut describe the same object. Both overlays lighten from the
   base, never darken below it, so the darkest point of the fill is always the
   base gold and the label cannot drop under its 5.22:1 in any state.

   background-image is not interpolatable — a gradient swap on hover would snap
   — so the overlays are fixed and background-color animates beneath them.

   It carries a contact shadow at rest: this is the page's one job, and on a
   phone, where hover never fires, the rest state is the only state most
   visitors will ever see. */
.btn--primary {
  color: var(--ink-soft);                        /* 5.22:1 on gold */
  transition: transform 200ms var(--ease);
}
.btn--primary::before {
  background: rgba(var(--scrim), 0.55);
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.40))
          drop-shadow(0 2px 3px rgba(0, 0, 0, 0.22));
  transition: filter 200ms var(--ease);
}
.btn--primary::after {
  background-color: var(--gold);
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.20) 0 1px, rgba(255, 255, 255, 0) 1px),
    linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 62%);
  transition: background-color 200ms var(--ease);
}
.btn--primary:hover { transform: translateY(-1px); }
.btn--primary:hover::after { background-color: var(--gold-lift); }
.btn--primary:hover::before {
  filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.45))
          drop-shadow(0 5px 10px rgba(0, 0, 0, 0.32));
}
.btn--primary:active {
  transform: translateY(1px);
  transition: transform 90ms var(--ease-in);
}
.btn--primary:active::before {
  filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5));
  transition: filter 90ms var(--ease-in);
}

/* Outline — the same action at nav weight, so two bronze fills stop competing.

   The interior is opaque ink, and it has to be. Two reasons, both measured:

   1. The face pseudo sits on top of the rim pseudo, so anything translucent
      composites over gold rather than over the page. At 55% it came out
      rgb(91,74,47) — muddy gold at 3.09:1, worse than no fill at all.
   2. A genuinely transparent interior fails anyway at 1280px and up, where the
      bar is transparent over the photograph and this button sits top-right.
      Only the header ramp reaches that far, so the frost behind composites to
      roughly sRGB 72 and gold text on it lands at 3.50:1, under the 4.5:1
      it needs.

   Opaque ink solves both and costs nothing visually: below 1280 it is the same
   colour as the bar, so the button reads as pure gold rule and gold label;
   above it, it reads as a dark plate with a gold edge. Either way the label
   has a fixed 5.22:1 ground that does not depend on the photograph. */
.btn--outline {
  color: var(--gold);                          /* 5.22:1 on --ink */
  transition: color 160ms var(--ease-out);
}
.btn--outline::before { background: var(--gold); }
.btn--outline::after {
  background-color: var(--ink);
  transition: background-color 160ms var(--ease-out);
}
.btn--outline:hover { color: var(--ink-soft); }
.btn--outline:hover::before { background: var(--gold-lift); }
.btn--outline:hover::after { background-color: var(--gold-lift); }

/* Same control, smaller: the cut scales with it so the angle stays 45 degrees
   and both RSVP buttons read as one shape. */
.btn--sm {
  --cut: 10px;
  min-height: 42px;
  padding: 0.5rem 1.125rem;
  font-size: 0.8125rem;
}

/* --------------------------------------------------------------------------
   HERO — base: photo band on top, type on solid ink below.

   This is the layout everything under 1280px gets, phones included. Most of
   this traffic is a text message opened on a phone, so the phone case is
   written first and the wide case is the override.

   Why a split rather than type over the photo: in the portrait crop the water
   and forest interleave down the entire frame, so every horizontal band
   demands alpha 0.50-0.61. Type over that photograph means burying it under
   58% black at full height. The band keeps the photograph clean where it is
   clean, dissolves it into the type ground, and gives the type a perfect
   surface — 17:1 instead of a fight for 4.5:1.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  isolation: isolate;
  background: var(--ink);
}

.hero__media {
  display: block;
  position: relative;
  height: var(--media-h);
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 45%;  /* ridgeline and its reflection, both in frame */
}

.hero::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--media-h);
  z-index: 1;
  pointer-events: none;
  /* One gradient only: the dissolve into the type ground, so the split doesn't
     read as two stacked boxes. No header ramp needed — the bar above is solid,
     which leaves the top 38% of the strip as untouched photograph. */
  background: linear-gradient(180deg,
    rgba(var(--scrim), 0) 38%,
    rgba(var(--scrim), 0.50) 72%,
    rgba(var(--scrim), 0.90) 92%,
    var(--ink) 100%);
}

.hero__inner {
  position: relative;
  z-index: 2;
  padding: clamp(2rem, 6vw, 3rem) var(--gutter) 0;
}

/* --------------------------------------------------------------------------
   Hero — type
   -------------------------------------------------------------------------- */

.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-bottom: clamp(1.125rem, 2.4vw, 1.625rem);
  color: var(--stone);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.eyebrow__rule {
  display: block;
  width: clamp(2rem, 5vw, 3.5rem);
  height: 1px;
  background: currentColor;
  opacity: 0.55;
}

.hero__title {
  color: var(--paper-hi);
  /* Sized so "Stronger Communities." — the widest line at 10.72em — holds one
     line from 375px up, with ~5% headroom for fallback-font metrics. */
  font-size: clamp(1.8125rem, 7.2vw, 3.25rem);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.028em;
  text-wrap: pretty;
}
.hero__title-line { display: block; }

.hero__lede {
  max-width: 54ch;
  margin-top: clamp(1.125rem, 2.6vw, 1.625rem);
  color: var(--paper-lede);
  font-size: clamp(1.0625rem, 0.5vw + 0.95rem, 1.1875rem);
  line-height: 1.55;
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2rem);
  margin-top: clamp(1.75rem, 3.4vw, 2.5rem);
}

.hero__jump {
  /* Light caps to match the primary, so the pair reads as one system rather
     than a tracked button next to an untreated link. Inline-block padding
     brings the tap target to ~28px — at 13px the bare line box is only ~20px,
     under the 24px WCAG 2.5.8 asks for. */
  display: inline-block;
  padding-block: 0.35rem;
  color: var(--paper-hi);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
  text-decoration-color: rgba(251, 250, 248, 0.45);
  transition: text-decoration-color 150ms ease;
}
.hero__jump:hover { text-decoration-color: var(--paper-hi); }
.hero__jump-arrow {
  display: inline-block;
  transition: transform 150ms ease;
}
.hero__jump:hover .hero__jump-arrow { transform: translateY(2px); }

/* --------------------------------------------------------------------------
   Fact rail — date, time, venue, cost. The same four at every width.

   Replaces the floating info card in the concept, which sat off-grid on top of
   the photograph and was the one element reading as a widget rather than a
   designed page. Anchored to the baseline it aligns with the headline, gives
   the frame a foot, and collapses to a clean list on a phone.
   -------------------------------------------------------------------------- */

.factrail {
  position: relative;
  z-index: 2;
  margin-top: clamp(2rem, 5vw, 3rem);
  padding: 0 var(--gutter) clamp(2rem, 5vw, 3rem);
}

.factrail__label {
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.factrail__value {
  margin-top: 0.3rem;
  color: var(--paper);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.3;
}

.factrail__sub {
  display: block;
  margin-top: 0.2rem;
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.875rem;
  font-weight: 400;
}

/* The one link in the hero besides the CTA. Underlined rather than coloured:
   at 62% white on the navy it has to be told from the three sub-lines beside
   it by more than a hue, and a second accent colour up here would compete with
   the CTA. Lifts to full white on hover so the target is unambiguous. */
.factrail__link { text-decoration: none; }
.factrail__link .factrail__sub {
  text-decoration: underline;
  text-decoration-color: rgba(255, 255, 255, 0.34);
  text-underline-offset: 3px;
  transition: color 160ms var(--ease-out), text-decoration-color 160ms var(--ease-out);
}
.factrail__link:hover .factrail__sub {
  color: rgba(255, 255, 255, 0.92);
  text-decoration-color: rgba(255, 255, 255, 0.7);
}
.factrail__link:focus-visible { outline: 2px solid #CFA662; outline-offset: 4px; }

/* Below 600px the rail reads fastest as label/value rows. A 2x2 grid at 375px
   forces "Black Rifle Coffee Company" into a three-line wrap. */
@media (max-width: 599px) {
  .factrail__item {
    display: grid;
    grid-template-columns: 4.5rem 1fr;
    align-items: baseline;
    column-gap: 1rem;
    padding: 0.8125rem 0;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
  }
  .factrail__item:first-child { border-top: 0; }
  .factrail__label { padding-top: 0.2rem; }
  .factrail__value { margin-top: 0; }
}

@media (min-width: 600px) {
  .factrail {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.25rem, 3vw, 2rem) clamp(1.5rem, 4vw, 3rem);
  }
}

/* Four across as soon as there is room. A 2x2 at tablet width leaves the right
   half of the rail empty and the band stops reading as a band. */
@media (min-width: 860px) {
  .factrail {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
    gap: clamp(1.5rem, 2.5vw, 2.5rem);
  }
  /* Hairline separators drawn only where a column begins — restrained
     dividers, not boxes. */
  .factrail__item + .factrail__item {
    padding-left: clamp(1.5rem, 2.5vw, 2.5rem);
    margin-left: calc(clamp(1.5rem, 2.5vw, 2.5rem) * -1);
    border-left: 1px solid rgba(255, 255, 255, 0.16);
  }
}

/* --------------------------------------------------------------------------
   HERO — 1280px and up: full-bleed photograph, type over the water.

   Type column runs to 46% of the hero width — wide enough that "Stronger
   Businesses. / Stronger Communities." holds two lines and the matched pair
   stays a pair.

   That width is what the scrim costs. Measured per element, the requirement
   to clear 4.5:1 is cheap almost everywhere and expensive in exactly one
   place — the last stretch of the headline:

     eyebrow   x  5-25%   alpha 0.34
     actions   x  5-35%   alpha 0.22
     lede      x  5-39%   alpha 0.53
     headline  x 38-45%   alpha 0.56   <- p99.5 sRGB 247, near-white frost

   So the ramp is not a left-dark wash. It is low and even across the water,
   which needs almost nothing, and peaks over the treeline at 30-47% where the
   headline's tail crosses the brightest frost in the file. Two layers, because
   a monotonic gradient strong enough for the treeline would have to be at
   least that strong across all the water to its left, and that is what turns
   the water to mud.
   -------------------------------------------------------------------------- */

@media (min-width: 1280px) {

  .site-header {
    position: absolute;
    inset: 0 0 auto 0;
    background: none;
  }
  /* NOTE: /support and /ask need top clearance because of this rule. See
     .section--pagetop, which has to live below .section--rsvp to win on
     source order. */

  .hero {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* Capped short of the viewport so the next section is visibly cut off at
       the fold. A hero that exactly fills the screen reads as a template and
       gives no scroll cue. Never 100vh. */
    min-height: 42rem;
    min-height: clamp(42rem, 86svh, 52rem);
    padding-top: clamp(6.5rem, 13vh, 9.5rem);
  }

  .hero__media {
    position: absolute;
    inset: 0;
    z-index: -2;
    height: 100%;
  }
  /* Centred, not left-aligned. The old aerial had its subject — the water
     column — hard left, so cropping from the left edge mattered. Bowman Lake
     is symmetrical: the ridgeline and its reflection sit mid-frame and the
     left edge is unlit hillside, so centring keeps the subject and gives the
     navy the dead side of the picture to cover. */
  .hero__media img { object-position: center; }

  /* ONE GRADIENT, left to right, solid to nothing.

     --ink, NOT the navy. #0a1522 is the same ground the dark sections use, so
     the left of the hero is literally the page's dark colour and the hero
     stops being a separate blue object sitting above the rest of the page.
     --scrim is already that colour as an rgb triple, so there is no second
     token to keep in step.

     NO FLOOR. It reaches genuine zero at 72% and the right 28% of the frame is
     untouched photograph — the ridgeline's lit face and the brightest part of
     the reflection are exactly as shot.

     WHERE THE WEIGHT IS RELATIVE TO THE TYPE. The type column is capped at
     46% (.hero__inner, min(46%, 46rem)), and both are percentages of the same
     element so they cannot drift apart at any viewport:

       0–14%   solid                       gutter and the start of every line
       46%     45% opacity                 the type column's right edge
       72%     zero                        26 points of frame past the type

     Fades to rgba(var(--scrim), 0), never `transparent` — that keyword is
     rgba(0,0,0,0) and interpolates through black, bruising the mid-ramp. */
  .hero::before {
    inset: 0;
    height: auto;
    z-index: -1;
    background: linear-gradient(90deg,
      var(--ink) 0%,
      var(--ink) 14%,
      rgba(var(--scrim), 0) 72%);
  }

  .hero__inner {
    /* Type must not cross 46%. Column and scrim are both percentages of the
       same element, so they stay locked together at every viewport. */
    max-width: min(46%, 46rem);
    padding: 0 1rem 0 var(--gutter);
  }

  .hero__title {
    /* Sized so "Stronger Communities." — 10.72em, the binding line — holds one
       line across the range with ~6% headroom for fallback-font metrics. */
    font-size: clamp(2.75rem, calc(3.6vw - 1.6px), 3.375rem);
    line-height: 1.04;
  }

  .hero__lede {
    max-width: 46ch;
    margin-top: clamp(1rem, 2vw, 1.5rem);
  }

  .hero__actions { margin-top: clamp(1.5rem, 2.6vw, 2rem); }

  /* Rail pinned to the base; type stays at the top of the frame. */
  .hero .factrail {
    margin-top: auto;
    padding-top: clamp(1.5rem, 2.4vw, 2rem);
    padding-bottom: clamp(1.5rem, 2.4vw, 2rem);
    /* No ground. The rail sits on bare photograph like everything else in
       this hero. It previously carried a navy wash that composited with the
       ramp to a near-flat band; both are gone. */
  }
  .hero .factrail::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: clamp(4rem, 9vw, 7rem);
    pointer-events: none;
    background: none;
  }
}


/* ==========================================================================
   SECTIONS

   ONE LEFT EDGE, BY DEFAULT. --gutter is where a section starts unless it has
   a reason not to be, and the leftmost element of nearly every section sits on
   it exactly — logo, hero headline, numerals, prose, footer.

   Do NOT read a heading away from the gutter as a violation. Three sections
   put their heading in a second column while their own leftmost content stays
   at 72 (measured at 1440):

     What Happens  numeral 01 at 72; step titles and thesis at 195
     Who It's For  prose at 72; headline mirrored to the right at 756
     Who's behind  portrait at 72; bio and heading beside it at 456

   TWO SECTIONS GENUINELY LEAVE THE GUTTER, and they are meant to: RSVP and the
   closing CTA are centred columns (wrap at 296, content at 368). They are the
   two asks on the page, and centring is what stops an ask reading as one more
   section in the scroll. They keep the gutter as INTERNAL padding, so the
   system is still the one system — it is applied to a centred column instead
   of the viewport.

   The original warning still stands for everything else: a centred max-width
   laid on top of the gutter makes two systems that drift (they were 96px out
   at 1440 and 336px out at 1920 before this). Line length is controlled per
   element with a max-width in ch or rem, because measure is a property of
   text, not of a wrapper.

   ONE SHAPE PER SECTION. The sections deliberately do not share an entrance:

     Why V2B       narrow column, then a full-bleed dark stat band
     What Happens  opens cold on 01; the thesis lands last
     Partners      full-bleed, label inline at the left
     Who It's For  mirrored split — prose left, headline right
     RSVP          inverts the ground to light; form left, details in the margin
     Closing       the only centred block, and the shortest

   Grounds alternate all the way down: photo, light, dark, light, dark, light,
   dark. No two adjacent sections share one.
   ========================================================================== */

.wrap {
  padding-inline: var(--gutter);
}
.wrap--narrow > * { max-width: 36rem; }
/* margin-inline: auto is what actually centres this. Without it the max-width
   box sits flush left and text-align only centres the text inside that box —
   which put the closing block 280px left of the viewport centre at 1440. */
.wrap--center {
  max-width: calc(44rem + var(--gutter) * 2);
  margin-inline: auto;
  text-align: center;
}

.section {
  padding-block: clamp(4rem, 10vw, 8.5rem);
  background: var(--ink);
  color: var(--paper);
}
.section--light {
  background: var(--paper);
  color: var(--ink);
}

.section__title {
  margin: 0;
  font-size: clamp(1.875rem, 3.6vw, 2.875rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.022em;
  text-wrap: balance;
}
.section__lede {
  margin-top: clamp(1rem, 2vw, 1.5rem);
  max-width: 46ch;
  font-size: 1.0625rem;
  line-height: 1.6;
}
.wrap--center .section__lede { margin-inline: auto; }

.prose { margin-top: clamp(1.25rem, 2.5vw, 1.75rem); }
.prose p { max-width: 54ch; font-size: 1.0625rem; line-height: 1.65; }
.prose p + p { margin-top: 1.125rem; }
.section--light .prose p { color: #35404B; }
.section:not(.section--light) .prose p { color: var(--paper-lede); }

/* Eyebrow on paper needs its own value — the stone tone is tuned for type over
   the photograph and disappears here.

   CONTRAST: #a28455 is 3.52:1 on white. The bronze this replaced was 6.22:1.
   At 12px this is UNDER the 4.5:1 floor and is one of the eleven gold-on-white
   sites listed in the audit — left as specified rather than silently darkened.
   Fixing it means either a darker gold on paper or promoting these to the
   ink colour; both are decisions, not repairs. */
.eyebrow--dark { color: #a28455; }
.eyebrow--dark .eyebrow__rule { opacity: 0.45; }

/* --------------------------------------------------------------------------
   03 — Why V2B: narrow, then the stats break full bleed on a dark band
   -------------------------------------------------------------------------- */

.section--why { padding-bottom: 0; }

.statband {
  margin-top: clamp(3.5rem, 8vw, 6rem);
  /* Bottom padding is smaller than top: What Happens is also dark and follows
     immediately, so the two paddings stack into one gap. Measured 274px before
     this. */
  padding-top: clamp(3rem, 7vw, 5.5rem);
  padding-bottom: clamp(2.25rem, 4.5vw, 3.5rem);
  background: var(--ink);
  color: var(--paper);
}

.stats {
  display: grid;
  gap: clamp(2.5rem, 6vw, 5rem);
  margin: 0;
}
@media (min-width: 800px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}

.stats__value {
  font-size: clamp(3.25rem, 9vw, 7rem);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
  color: var(--paper-hi);
}
.stats__label {
  margin: 0.875rem 0 0;
  max-width: 32ch;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--paper-lede);
}
.stats__source {
  margin-top: clamp(2rem, 4vw, 3rem);
  max-width: 78ch;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.62);
}

/* --------------------------------------------------------------------------
   04 — What Happens: cold open on 01, thesis last
   -------------------------------------------------------------------------- */

/* Follows the dark stat band directly — no full section padding on top. */
.section--what { padding-top: clamp(2.5rem, 5vw, 4rem); }

.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.steps__item {
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
}
.steps__num {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}
.steps__title {
  margin: 0.5rem 0 0;
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  font-weight: 600;
  letter-spacing: -0.012em;
}
.steps__body {
  margin: 0.5rem 0 0;
  max-width: 52ch;
  color: var(--paper-lede);
  line-height: 1.6;
}
@media (min-width: 900px) {
  .steps__item {
    display: grid;
    grid-template-columns: 5rem minmax(0, 12rem) minmax(0, 1fr);
    gap: clamp(1.5rem, 3vw, 3rem);
    align-items: baseline;
  }
  .steps__num, .steps__title, .steps__body { margin: 0; }
}

/* The claim arrives after the evidence. Sized like a headline because it is
   one — it just does not open the section. */
.thesis {
  margin: clamp(2.5rem, 6vw, 4.5rem) 0 0;
  max-width: 18ch;
  font-size: clamp(1.875rem, 4vw, 3.25rem);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.026em;
  color: var(--paper-hi);
}
.thesis__note {
  margin: 1rem 0 0;
  max-width: 44ch;
  color: var(--paper-lede);
  line-height: 1.6;
}
@media (min-width: 900px) {
  .thesis { margin-left: calc(5rem + clamp(1.5rem, 3vw, 3rem)); }
  .thesis__note { margin-left: calc(5rem + clamp(1.5rem, 3vw, 3rem)); }
}

/* --------------------------------------------------------------------------
   05 — Supporters: full bleed, label inline at the left

   Tightened after measuring: of 918px, the generic section padding was 272px,
   tier gaps 96px, and the space between each tier's note and its names 44px.
   None of that was carrying meaning at three names.
   -------------------------------------------------------------------------- */

.section--partners { padding-block: clamp(3rem, 6vw, 5.5rem); }

.wall__label {
  margin: 0 0 clamp(1.25rem, 2.6vw, 1.75rem);
  max-width: 30ch;
  font-size: clamp(1.125rem, 1.8vw, 1.375rem);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.wall__label span { display: block; color: #5C6976; }

/* Flex, not grid. auto-fit with 1fr stretches a short row across the whole
   width — two supporters ended up 650px apart with the rule framing the space
   between them. Flex-wrap lets each name take its natural width and clusters
   them at the left, so a tier of one reads the same as a tier of six. */
.wall {
  display: flex;
  flex-wrap: wrap;
  /* TOP, not centre, on the cross axis. Every entry reserves the same cell
     height for its logo or its name, so aligning to the start puts all the
     marks in one band and all the role labels on one line beneath it —
     including entries that have no role label at all. Centring made an entry's
     height depend on whether it happened to carry a role, which dropped a
     role-less name 12px below its neighbours. */
  align-items: flex-start;
  /* Centred on the main axis. Marks sit centred in their tier rather than
     hanging on the gutter — the one place on the page where the left edge is
     deliberately given up, because a showcase row of two or three reads as a
     row rather than as an abandoned left column. */
  justify-content: center;
  gap: clamp(1.25rem, 2.5vw, 1.75rem) clamp(1.75rem, 3.2vw, 2.75rem);
  margin: clamp(0.625rem, 1.2vw, 0.75rem) 0 0;
  padding: clamp(0.875rem, 1.6vw, 1rem) 0 0;
  list-style: none;
  border-top: 1px solid rgba(10, 21, 34, 0.16);
}

/* EXCEPT A TIER OF ONE ON A WIDE COLUMN, which hangs on the gutter like
   everything else on the page. Centring a single mark does not centre a row —
   there is no row — it just casts the one logo adrift from the label and rule
   that introduce it, 370px away at 1440 with nothing in between. The old
   auto-fit bug wearing a different hat: a tier of one has to read like a tier
   of six, and at 1fr it stretched, at centre it floats.

   Only above 36rem. On a phone the column is barely wider than the mark, so
   centring moves it 90px rather than 370 and nothing is adrift — while a lone
   logo pinned left under a centred wall below it looks like a mistake. Without
   :has() support this does not apply and everything centres, which is the
   harmless direction to fail in. */
@media (min-width: 36rem) {
  .wall:has(> .wall__item:only-child) { justify-content: flex-start; }
}

/* Tiers do the work the old count-aware rule was doing. That rule scaled three
   names up to display size so a short row would not read as a gap; tiering
   groups them by what each organisation actually gave, which carries the same
   weight without inflating the section. So the names sit smaller and tighter
   and the wall stops dominating the page. */

/* ONE gap between tiers, wherever they sit. Three different values used to be
   in play — the stacked margin, the organiser tier's own larger margin, and the
   grid's row-gap — so the rhythm changed depending on which pair you measured.
   Everything below reads from this token. */
.tiers { --tier-gap: clamp(2rem, 3.6vw, 3rem); }
.tier + .tier { margin-top: var(--tier-gap); }

/* Three tiers, two rows. Contributors takes the top-left with Organized by
   beside it; Supporters runs the full width beneath, so the tier most likely
   to grow grows DOWN rather than squeezing a column.

   THE COLUMNS ARE NOT EQUAL, and that is the whole trick. Sitting beside
   Contributors, the organiser tier would otherwise read as its peer purely
   from position — equal width is what says "same rank". At 1.55fr to 1fr it
   reads as the narrower column it is, and the smaller cells below finish the
   job. Attribution, still not a claim of scale.

   THE BREAKPOINT IS 90rem, NOT 60rem, AND THE ARITHMETIC IS THE REASON. Two
   full-size cells side by side need 2 x 13.5rem plus the wall's own gap —
   about 476px. The organiser column is the 1fr of 1.55fr/1fr, so it only
   reaches 476px at roughly 1414px of viewport. Below that the two organiser
   logos wrap to two rows, which makes the attribution tier the TALLEST block
   in the row and inverts the ranking this layout exists to state. So below
   90rem everything stacks in DOM order and attribution lands last on its own,
   which says the same thing a different way.

   This is the cost of one cell size everywhere. The alternative is a smaller
   cell for this tier, which was tried and removed for being the only place on
   the page where a logo is a different size. */
@media (min-width: 90rem) {
  .tiers {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    grid-template-areas:
      "contributors organizers"
      "supporters   supporters";
    column-gap: clamp(2rem, 4vw, 3.5rem);
    row-gap: var(--tier-gap);
    align-items: start;
  }
  .tiers .tier + .tier { margin-top: 0; }
  /* Organized by has no note and must not be given one — a note is exactly the
     weight this tier is meant not to carry. So it reserves the space instead,
     and its rule lands on the same line as Contributors'. Inside the media
     query only: below 90rem the tiers stack, there is nothing to line up with,
     and this would just be a gap under a heading. */
  .tier--organizers .tier__label { margin-bottom: var(--tier-note-space); }

  .tier--contributors { grid-area: contributors; }
  .tier--supporters   { grid-area: supporters; }
  /* Row gap owns the spacing now; the stacked-layout margin would double it. */
  .tier--organizers   { grid-area: organizers; margin-top: 0; }
}

.tier__label {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
}
.tier__note {
  margin: 0.3125rem 0 0;
  max-width: 62ch;                    /* one line, not two */
  font-size: 0.875rem;
  line-height: 1.5;
  color: #5C6976;
}
/* Exactly the vertical space one .tier__note occupies: its top margin plus one
   line box. Derived from the three values directly above — change the note's
   margin, size or leading and change this with it. Used to hold that space
   open in a tier that has no note, so the rules below the labels land on one
   line. Only ever applied where the note is a single line, which the 62ch cap
   and the column width together guarantee. */
.tier { --tier-note-space: calc(0.3125rem + 0.875rem * 1.5); }

/* Attribution, not a claim of scale: the organiser tier is quieter than the
   two above it and carries no explanatory note. */
.tier--organizers { margin-top: var(--tier-gap); }
.tier--organizers .tier__label { color: #5C6976; font-weight: 500; }
.wall--quiet .wall__name { font-size: 0.9375rem; font-weight: 500; color: #35404B; }
.wall--quiet { padding-block: clamp(1rem, 2vw, 1.25rem); border-bottom: 0; }
/* ONE CELL SIZE ON THE WHOLE WALL. The quiet tier briefly had its own smaller
   box to keep it subordinate once it moved up beside Contributors; that made
   the organiser logos the only ones on the page at a different scale, which is
   a bigger inconsistency than the ranking it bought. Subordination is carried
   by the narrower column, the grey label, and the absence of a tier note —
   none of which touch the logos. Do not reintroduce a per-tier cell size. */

.wall__item { display: flex; align-items: center; flex: 0 0 auto; }

/* LOGO CELLS — IDENTICAL FOR EVERY LOGO.

   One fixed box, object-fit: contain, centred. The same cell whatever shape
   arrives. No per-file tuning, no measured ratios, nothing that drives a row.

   This replaced a per-logo scaling system that corrected each file's internal
   padding to a target ink height. It worked for the two logos it was measured
   against and would not have survived the third: every new partner logo needed
   its own measurement, and the value that measurement produced still had to be
   overridden by eye afterwards. A grid that needs hand-tuning per file is not
   a grid.

   The trade, taken deliberately: a wide wordmark reads lighter than a compact
   mark in the same cell, because it letterboxes to the cell height while a
   compact mark fills it. That is the cost of absorbing whatever partners send
   without touching CSS.

   Adding a supporter is an edit to the markup in index.html — name, role
   label, tier, logo filename, URL. Nothing here needs touching. */
.wall__mark { display: block; }

/* A text-only entry's NAME gets the same box as a logo cell — not the mark as
   a whole. That distinction is the entire fix: role labels line up only if the
   thing directly above them is the same height in both kinds of entry. Putting
   the min-height on .wall__mark centres name-plus-role together and leaves the
   two roles exactly as far apart as before, which is what happened the first
   time. Tier-level, so the quiet tier's smaller cell is inherited and no file
   is ever measured.

   The name is centred vertically inside that box the way a logo is, and stays
   left-aligned horizontally — a centred cell is right for a logo whose visual
   left edge moves with every file, and wrong for a name that should hang on
   the same left edge as the tier label above it. */
/* Unconditional now. This used to start at 36rem because below it every entry
   was alone on its row and the reserved height was just a gap between a name
   and its own role label. Two-across on a phone ends that: a name can share a
   row with a logo at 375, so it has to reserve the same box there too. It
   reads --logo-cell-h, so it shrinks with the cell. */
.wall__mark .wall__name {
  display: flex;
  align-items: center;
  min-height: var(--logo-cell-h, 6.075rem);
}

.wall__cell {
  display: flex;
  align-items: center;
  justify-content: center;
  /* +35%, aspect preserved at 2.22:1 so logos letterbox exactly as before,
     just larger. 10 -> 13.5rem, 4.5 -> 6.075rem. */
  width: var(--logo-cell-w, 13.5rem);
  height: var(--logo-cell-h, 6.075rem);
}
/* TWO ACROSS ON A PHONE. Two full-size cells plus the row gap need about
   460px, which a 375px viewport does not have, so below 31.25rem the cell
   drops to 9.25rem x 4.1625rem — the same 2.22:1, and still ONE size for every
   logo on the wall at that viewport. The rule against per-tier cell sizes is
   about two logos differing side by side; this changes all of them at once. */
@media (max-width: 31.25rem) {
  .wall { --logo-cell-w: 9.25rem; --logo-cell-h: 4.1625rem; }
}

.wall__logo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Every entry links to that organisation, in a new tab. */
.wall__link {
  display: block;
  color: inherit;
  text-decoration: none;
  border-radius: 2px;
  transition: opacity 160ms var(--ease-out);
}
.wall__link:hover { opacity: 0.68; }
.wall__link:focus-visible { outline: 2px solid #a28455; outline-offset: 6px; }
.wall__link:hover .wall__name { text-decoration: underline; text-underline-offset: 4px; }

.wall__name {
  display: block;
  font-size: clamp(1rem, 1.2vw, 1.125rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.008em;
  text-wrap: balance;
}
.wall__name--tbc { color: #5C6976; }
/* The role label survives into the logo path — "Host venue" is why a stranger
   understands Black Rifle is on this wall, and that gets more useful as more
   logos arrive, not less. Centred under the cell rather than left-aligned: the
   logo inside is centred by object-fit, so its visual left edge moves with
   every file, and only the cell's centre is stable. */
.wall__cell + .wall__role { text-align: center; }
.wall__role {
  display: block;
  margin-top: 0.3125rem;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #a28455;
}
.wall__note {
  margin: clamp(1rem, 2vw, 1.375rem) 0 0;
  font-size: 0.9375rem;
  color: #5C6976;
}

/* --------------------------------------------------------------------------
   06 — Who It's For: mirrored split, hanging list
   -------------------------------------------------------------------------- */

.mirror__body {
  margin-top: 1.25rem;
  max-width: 48ch;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--paper-lede);
}
@media (min-width: 900px) {
  .mirror {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 5rem);
    align-items: start;
  }
  /* Heading stays first in the DOM for reading order; only the placement flips. */
  .mirror__lead { grid-column: 2; grid-row: 1; }
  .mirror__body { grid-column: 1; grid-row: 1; margin-top: 0.5rem; }
}

.groups {
  margin: clamp(2.5rem, 5vw, 4rem) 0 0;
  padding-top: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.groups__item { padding-block: clamp(1rem, 2vw, 1.375rem); }
.groups__item + .groups__item { border-top: 1px solid rgba(255, 255, 255, 0.10); }
.groups__title {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.groups__body {
  margin: 0.375rem 0 0;
  max-width: 52ch;
  color: var(--paper-lede);
  line-height: 1.6;
}
/* Hanging labels: the term sits in the left margin, the description runs
   beside it. A list, not a grid of pseudo-cards. */
@media (min-width: 900px) {
  .groups__item {
    display: grid;
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: clamp(1.5rem, 3vw, 3rem);
    align-items: baseline;
  }
  .groups__body { margin: 0; }
}

/* --------------------------------------------------------------------------
   04b — Agenda: a time rail

   Shares the dark ground with What Happens above it because it answers the
   same question concretely, but the shape is unrelated: times at display scale
   in a left column, description beside them, no eyebrow and no lead block.
   The slightly lifted ground separates the two without a hard edge.
   -------------------------------------------------------------------------- */

.section--agenda {
  background: #10151B;
  padding-block: clamp(3rem, 7vw, 6rem);
}

.agenda__title {
  margin: 0;
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--paper-hi);
}
.agenda__note {
  margin: 0.4375rem 0 0;
  max-width: 52ch;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.62);
}

.agenda {
  margin: clamp(1.75rem, 4vw, 2.75rem) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.agenda__slot {
  padding-block: clamp(1.125rem, 2.4vw, 1.75rem);
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
}
.agenda__time {
  margin: 0;
  font-size: clamp(1.25rem, 2.4vw, 1.75rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--gold-lift);
  white-space: nowrap;
}
.agenda__name {
  margin: 0.375rem 0 0;
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--paper-hi);
}
.agenda__body p {
  margin: 0.375rem 0 0;
  max-width: 58ch;
  line-height: 1.6;
  color: var(--paper-lede);
}

@media (min-width: 46rem) {
  .agenda__slot {
    display: grid;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
    gap: clamp(1.5rem, 3vw, 3rem);
    align-items: baseline;
  }
  .agenda__name { margin-top: 0; }
}

.agenda__standing {
  margin: clamp(1.5rem, 3vw, 2rem) 0 0;
  max-width: 60ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--paper-lede);
}
.agenda__standing strong { color: var(--paper-hi); font-weight: 600; }

/* --------------------------------------------------------------------------
   06b — FAQ: native disclosure

   <details>/<summary>. No JavaScript, keyboard-operable for free, and seven
   long answers do not have to sit open. Structurally unlike anything else on
   the page, which is the point.
   -------------------------------------------------------------------------- */

.faq__title {
  margin: 0 0 clamp(1.25rem, 2.6vw, 1.75rem);
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.012em;
}

.faq { border-top: 1px solid rgba(10, 21, 34, 0.16); max-width: 62rem; }
.faq__item { border-bottom: 1px solid rgba(10, 21, 34, 0.16); }

.faq__q {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding-block: clamp(0.875rem, 1.8vw, 1.125rem);
  font-size: clamp(1rem, 1.5vw, 1.125rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  cursor: pointer;
  list-style: none;                   /* hide the default triangle */
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: #35404B; }
.faq__q:focus-visible { outline: 2px solid #a28455; outline-offset: 3px; }

/* Own marker: a bronze cross that becomes a minus when open. Drawn with
   pseudo-elements rather than an icon font or an SVG file. */
.faq__q::after {
  content: "";
  flex: none;
  width: 0.75rem;
  height: 0.75rem;
  margin-left: auto;
  align-self: center;
  background:
    linear-gradient(currentColor, currentColor) center/100% 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center/1.5px 100% no-repeat;
  color: #a28455;
  transition: transform 180ms var(--ease);
}
.faq__item[open] .faq__q::after {
  background: linear-gradient(currentColor, currentColor) center/100% 1.5px no-repeat;
  transform: rotate(180deg);
}

.faq__a { padding-bottom: clamp(1rem, 2vw, 1.375rem); }
.faq__a p {
  margin: 0;
  max-width: 62ch;
  line-height: 1.65;
  color: #35404B;
}

/* --------------------------------------------------------------------------
   06c — Who's behind it

   The only photograph of a person on the site, and the only first-person
   voice. Portrait left at a fixed measure, text beside it.
   -------------------------------------------------------------------------- */

.section--bio { background: var(--ink); }

.bio__portrait { margin: 0 0 clamp(1.5rem, 3vw, 0) 0; }
.bio__portrait img {
  display: block;
  width: 100%;
  max-width: 20rem;
  height: auto;
  aspect-ratio: 800 / 1200;
  object-fit: cover;
}

.bio__text p {
  margin: clamp(1rem, 2vw, 1.375rem) 0 0;
  max-width: 56ch;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--paper-lede);
}

@media (min-width: 52rem) {
  .bio {
    display: grid;
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: start;
  }
  .bio__portrait { margin: 0; }
}

/* --------------------------------------------------------------------------
   07 — RSVP: light ground, form left, details hanging in the right margin
   -------------------------------------------------------------------------- */

/* Single centred column, not a two-column split.

   The split put the four event facts in a right-hand column beside a form four
   times its height, so most of that column was empty — the dead space was the
   section's real problem, not its density. The facts are now a compact rail
   directly above the form, and the whole thing is one column centred in the
   viewport, so the remaining whitespace is symmetric margin rather than a
   stranded column.

   The form still splits into two fields per row inside this column. */
.section--rsvp { padding-block: clamp(3rem, 6vw, 5rem); }

/* Any page that opens straight onto a section rather than a hero — /support,
   /ask. At 1280 and up the header is position:absolute so it floats over the
   page and takes no flow space; the landing page absorbs that in the hero's own
   padding-top, but a page whose first element is a section has nothing
   absorbing it, and the eyebrow lands under the nav.
   --anchor-offset rather than a hand-picked number: it is what every in-page
   anchor scrolls to, so opening /support and jumping to #partners put a heading
   in the same place instead of two subtly different ones. Below 1280 the header
   is sticky and in flow, so it already clears and this must not apply.
   Must stay after .section--rsvp above: same specificity, source order decides. */
@media (min-width: 1280px) {
  .section--pagetop { padding-top: var(--anchor-offset); }
}

.rsvp {
  max-width: calc(44rem + var(--gutter) * 2);
  margin-inline: auto;
}

/* A form, not a second hero. */
.section--rsvp .section__title {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  letter-spacing: -0.015em;
}
.rsvp .section__lede { color: #35404B; }

/* Compact rail: the four facts on one line where there is room. */
.rsvp__rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem clamp(1.5rem, 3vw, 2.5rem);
  margin: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
  padding-top: clamp(1rem, 2vw, 1.25rem);
  border-top: 1px solid rgba(10, 21, 34, 0.16);
}
.rsvp__fact dt {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #a28455;
}
.rsvp__fact dd {
  margin: 0.1875rem 0 0;
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   Form. On paper here, so the field styling inverts from the earlier dark
   treatment — including the CTA, whose gold fill reaches 3.52:1 against
   --paper. That now clears the 3:1 a control boundary requires on its own; the
   dark rim below is kept for the shape, not because contrast still needs it.
   -------------------------------------------------------------------------- */

.form { margin-top: clamp(1.5rem, 3vw, 2rem); }

.field { margin-bottom: 1rem; border: 0; padding: 0; }

/* Two columns once there is room. Everything is full width by default and only
   the paired text inputs opt into a half — so the radio set, the opt-in and the
   submit keep the full measure without extra rules. */
@media (min-width: 40rem) {
  .form {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(1rem, 2.5vw, 1.5rem);
    align-items: start;
  }
  .form > * { grid-column: 1 / -1; }
  .form > .field--half { grid-column: span 1; }

  /* Paired fields have hints of different lengths — Organization runs to three
     lines where Role runs to one — which left their inputs 38px out of step.
     Stretch both cells to the row height and push the input to the bottom of
     each, so inputs land on a common line while the hints stay above the field
     where they are read before it is filled. */
  .form { align-items: stretch; }
  .form > .field--half { display: flex; flex-direction: column; }
  .form > .field--half .field__input { margin-top: auto; }
}
.field__label {
  display: block;
  margin-bottom: 0.4375rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
}
.field__optional { font-weight: 400; color: #5C6976; }

/* Hint sits between the label and the input, so it is read before the field is
   filled rather than after. */
.field__hint {
  margin: 0 0 0.4375rem;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: #5C6976;                     /* 5.9:1 on --paper */
}
.field__label + .field__hint { margin-top: -0.125rem; }

.field__input {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 0.75rem 0.9375rem;
  border: 1px solid rgba(10, 21, 34, 0.28);
  border-radius: 0;
  background: #FFFFFF;
  color: var(--ink);
  font: inherit;
  font-size: 1rem;                    /* 16px — stops iOS zooming on focus */
  transition: border-color 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
}
.field__input:hover { border-color: rgba(10, 21, 34, 0.48); }
.field__input:focus {
  outline: none;
  border-color: #a28455;
  box-shadow: inset 0 0 0 1px #a28455;
}
.field__input:focus-visible { outline: 2px solid #a28455; outline-offset: 2px; }
.field__input[aria-invalid="true"] { border-color: #A3352A; }

.field__textarea {
  min-height: 7.5rem;
  height: auto;
  padding-block: 0.75rem;
  line-height: 1.55;
  resize: vertical;
}
/* The textarea is not one of the paired half-fields, so it must not inherit
   the margin-top:auto that bottom-aligns those inputs. */
.form > .field .field__textarea { margin-top: 0; }

.support__note { margin-top: clamp(1.5rem, 3vw, 2rem); }
.support__note a { color: #a28455; }
/* /ask points at the FAQ from its lede, the first link to appear in a lede on
   paper. Without this it renders in the browser's default blue. Same bronze as
   the note link above — 6.22:1 on --paper. */
.section--light .section__lede a { color: #a28455; }

.field__error {
  margin: 0.4375rem 0 0;
  font-size: 0.8125rem;
  color: #A3352A;                     /* 6.4:1 on --paper */
}

.field--choice legend { padding: 0; }

.choice {
  display: flex;
  align-items: flex-start;
  gap: 0.6875rem;
  padding-block: 0.3125rem;
  cursor: pointer;
}
.choice__input {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  margin-top: 0.1875rem;
  accent-color: #a28455;
}
.choice__text { font-size: 0.9375rem; line-height: 1.5; color: #35404B; }
.choice--optin {
  margin-block: 0.25rem 1.25rem;
  padding-top: 0.875rem;
  border-top: 1px solid rgba(10, 21, 34, 0.16);
}

/* The business card, revealed on opt-in (see rsvp.js).

   ON THE FORM'S OWN LEFT EDGE AND ITS OWN GRID. It was briefly inset behind a
   bronze rule, which made a block of ordinary form fields read as a pull-quote
   and left its inputs narrower than every input above them. A revealed section
   needs to be recognisable as revealed, not as a different kind of thing: the
   heading and the space around it carry that on their own.

   Nothing animates in. A block of fields sliding open under the cursor is a
   flourish, and this one is already a surprise. */
.card-fields {
  margin-block: 1.5rem 1.25rem;
}
.card-fields > legend { padding: 0; }
.card-fields > .field__hint { margin-bottom: 0.875rem; }
.card-fields .field:last-child { margin-bottom: 0; }

@media (min-width: 40rem) {
  /* The SAME two columns and the SAME gutter as .form, restated because the
     form's rules only reach its direct children and these sit inside a
     fieldset. Same values on the same full width means the columns land on the
     form's columns exactly — a half here is the width of Name above it, not a
     half of some narrower inner box. Change one and change the other. */
  .card-fields__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(1rem, 2.5vw, 1.5rem);
    align-items: start;
  }
  .card-fields__grid > * { grid-column: 1 / -1; }
  .card-fields__grid > .field--half { grid-column: span 1; }
}

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Turnstile renders an iframe into this div. :not(:empty) so the gap only
   exists once there is a widget — with no sitekey configured the div stays
   empty and must not leave a hole above the button. */
.form__turnstile:not(:empty) { margin-bottom: clamp(1rem, 2vw, 1.25rem); }

.form__submit { width: 100%; }
@media (min-width: 480px) { .form__submit { width: auto; } }
.form__submit[disabled] { opacity: 0.6; cursor: progress; }
/* Darker rim on paper. Was required when the fill was 2.6:1 against --paper;
   gold is 3.52:1 and clears 1.4.11 unaided, so this is now a styling choice. */
.section--light .btn--primary::before { background: rgba(var(--scrim), 0.85); }

.form__status:empty { display: none; }
.form__status { margin: 0 0 1.125rem; font-size: 0.9375rem; color: #35404B; }

/* Confirmation, in place of the form */
.confirm { max-width: 34rem; }
.confirm:focus { outline: none; }
.confirm__title {
  margin: 0;
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.015em;
}
.confirm__note { margin: 0.625rem 0 0; line-height: 1.6; }
.section--light .confirm__note { color: #35404B; }

.factrail--confirm {
  display: grid;
  gap: 1.125rem clamp(1.5rem, 4vw, 3rem);
  margin-top: clamp(1.5rem, 3vw, 2rem);
  padding: clamp(1.25rem, 3vw, 1.75rem) 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  background: none;
}
@media (min-width: 600px) {
  .factrail--confirm { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.factrail--confirm .factrail__item { padding: 0; margin: 0; border: 0; }

/* The in-page confirmation replaces the form, and the form now sits on paper.
   The fact-rail colours were written for type over the photograph, so on a
   light ground the rule and the labels vanish — the same failure as the email,
   one layer down. Restate them for the light surface. */
.section--light .factrail--confirm { border-top-color: rgba(10, 21, 34, 0.16); }
.section--light .factrail--confirm .factrail__label,
.section--light .factrail--confirm .factrail__sub { color: #59636F; }
.section--light .factrail--confirm .factrail__value { color: var(--ink); }
.section--light .confirm__title { color: var(--ink); }



/* ==========================================================================
   UTILITY PAGES — RSVP confirmation, cancel confirm, cancel done, errors

   Short, single-purpose, arrived at from an email. They deliberately do NOT
   use the landing page's container: that puts one left edge at --gutter and
   lets content run the full width, which is right for a long scrolling page
   and wrong for four lines, which end up pinned to the left of an empty
   screen. Here the column is centred and capped, the whole thing is centred
   vertically, and the logo sits above it so the page still identifies itself
   with no navigation present.

   Same palette, same type scale, same components. Only the container differs.
   ========================================================================== */

.utility {
  min-height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(2.5rem, 8vw, 5rem) var(--gutter);
  background: var(--ink);
  color: var(--paper);
}

.utility__inner {
  width: 100%;
  max-width: 34rem;
}

.utility__home {
  display: inline-flex;
  margin-bottom: clamp(2rem, 5vw, 3rem);
  line-height: 0;
}
.utility__home .logo { height: clamp(2.5rem, 3.2vw, 3rem); }

.utility__title {
  margin: 0;
  font-size: clamp(1.75rem, 4vw, 2.375rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* Body copy on these pages comes through as .section__lede from the handlers. */
.utility .section__lede {
  margin-top: clamp(1rem, 2.5vw, 1.375rem);
  max-width: none;
  color: var(--paper-lede);
}
.utility .section__lede a { color: var(--gold-lift); }

.utility .form { margin-top: clamp(1.75rem, 4vw, 2.25rem); }
.utility .closing__note { margin-top: 1rem; }

/* Date, time, venue, cost — restated on every confirmation. */
.utility__facts {
  display: grid;
  gap: 1.125rem clamp(1.5rem, 5vw, 2.5rem);
  margin: clamp(1.75rem, 4vw, 2.25rem) 0 0;
  padding-top: clamp(1.25rem, 3vw, 1.5rem);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
@media (min-width: 32rem) {
  .utility__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.utility__fact-label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.utility__fact-value {
  margin: 0.25rem 0 0;
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.35;
  color: var(--paper);
}
.utility__fact-sub {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.875rem;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.62);
}

.utility__back { margin-top: clamp(2rem, 5vw, 2.75rem); }

/* --------------------------------------------------------------------------
   08 — Closing
   -------------------------------------------------------------------------- */

.closing__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
}
.closing__note {
  margin-top: clamp(1.5rem, 3vw, 2rem);
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.62);
}
/* The colour above is for the dark closing section. On paper it is the paper —
   1.00:1, text the exact colour of its own background — which is how the note
   at the foot of /support shipped invisible. Same idiom as
   .section--light .confirm__note below: raise specificity rather than rely on
   source order, so a rule moving in this file cannot bring the bug back. */
.section--light .closing__note { color: #5C6976; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.site-footer {
  padding: clamp(2.5rem, 6vw, 4rem) var(--gutter) clamp(2rem, 5vw, 3rem);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  background: var(--ink);
}
.site-footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.5rem, 4vw, 3rem);
}
.site-footer__home { display: block; line-height: 0; }
.site-footer__logo { height: clamp(2.5rem, 3.2vw, 3rem); }

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 2.5vw, 2rem);
}
.site-footer__nav a {
  display: inline-block;
  padding-block: 0.35rem;             /* ~28px target, over the 24px minimum */
  color: var(--paper-lede);
  font-size: 0.9375rem;
  text-decoration: none;
  transition: color 160ms var(--ease-out);
}
.site-footer__nav a:hover { color: var(--paper-hi); text-decoration: underline;
  text-underline-offset: 5px; }

.site-footer__meta {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem clamp(2rem, 5vw, 4rem);
  justify-content: space-between;
  /* Share a first baseline, not a box top. Default stretch aligned the two
     blocks' BOXES, and because the left block's first line box is taller than
     the right's, its text sat 4.5px lower than "Organized by" beside it. */
  align-items: baseline;
}
.site-footer__event {
  margin: 0;
  font-size: 0.875rem;
  color: var(--paper-lede);
}
.site-footer__contact {
  margin: 0;
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.62);
}
.site-footer__contact a {
  display: inline-block;
  /* The padding is the touch target — 17px of line box was under the 24px
     minimum. The negative margin takes it back OUT of line-box height, so the
     target stays 25px while the line returns to its natural 21px. Without this
     the two route lines set a 31px rhythm against the credits block's 26px
     beside them, and no amount of nudging the gap reconciles two blocks whose
     leading differs. */
  padding-block: 0.25rem;
  margin-block: -0.25rem;
  color: var(--gold-lift);
}
/* Keeps the two routes as ONE item in __meta's space-between row. Two loose
   <p>s here would become a fourth flex child and redistribute the whole row.
   The gap matches .site-footer__credits p + p exactly, so both columns run on
   the same vertical rhythm. */
.site-footer__routes { display: grid; gap: 0.35rem; }

.site-footer__credits {
  color: rgba(255, 255, 255, 0.62);   /* 7.08:1 on --ink */
  font-size: 0.8125rem;
  line-height: 1.6;
  text-wrap: pretty;
}
.site-footer__credits p + p { margin-top: 0.35rem; }

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .hero__jump:hover .hero__jump-arrow { transform: none; }
  .btn--primary:hover,
  .btn--primary:active { transform: none; }
}

/* ==========================================================================
   /admin — the attendee list

   Uses the site's vocabulary, not an admin dialect of its own:
   --gutter for the page edge (every other page uses it), the eyebrow from the
   section headers, the hero's fact-rail label/value for every figure, and the
   agenda rail's display numeral for the head-count.

   NO CHAMFER anywhere here. It is reserved for the RSVP control; an admin
   button borrowing it would spread the one shape the brand keeps scarce.

   Hierarchy: the name is the only strong weight in a row. Everything else —
   type, date, opt-in state — shares one small-caps metadata voice, and the
   actions stay behind a quiet disclosure until wanted.
   ========================================================================== */

.ad { background: var(--ink); color: var(--paper); }
.ad__page {
  max-width: 78rem;
  padding: clamp(1.75rem, 5vw, 3rem) var(--gutter) 5rem;
}

.ad__head { margin-bottom: clamp(1.5rem, 3vw, 2rem); }
.ad__head .eyebrow { margin-bottom: 0.625rem; }
.ad__when {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--paper-lede);
}

/* Head-count: the agenda rail's display numeral. Bronze, tabular, tight. */
.ad__count {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0 0 clamp(1.5rem, 3vw, 2rem);
  padding-bottom: clamp(1.25rem, 2.5vw, 1.75rem);
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
}
.ad__count-n {
  font-size: clamp(3.25rem, 9vw, 5rem);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--gold-lift);
}
.ad__count-label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}

/* Every summary figure, one format — the hero's fact rail. */
.ad-rail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
  margin: 0 0 clamp(1.5rem, 3vw, 2rem);
}
.ad-rail--types {
  padding-top: clamp(1.25rem, 2.5vw, 1.75rem);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.ad-rail__label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.ad-rail__value {
  margin: 0.3rem 0 0;
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--paper-hi);
}
.ad-rail__sub {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.8125rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: rgba(255, 255, 255, 0.62);
}

/* MOBILE: the explanatory subs go. Seven fact pairs each carrying a line of
   prose put roughly 750px of statistics between the head-count and the search
   box, on the one screen whose main job is finding one person fast. The labels
   say what the figures are; the prose only says it again at length.

   A sub carrying a FIGURE stays — see fact(isData) in admin/index.js. Saving a
   line by deleting a number is not a trade worth making at the door. */
@media (max-width: 30rem) {
  .ad-rail__sub:not(.ad-rail__sub--data) { display: none; }
}
.ad__banner {
  margin: 0 0 1.25rem;
  padding: 0.75rem 0.9375rem;
  border-left: 2px solid var(--gold);
  background: rgba(190, 149, 83, 0.1);
  font-size: 0.9375rem;
  color: var(--paper-hi);
}
.ad__banner--bad { border-left-color: #E2A08A; background: rgba(226, 160, 138, 0.1); }

/* ---- tools ---- */

.ad__tools { display: flex; gap: 0.625rem; margin-bottom: 0.5rem; max-width: 34rem; }
.ad__search {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 48px;
  padding: 0.625rem 0.875rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 0;
  background: rgba(255, 255, 255, 0.04);
  color: var(--paper-hi);
  font: inherit;
  font-size: 1rem;                    /* 16px — stops iOS zooming on focus */
}
.ad__search::placeholder { color: rgba(255, 255, 255, 0.45); }
.ad__search:focus-visible { outline: 2px solid var(--gold-lift); outline-offset: 2px; }
.ad__csv {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 1rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  color: var(--paper-lede);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
}
.ad__csv:hover { border-color: var(--gold); color: var(--gold-lift); }
.ad__csv:focus-visible { outline: 2px solid var(--gold-lift); outline-offset: 2px; }

.ad__status:empty { display: none; }
.ad__status { margin: 0 0 0.5rem; font-size: 0.8125rem; color: rgba(255, 255, 255, 0.62); }

.ad__h {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: clamp(1.75rem, 3.5vw, 2.5rem) 0 0;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.ad__h span { font-variant-numeric: tabular-nums; color: var(--paper-lede); }
.ad__h--muted { color: rgba(255, 255, 255, 0.38); }

/* ---- rows ---- */

.ad__list { margin: 0.75rem 0 0; padding: 0; list-style: none; }
.ad-empty { padding: 1rem 0; color: rgba(255, 255, 255, 0.62); }

.ad-row {
  display: grid;
  gap: 0.5rem;
  padding: 1rem 0 1rem 0.875rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  /* THE LEFT RAIL IS ATTENDANCE. Fixed x position down the whole list, so it
     reads as a column rather than as a property of each row — which is what
     lets you scan for "who is coming" without reading anything. Pattern does
     the work as well as colour: solid, dashed and dotted are three different
     things to a monochrome eye, so this does not lean on hue alone. */
  border-left: 5px solid transparent;
}
.ad-row--in     { border-left-color: var(--gold); }
.ad-row--away   { border-left: 5px dashed rgba(255, 255, 255, 0.42); }
.ad-row--unset  { border-left: 5px dotted rgba(255, 255, 255, 0.22); }

/* STATUS IS THE WHOLE ROW, not a word in it. A cancelled row is dim and its
   name is struck, so it never has to be read to be placed — it looks spent
   from across the list. The chip stays as confirmation, not as the signal. */
.ad-row.is-cancelled { border-left: 5px solid rgba(255, 255, 255, 0.16); opacity: 0.55; }
.ad-row.is-cancelled .ad-row__name {
  color: var(--paper-lede);
  font-weight: 500;
  text-decoration: line-through;
  text-decoration-color: rgba(255, 255, 255, 0.45);
}
.ad-row.is-cancelled:hover,
.ad-row.is-cancelled:focus-within { opacity: 1; }

/* The one strong weight in the row. */
.ad-row__name {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--paper-hi);
}
.ad-row__org {
  margin: 0.1875rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--paper-lede);
}
/* What they said they do, in their words. Set quieter than the organisation
   line above it so two lines of the same size do not compete — this is the
   line you read second, when the name and the company have not placed them. */
.ad-row__does {
  margin: 0.1875rem 0 0;
  font-size: 0.875rem;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.62);
}

/* One metadata voice for type, date and state — small caps, quiet, separated
   by rules rather than punctuation so nothing reads louder than anything else. */
.ad-row__meta,
.ad-row__marks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem 0.75rem;
  margin: 0.4375rem 0 0;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.ad-row__marks:empty { display: none; }
.ad-row__meta > span + span,
.ad-row__marks > span + span {
  padding-left: 0.75rem;
  border-left: 1px solid rgba(255, 255, 255, 0.18);
}

.ad-mark--on { color: var(--gold-lift); }
.ad-mark--news { color: var(--stone); }
.ad-mark--off,
.ad-mark--edit { color: rgba(255, 255, 255, 0.38); }
.ad-mark--cancelled { color: #E2A08A; }
/* Not attending. Distinct from cancelled — this is a live registration and a
   real contact, it is simply not a seat. Stone rather than the warning tone. */
.ad-mark--remote { color: var(--stone); }
/* Spam flags. Amber, not red: nothing here was refused, and a red chip on a row
   that was accepted reads as an error the organiser has to clear. */
.ad-mark--flag { color: #E0B361; }

/* Inbox rows — questions from /ask, partner inquiries from /support. Same row
   grammar as an attendee, with one addition: the message gets running type,
   because unlike every other field on this page it is the thing you came to
   read rather than scan. Capped at a comfortable measure so a long question
   does not run the full width of a desktop screen. */
.ad-row__msg {
  margin: 0.375rem 0 0;
  max-width: 62ch;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--paper-lede);
}

.ad-row__contact { display: flex; flex-wrap: wrap; gap: 0.125rem 1rem; }
.ad-row__contact a {
  display: inline-block;
  padding-block: 0.25rem;             /* keeps the tap target over 24px */
  font-size: 0.875rem;
  color: var(--gold-lift);
}

/* ---- actions: quiet until needed ---- */

/* ---- state and actions: the control IS the readout -------------------- */

.ad-row__state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.125rem;
  margin-top: 0.25rem;
}

.ad-seg { display: flex; align-items: center; gap: 0.5rem; }
.ad-seg__label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}
.ad-seg__group { display: inline-flex; }

/* Unlit segments sit close to the ground so the lit one is the only thing the
   eye catches. Scanning a column of these reads as a pattern of WHERE the lit
   segment sits, which is the whole point — position carries the state. */
.ad-seg__opt {
  min-height: 34px;
  padding: 0 0.625rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0;
  background: none;
  color: rgba(255, 255, 255, 0.5);
  font: inherit;
  font-size: 0.8125rem;
  line-height: 1;
  cursor: pointer;
  transition: color 120ms var(--ease-out), border-color 120ms var(--ease-out);
}
.ad-seg__opt + .ad-seg__opt { border-left: 0; }
.ad-seg__opt:hover { color: var(--paper-hi); border-color: rgba(255, 255, 255, 0.45); }
.ad-seg__opt:focus-visible { outline: 2px solid var(--gold-lift); outline-offset: 2px; z-index: 1; }

/* Lit. Directory gold, updates stone — the two opt-ins already answered
   different questions in different colours, and keeping that means the two
   controls are told apart before either is read. */
.ad-seg__opt[aria-pressed="true"] {
  border-color: var(--gold);
  background: rgba(162, 132, 85, 0.22);
  color: var(--gold-lift);
  font-weight: 600;
}
.ad-seg--news .ad-seg__opt[aria-pressed="true"] {
  border-color: rgba(201, 207, 218, 0.7);
  background: rgba(201, 207, 218, 0.16);
  color: var(--paper-hi);
}

.ad-row__ops { display: flex; gap: 0.5rem; }
.ad-op { display: inline-flex; }
.ad-op__go {
  min-height: 34px;
  padding: 0 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 0;
  background: none;
  color: rgba(255, 255, 255, 0.62);
  font: inherit;
  font-size: 0.8125rem;
  line-height: 1;
  cursor: pointer;
  transition: color 120ms var(--ease-out), border-color 120ms var(--ease-out);
}
.ad-op__go:hover { color: var(--paper-hi); border-color: rgba(255, 255, 255, 0.5); }
.ad-op__go:focus-visible { outline: 2px solid var(--gold-lift); outline-offset: 2px; }
/* The one destructive control. Warm rather than red: it is reversible in one
   tap and nothing is deleted, so it does not deserve an alarm. */
.ad-op__go--warn:hover { color: #E2A08A; border-color: #E2A08A; }

/* Wide enough for the row to become three columns: who, how to reach them,
   and what state they are in. The state column is last because it is the one
   you act on, not the one you scan. */
@media (min-width: 60rem) {
  .ad-row {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    column-gap: clamp(1rem, 2.5vw, 2rem);
  }
  .ad-row__state { grid-column: 1 / -1; }
}

.ad__who {
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.38);
}

/* Above a phone the row becomes three columns: identity, contact, actions. */
@media (min-width: 46rem) {
  .ad-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 14rem) minmax(0, 7rem);
    align-items: start;
    gap: 0.5rem 1.5rem;
  }
  .ad-row__contact { flex-direction: column; gap: 0; }
  /* Its own full-width row under the identity and the address. As the third
     grid child it would otherwise drop into the 7rem actions column and set
     a two-word ragged column down the side of the page. The empty actions
     column it leaves on inbox rows is deliberate — it keeps the email column
     landing in the same place as the attendee rows above. */
  .ad-row__msg { grid-column: 1 / -1; }
}
