/* ==========================================================================
   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:        #0B0F14;   /* deep charcoal-navy */
  --ink-soft:   #14181D;   /* text on bronze */
  --scrim:        9, 13, 18;

  /* Light */
  --paper:      #F6F4F0;   /* warm off-white */
  --paper-hi:   #FBFAF8;   /* display type */
  --paper-lede: #EFECE6;   /* body over photograph — solid, never translucent */
  --stone:      #E8E1D2;   /* eyebrow and hairlines over photograph */

  /* Accent — used sparingly: button fill and focus ring, nothing else.
     Bronze is deliberately absent from type set over the photograph; at
     #BE9553 it only reaches ~2.5:1 against the scrimmed water. */
  --bronze:      #BE9553;
  --bronze-lift: #CFA662;

  --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(--bronze-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(11, 15, 20, 0.94);
  border-bottom-color: rgba(246, 244, 240, 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(246, 244, 240, 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(246, 244, 240, 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 bronze and the label cannot drop under its 6.46: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);                        /* 6.46:1 on bronze */
  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(--bronze);
  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(--bronze-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 bronze rather than over the page. At 55% it came out
      rgb(91,74,47) — muddy bronze 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 bronze 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 bronze rule and bronze label;
   above it, it reads as a dark plate with a bronze edge. Either way the label
   has a fixed 7.09:1 ground that does not depend on the photograph. */
.btn--outline {
  color: var(--bronze);                          /* 7.09:1 on --ink */
  transition: color 160ms var(--ease-out);
}
.btn--outline::before { background: var(--bronze); }
.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(--bronze-lift); }
.btn--outline:hover::after { background-color: var(--bronze-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: 28% 30%;   /* water, treeline and the road curve 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(246, 244, 240, 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(246, 244, 240, 0.62);
  font-size: 0.875rem;
  font-weight: 400;
}

/* 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(246, 244, 240, 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(246, 244, 240, 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%;
  }
  /* Left-aligned, not centred. On tall viewports cover crops horizontally, and
     centring throws away the left edge — the water drops from 33% of the frame
     to 27% and the treeline slides under the headline. Left-aligned keeps the
     whole water column at every aspect ratio. */
  .hero__media img { object-position: left center; }

  .hero::before {
    inset: 0;
    height: auto;
    z-index: -1;
    background:
      /* header ramp — the top of this crop is bright frost (p99.5 ~213) */
      linear-gradient(180deg,
        rgba(var(--scrim), 0.72) 0%,
        rgba(var(--scrim), 0.68) 8%,
        rgba(var(--scrim), 0.46) 13%,
        rgba(var(--scrim), 0.14) 19%,
        rgba(var(--scrim), 0) 26%),
      /* treeline knockdown — carries the headline's tail across the frost */
      linear-gradient(90deg,
        rgba(var(--scrim), 0) 12%,
        rgba(var(--scrim), 0.12) 24%,
        rgba(var(--scrim), 0.34) 30%,
        rgba(var(--scrim), 0.44) 36%,
        rgba(var(--scrim), 0.50) 42%,
        rgba(var(--scrim), 0.50) 47%,
        rgba(var(--scrim), 0.24) 58%,
        rgba(var(--scrim), 0) 70%),
      /* bed — low and even, so the water keeps its tonal range */
      linear-gradient(90deg,
        rgba(var(--scrim), 0.32) 0%,
        rgba(var(--scrim), 0.32) 20%,
        rgba(var(--scrim), 0.26) 40%,
        rgba(var(--scrim), 0.14) 55%,
        rgba(var(--scrim), 0) 70%);
  }

  .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);
    /* Small type crosses the full width here, including the brightest frost in
       the file (sRGB 254), so the rail gets its own ground rather than trusting
       the scrim. Solid, with the transition band above the text — not a
       gradient running through it. */
    background: rgba(var(--scrim), 0.94);
  }
  .hero .factrail::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: clamp(4rem, 9vw, 7rem);
    pointer-events: none;
    background: linear-gradient(180deg,
      rgba(var(--scrim), 0) 0%,
      rgba(var(--scrim), 0.55) 62%,
      rgba(var(--scrim), 0.94) 100%);
  }
}


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

   ONE LEFT EDGE. Every horizontal measurement on this page comes from
   --gutter — the logo, the hero headline, every section, the footer credits.
   There is no centred container anywhere: a centred max-width adds margin on
   top of the gutter and the two systems drift apart (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. #6B5836 is 6.6:1 on --paper. */
.eyebrow--dark { color: #6B5836; }
.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(246, 244, 240, 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(246, 244, 240, 0.16);
}
.steps__item {
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
  border-bottom: 1px solid rgba(246, 244, 240, 0.16);
}
.steps__num {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--bronze);
  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;
  align-items: 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(11, 15, 20, 0.16);
}

/* 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. */

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

/* Contributors and Supporters side by side: they are parallel categories, not
   a ranking, and stacking them implies one outranks the other. Side by side
   only once each column can still hold a logo cell without crowding. */
@media (min-width: 60rem) {
  .tiers {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(2rem, 4vw, 3.5rem);
    align-items: start;
  }
  .tiers .tier + .tier { 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;
}

/* 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: clamp(1.75rem, 3.2vw, 2.5rem); }
.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; }

.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; }

.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);
}
.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 #6B5836; 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: #6B5836;
}
.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(246, 244, 240, 0.16);
}
.groups__item { padding-block: clamp(1rem, 2vw, 1.375rem); }
.groups__item + .groups__item { border-top: 1px solid rgba(246, 244, 240, 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(246, 244, 240, 0.62);
}

.agenda {
  margin: clamp(1.75rem, 4vw, 2.75rem) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid rgba(246, 244, 240, 0.16);
}
.agenda__slot {
  padding-block: clamp(1.125rem, 2.4vw, 1.75rem);
  border-bottom: 1px solid rgba(246, 244, 240, 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(--bronze-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(11, 15, 20, 0.16); max-width: 62rem; }
.faq__item { border-bottom: 1px solid rgba(11, 15, 20, 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 #6B5836; 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: #6B5836;
  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(11, 15, 20, 0.16);
}
.rsvp__fact dt {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #6B5836;
}
.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 bronze fill only reaches 2.6:1 against
   --paper and needs a dark rim to hold the 3:1 a control boundary requires.
   -------------------------------------------------------------------------- */

.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(11, 15, 20, 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(11, 15, 20, 0.48); }
.field__input:focus {
  outline: none;
  border-color: #6B5836;
  box-shadow: inset 0 0 0 1px #6B5836;
}
.field__input:focus-visible { outline: 2px solid #6B5836; 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: #6B5836; }
/* /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: #6B5836; }

.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: #6B5836;
}
.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(11, 15, 20, 0.16);
}

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

.form__submit { width: 100%; }
@media (min-width: 480px) { .form__submit { width: auto; } }
.form__submit[disabled] { opacity: 0.6; cursor: progress; }
/* Darker rim on paper: the bronze face alone is 2.6:1 against --paper, under
   the 3:1 WCAG 1.4.11 asks of a control boundary. */
.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(246, 244, 240, 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(11, 15, 20, 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(--bronze-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(246, 244, 240, 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(246, 244, 240, 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(246, 244, 240, 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(246, 244, 240, 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(246, 244, 240, 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(246, 244, 240, 0.12);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem clamp(2rem, 5vw, 4rem);
  justify-content: space-between;
}
.site-footer__event {
  margin: 0;
  font-size: 0.875rem;
  color: var(--paper-lede);
}
.site-footer__contact {
  margin: 0;
  font-size: 0.875rem;
  color: rgba(246, 244, 240, 0.62);
}
.site-footer__contact a {
  display: inline-block;
  padding-block: 0.25rem;             /* 17px line box was under the 24px minimum */
  color: var(--bronze-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. */
.site-footer__routes { display: grid; gap: 0.125rem; }

.site-footer__credits {
  color: rgba(246, 244, 240, 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 door 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(246, 244, 240, 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(--bronze-lift);
}
.ad__count-label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 244, 240, 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(246, 244, 240, 0.16);
}
.ad-rail__label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 244, 240, 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(246, 244, 240, 0.62);
}

.ad__banner {
  margin: 0 0 1.25rem;
  padding: 0.75rem 0.9375rem;
  border-left: 2px solid var(--bronze);
  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(246, 244, 240, 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(246, 244, 240, 0.45); }
.ad__search:focus-visible { outline: 2px solid var(--bronze-lift); outline-offset: 2px; }
.ad__csv {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 1rem;
  border: 1px solid rgba(246, 244, 240, 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(--bronze); color: var(--bronze-lift); }
.ad__csv:focus-visible { outline: 2px solid var(--bronze-lift); outline-offset: 2px; }

.ad__status:empty { display: none; }
.ad__status { margin: 0 0 0.5rem; font-size: 0.8125rem; color: rgba(246, 244, 240, 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(246, 244, 240, 0.62);
}
.ad__h span { font-variant-numeric: tabular-nums; color: var(--paper-lede); }
.ad__h--muted { color: rgba(246, 244, 240, 0.38); }

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

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

.ad-row {
  display: grid;
  gap: 0.5rem;
  padding: 1rem 0;
  border-top: 1px solid rgba(246, 244, 240, 0.12);
}
.ad-row.is-cancelled .ad-row__name { color: var(--paper-lede); font-weight: 500; }

/* 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);
}

/* 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(246, 244, 240, 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(246, 244, 240, 0.18);
}

.ad-mark--on { color: var(--bronze-lift); }
.ad-mark--news { color: var(--stone); }
.ad-mark--off,
.ad-mark--edit { color: rgba(246, 244, 240, 0.38); }
.ad-mark--cancelled { color: #E2A08A; }

/* 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(--bronze-lift);
}

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

.ad-manage { justify-self: start; }
.ad-manage > summary {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 0.5rem 0 0;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 244, 240, 0.45);
  cursor: pointer;
  list-style: none;
}
.ad-manage > summary::-webkit-details-marker { display: none; }
.ad-manage > summary::after { content: " +"; }
.ad-manage[open] > summary { color: var(--bronze-lift); }
.ad-manage[open] > summary::after { content: " –"; }
.ad-manage > summary:hover { color: var(--paper-lede); }
.ad-manage > summary:focus-visible { outline: 2px solid var(--bronze-lift); outline-offset: 2px; }

.ad-manage__body {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.625rem;
  padding: 0.875rem;
  border-left: 2px solid rgba(246, 244, 240, 0.16);
  background: rgba(255, 255, 255, 0.03);
}

.ad-act > summary {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 0 0.6875rem;
  border: 1px solid rgba(246, 244, 240, 0.24);
  font-size: 0.8125rem;
  color: var(--paper-lede);
  cursor: pointer;
  list-style: none;
  white-space: nowrap;
}
.ad-act > summary::-webkit-details-marker { display: none; }
.ad-act > summary:hover { border-color: rgba(246, 244, 240, 0.5); color: var(--paper-hi); }
.ad-act > summary:focus-visible { outline: 2px solid var(--bronze-lift); outline-offset: 2px; }
.ad-act[open] { flex-basis: 100%; }
.ad-act[open] > summary { border-color: var(--bronze); color: var(--bronze-lift); }

.ad-act__confirm { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 0.75rem; margin-top: 0.5rem; }
.ad-act__go {
  min-height: 38px;
  padding: 0 0.875rem;
  border: 0;
  background: var(--bronze);
  color: var(--ink-soft);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.ad-act__go:hover { background: var(--bronze-lift); }
.ad-act__go:focus-visible { outline: 2px solid var(--bronze-lift); outline-offset: 2px; }
.ad-act__why { font-size: 0.75rem; color: rgba(246, 244, 240, 0.55); }

.ad__who {
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
  font-size: 0.75rem;
  color: rgba(246, 244, 240, 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; }
  .ad-manage { justify-self: end; }
  .ad-manage__body { grid-column: 1 / -1; }
  .ad-manage[open] { grid-column: 1 / -1; justify-self: stretch; }
}
