/* ============================================================
   TargetHR v4 — v3 on the yellow pairing, with a global claim and a map.

   Act 1  proof of clients     white
   Act 2  what they say        white, with two tinted territories
   Act 3  team, map, numbers   white, one screen, on an indigo map panel
          and the ask            and the dark ask beneath it

   The page stays white throughout and colour does the signalling: indigo for
   the client side, the guide's yellow for the candidate side, and deep indigo
   only for panels inside Act 3 — the map, and the CTA at the very bottom. An
   earlier draft darkened the ground act by act, but the jump to a full indigo
   screen read as an unexplained change of scene rather than as an arrival.
   ============================================================ */

/* ============================================================
   [ASSET: palette] — PLACEHOLDER VALUES.
   Derived by sampling the style guide's artwork, NOT read from its swatch
   page. Replace the twelve values in this block with the real hexes and the
   rest of the file follows — nothing below hard-codes a colour.
   ============================================================ */
:root {
  --indigo-900: #1B2050;   /* deepest fill — Act 3 ground */
  --indigo-700: #2E3192;   /* brand indigo, matches the logo mark */
  --indigo-500: #4A55B8;   /* interactive / hover */
  --indigo-300: #AAB1D4;   /* soft */
  --indigo-100: #D4D8EA;   /* wash */
  --indigo-050: #EEF0F8;   /* Act 2 ground */
  /* The candidate side of Act 2 — the guide's pale yellow. The ground is a
     light wash of it, and the text colour is that hue taken well down so it
     carries small type on white; a pale yellow cannot, and a yellow dark
     enough to try is necessarily olive — that is what dark yellow is, not a
     colour choice. Shades of a brand hue are still the brand colour; the brown
     that once sat here was not in the guide at all.
     Swap the whole ramp with ?theme= — see the blocks below. */
  --pair-700:   #797115;   /* text-safe on white, 5.0:1 */
  --pair-500:   #C9B735;
  --pair-200:   #EFE4A6;
  --pair-050:   #FCF9E8;
  /* The media flag. Yellow is already the accent, so on this ramp the flag is
     indigo — left yellow it would vanish into the candidate panel it sits on. */
  --flag:       var(--indigo-700);
  --flag-ink:   #fff;

  --yellow:     #F7EFB3;   /* the guide's pale yellow */
  /* Same hue as --yellow, taken down in lightness so it can hold its own
     against white on a graphic element. Previously this was a mixed yellow at
     a different hue, which read as a second, unrelated yellow. */
  --yellow-deep:#B3A02E;

  --ink: #1E2233;
  --ink-soft: #5A6076;
  --paper: #FFFFFF;
  --paper-2: #F6F6FA;
  --hairline: rgba(30, 34, 51, 0.13);
  --hairline-dark: rgba(255, 255, 255, 0.16);

  /* Act 2's walls. One tile, two courses of bricks: a faint ink wash for the
     mortar, then white bricks at slightly different strengths so the wall
     has some grain. Neutral on purpose, so it sits on either side's tint and
     on every ?theme= pairing without a second version. */
  --brick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='48'%3E%3Crect width='120' height='48' fill='rgba(27,32,80,0.055)'/%3E%3Cg fill='%23fff'%3E%3Crect x='1.5' y='1.5' width='57' height='21' rx='2.5' fill-opacity='.62'/%3E%3Crect x='61.5' y='1.5' width='57' height='21' rx='2.5' fill-opacity='.44'/%3E%3Crect x='-28.5' y='25.5' width='57' height='21' rx='2.5' fill-opacity='.5'/%3E%3Crect x='91.5' y='25.5' width='57' height='21' rx='2.5' fill-opacity='.5'/%3E%3Crect x='31.5' y='25.5' width='57' height='21' rx='2.5' fill-opacity='.68'/%3E%3C/g%3E%3C/svg%3E");

  --font-display: "Space Grotesk", "Helvetica Neue", sans-serif;
  --font-body: "Inter Tight", "Helvetica Neue", sans-serif;
  --pad-x: clamp(1.25rem, 4vw, 4.5rem);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

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

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

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

.btn {
  position: relative;   /* the sticker hangs off the button, not the section */
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 48px;
  padding: 0.7rem 1.5rem;
  background: var(--indigo-700);
  color: #fff;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  border-radius: 999px;
  transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover { background: var(--indigo-500); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn--lg { min-height: 56px; padding: 0.9rem 2rem; font-size: 1.0625rem; }
.btn--small { min-height: 42px; padding: 0.45rem 1.15rem; font-size: 0.9375rem; }
.btn--ghost {
  background: transparent; color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--hairline);
}
.btn--ghost:hover { background: rgba(30, 34, 51, 0.05); color: var(--ink); }
.btn--light { background: var(--yellow); color: var(--indigo-900); }
.btn--light:hover { background: #fff; }
.btn--outline {
  background: transparent; color: #fff;
  box-shadow: inset 0 0 0 1.5px var(--hairline-dark);
}
.btn--outline:hover { background: rgba(255, 255, 255, 0.1); }
/* The tailored deck: the guide's yellow, so it reads as its own offer
   rather than the lesser of two buttons. */
.btn--accent {
  background: var(--yellow);
  color: var(--indigo-900);
  box-shadow: inset 0 0 0 1.5px rgba(179, 160, 46, 0.45), 0 10px 26px rgba(179, 160, 46, 0.22);
}
.btn--accent:hover { background: #FBF5CC; }
.btn__icon { flex-shrink: 0; margin-left: -0.2rem; }
/* a sticker on the corner: the one thing other agencies don't offer */
.btn__meta { font-size: 0.72em; font-weight: 400; opacity: 0.65; letter-spacing: 0.08em; }

.chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: 0.73rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--indigo-700);
  background: rgba(46, 49, 146, 0.08);
  border: 1px solid rgba(46, 49, 146, 0.32);
  border-radius: 999px;
  padding: 0.3rem 0.9rem;
}
.chip__icon { flex-shrink: 0; margin-right: 0.4rem; vertical-align: -1px; }

/* The "there is more" mark: a chevron in a circle, sitting on the corner of
   whatever is holding text back. It turns over when the text is open, and
   it is only shown where something is actually cut off — and only on a
   phone, since nothing is clamped on a desktop. */
.more {
  position: absolute;
  right: 0.55rem;
  bottom: 0.55rem;
  display: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--indigo-050);
  color: var(--indigo-700);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--indigo-100);
  transition: transform 0.25s ease, background 0.2s ease;
}
.more[hidden] { display: none; }
.more:hover { background: var(--indigo-100); }
.bubble--candidate .more { background: var(--pair-050); color: var(--pair-700); box-shadow: inset 0 0 0 1px var(--pair-200); }
.is-open > .more,
.is-open .member__plate > .more { transform: rotate(180deg); }
@media (max-width: 620px) {
  .group__rail .bubble > .more:not([hidden]),
  .member__plate > .more:not([hidden]) { display: flex; }
}

/* ---------- Nav ---------- */

.nav {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem var(--pad-x);
}
.nav__logo img { width: clamp(120px, 14vw, 160px); height: auto; }

/* ---------- Act scaffolding ---------- */

.act { position: relative; }

/* The step marks. These used to be a line of small grey capitals, which read
   as a label on a section rather than as one of three steps — so the shape of
   the page, which is the whole argument, was invisible. The number is now the
   size of a heading, with the count beside it, and the label sits under it. */
.act__eyebrow {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--indigo-700);
  margin-bottom: clamp(0.75rem, 2vh, 1.1rem);
}
.act__num {
  display: flex;
  justify-content: center;
  align-items: baseline;
  width: fit-content;
  margin: 0 auto 0.3rem;
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--indigo-900);
  /* the guide's yellow, struck under the numeral like the headline's */
  background: linear-gradient(var(--yellow), var(--yellow)) no-repeat 0 82% / 100% 0.3em;
  padding: 0 0.12em;
}

.act__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 46rem;
  margin: 0 auto;
}
.act__title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.4vw, 3.4rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.04;
}
.act__sub { margin-top: 0.85rem; color: var(--ink-soft); max-width: 34rem; }

/* The scroll cue. A wide labelled pill sat straight in the orbit's path, so
   this is the same link reduced to a mark: the number of the step it leads to
   and an arrow, small enough to read as punctuation rather than furniture. */
.cue {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  width: fit-content;
  margin: clamp(1.5rem, 4vh, 2.5rem) auto 0;
  text-decoration: none;
  color: var(--ink-soft);
  transition: color 0.2s ease;
}
.cue:hover { color: var(--indigo-700); }
.cue__num {
  font-family: var(--font-display);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  opacity: 0.6;
}
.cue__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.75);
  box-shadow: inset 0 0 0 1px var(--hairline);
  animation: nudge 2.6s ease-in-out infinite;
}
.cue:hover .cue__arrow { background: #fff; box-shadow: inset 0 0 0 1px var(--indigo-300); }
@keyframes nudge {
  0%, 74%, 100% { transform: translateY(0); }
  86% { transform: translateY(3px); }
}
@media (prefers-reduced-motion: reduce) { .cue__arrow { animation: none; } }

/* ============================================================
   ACT 1 — proof of clients
   ============================================================ */

.act--1 {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: clamp(5rem, 10vh, 7rem) var(--pad-x) clamp(2rem, 5vh, 3rem);
  background:
    radial-gradient(70% 55% at 50% 42%, #fff 0%, rgba(255, 255, 255, 0) 72%),
    radial-gradient(120% 70% at 50% 108%, rgba(46, 49, 146, 0.07), rgba(46, 49, 146, 0) 66%),
    var(--paper-2);
}

.hero__orbit { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.hero__inner { position: relative; z-index: 2; max-width: 56rem; text-align: center; }

.hero__title {
  font-family: var(--font-display);
  /* sized so the line breaks fall where the sentence does: the promise,
     then who it is for, then the noun */
  font-size: clamp(2.1rem, 4.6vw, 3.9rem);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.035em;
}
.type { display: grid; }
.type__ghost, .type__live { grid-area: 1 / 1; }
.type__ghost { visibility: hidden; }
/* "Global Seed to Series C" is one idea, so the line break falls before it
   rather than inside it — except on a phone, where the phrase is wider than
   the screen and holding it together would push the page sideways. */
.type__nb { white-space: nowrap; }
@media (max-width: 620px) { .type__nb { white-space: normal; } }
.type__c { visibility: hidden; }
.type__c--on { visibility: visible; }
.type__caret {
  display: inline-block; position: relative; width: 0;
  animation: caret-blink 1.05s steps(1, end) infinite;
}
.type__caret::after {
  content: ""; position: absolute; left: 0.04em; bottom: -0.06em;
  width: 0.055em; height: 0.74em; background: var(--indigo-700);
}
.type__caret--done { animation-duration: 1.4s; opacity: 0.55; }
@keyframes caret-blink {
  0%, 45% { opacity: 1; } 50%, 95% { opacity: 0; } 100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .type__caret { display: none; } }

/* "grow" is the promise, so it is the word picked out, with the guide's
   yellow drawn under it like a highlighter once the line has finished
   typing. Same ink as the rest of the line — the stroke does the work. */
.hero__em {
  background: linear-gradient(var(--yellow), var(--yellow)) no-repeat 0 88% / 0% 0.36em;
  padding: 0 0.04em;
  margin: 0 -0.04em;
}
.js body.is-typed .type__live .hero__em {
  background-size: 100% 0.36em;
  transition: background-size 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.15s;
}
html:not(.js) .hero__em { background-size: 100% 0.36em; }
@media (prefers-reduced-motion: reduce) { .js body.is-typed .type__live .hero__em { transition: none; } }

.hero__sub { max-width: 34rem; margin: 1rem auto 0; color: var(--ink-soft); }
.hero__ctas { margin-top: 1.6rem; padding: 0 1.25rem 0.2rem; }
/* The row is an inline-block (the reveal animation needs it), so the buttons
   sit on a text baseline — and the icon in one of them moves that baseline,
   which left the pair a few pixels out of line. Aligned by their middles
   instead. */
.hero__ctas .btn { vertical-align: middle; }
.hero__ctas > span { display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 0.75rem; }
.hero__note { margin-top: 1rem; font-size: 0.85rem; color: var(--ink-soft); }
.hero__note a { color: var(--indigo-700); text-decoration-color: rgba(46, 49, 146, 0.35); }
.hero__note a:hover { color: var(--indigo-500); }

.reveal-line { display: block; overflow: hidden; }
.reveal-line > span { display: inline-block; }
.js .reveal-line > span { transform: translateY(115%); opacity: 0; }
.js body.is-loaded .act__eyebrow > span,
.js body.is-typed .reveal-line > span {
  transform: translateY(0);
  opacity: 1;
  transition: transform 0.85s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.6s ease;
  transition-delay: var(--reveal-delay, 0s);
}
.act__eyebrow > span { --reveal-delay: 0.05s; }
.hero__sub > span { --reveal-delay: 0.14s; }
.hero__ctas > span { --reveal-delay: 0.26s; }
.hero__note > span { --reveal-delay: 0.34s; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal-line > span { transform: none; opacity: 1; transition: none; }
}

/* ============================================================
   THE LOGO RIBBONS (narrow screens only)
   The orbit ring is sized to the viewport, so on a phone it drives capsules
   straight through the headline. Below ORBIT_MIN_W the canvas is dropped and
   these two ribbons run above and below the copy instead: the logos get a
   full-bleed band each, and the text gets a clear zone with nothing crossing
   it. Pure CSS marquee, so it costs no frames.
   ============================================================ */

.ribbon { display: none; }
.js .act--1.is-ribboned .ribbon {
  display: block;
  position: relative;
  z-index: 2;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  overflow: hidden;
  /* fade the ends so capsules arrive and leave rather than popping */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.js .act--1.is-ribboned .ribbon--top { margin-bottom: clamp(1.25rem, 4vh, 2rem); }
.js .act--1.is-ribboned .ribbon--bottom { margin-top: clamp(1.25rem, 4vh, 2rem); }

.ribbon__track {
  display: flex;
  width: max-content;
  gap: 0.5rem;
  animation: ribbon-run 42s linear infinite;
}
.ribbon--bottom .ribbon__track { animation-duration: 54s; animation-direction: reverse; }
.ribbon:hover .ribbon__track { animation-play-state: paused; }
@keyframes ribbon-run {
  /* the roster is rendered twice, so one full list of travel loops seamlessly */
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) { .ribbon__track { animation: none; } }

.ribbon__item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-shrink: 0;
  padding: 0.34rem 0.8rem 0.34rem 0.34rem;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 4px 14px rgba(27, 32, 80, 0.08), inset 0 0 0 1px var(--hairline);
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--ink-soft);
  white-space: nowrap;
}
.ribbon__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  overflow: hidden;
  color: #fff;
  font-size: 0.72rem;
}
.ribbon__mark img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }

/* Static client capsules — no-JS and reduced-motion fallback */
.hero__clients {
  position: relative; z-index: 2;
  display: none;
  list-style: none;
  align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 0.6rem; max-width: 58rem; margin-top: 2.5rem;
}
.hero__clients li {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.4rem 0.9rem 0.4rem 0.4rem;
  border-radius: 999px;
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-family: var(--font-display); font-size: 0.8125rem; font-weight: 500;
  color: var(--ink-soft);
}
.hero__clients .client-mark {
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; width: 26px; height: 26px;
  border-radius: 8px; overflow: hidden; color: #fff; font-size: 0.7rem;
}
.hero__clients .client-mark img { width: 100%; height: 100%; object-fit: contain; }
.hero__clients .client-more {
  padding-left: 0.9rem; background: transparent;
  box-shadow: inset 0 0 0 1px var(--hairline); color: var(--ink-soft);
}
html:not(.js) .hero__clients { display: flex; }
html:not(.js) .hero__orbit { display: none; }
@media (prefers-reduced-motion: reduce) {
  .hero__clients { display: flex; }
  .hero__orbit { display: none; }
}


/* ============================================================
   THEME SWITCH — ?theme=violet|slate|warm on the URL.
   Scoped to any [data-theme] element rather than <html>, so compare.html can
   put the pairings side by side on one page.
   Only the second side of Act 2 changes; indigo and the yellow accent stay
   put. Default (no parameter) is the guide yellow, in the :root block at the
   top. The media flag only steps off the accent when the candidate side is
   itself yellow, so every ramp here puts it back.
   This exists so the pairing can be compared in the browser rather than
   argued about in the abstract.
   ============================================================ */

/* The guide's own violet (h~258). Same treatment: the ground is the guide's
   light value and the text colour is the same hue taken darker, because the
   guide's mid-tones do not carry small text on white. */
[data-theme="violet"] {
  --pair-700: #55458A;   /* 7.4:1 on white */
  --pair-500: #7B6B9E;   /* the guide's own violet */
  --pair-200: #CFC7DE;
  --pair-050: #F2F0F7;
  --flag:     var(--yellow-deep);
  --flag-ink: var(--indigo-900);
}

/* The guide's slate-teal, same treatment: its own mid value, plus a darker
   shade of the same hue for text. */
[data-theme="slate"] {
  --pair-700: #3D6B7C;   /* 5.6:1 on white */
  --pair-500: #6E9AAB;   /* the guide's own slate */
  --pair-200: #C7DAE1;
  --pair-050: #EDF4F7;
  --flag:     var(--yellow-deep);
  --flag-ink: var(--indigo-900);
}

/* The warm pairing that was here before. Kept only for comparison — the hue
   is not in the style guide. */
[data-theme="warm"] {
  --pair-700: #9C5410;
  --pair-500: #C4791F;
  --pair-200: #EBCFA3;
  --pair-050: #FBF3E6;
  --flag:     var(--yellow-deep);
  --flag-ink: var(--indigo-900);
}

/* ============================================================
   ACT 2 — the testimonial wall.

   Two big tinted territories with a field of small bubbles drifting over
   them. The bubble carries a hook at rest and opens to the whole quote, which
   is what keeps the bubbles small and the field light while still holding a
   three or four sentence testimonial.

   JS owns transform, opacity, z-index, --bubble-w and --bubble-w-open, and
   measures the panels themselves for its geometry — there are no shared
   magic numbers between this file and initWall. Below WALL_MIN_W, and with
   JS off or reduced motion on, the rules here lay the same bubbles out as a
   plain grid with the full quotes showing.
   ============================================================ */

.act--2 {
  padding: clamp(4rem, 9vh, 6.5rem) var(--pad-x) clamp(2.5rem, 6vh, 4rem);
  background:
    radial-gradient(90% 60% at 50% 0%, #fff, rgba(255, 255, 255, 0) 72%),
    var(--paper-2);
}
.js .wall--live {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

/* ----- the two territories ----- */

/* Fallback (mobile, no-JS, reduced motion): the panel keeps its heading but
   loses its ground, and sits in flow ahead of the group it names. Hiding it
   outright took the only client/candidate labelling off small screens. */
.panel {
  grid-column: 1 / -1;
  padding: 0;
  background: none;
  box-shadow: none;
}
.panel + .bubble { margin-top: 0.15rem; }
.panel:not(:first-child) { margin-top: 1.75rem; }

.js .wall--live .panel {
  display: block;
  position: absolute;
  grid-column: auto;
  margin-top: 0;
  top: 0;
  bottom: 0;
  width: calc(50% - 1.25rem);
  border-radius: 34px;
  padding: clamp(1.4rem, 2.2vw, 2rem);
}
/* A wall has no frame: the tint and the bricks live on a layer behind the
   panel that is masked off towards its edges, so each side fades into the
   page like a stretch of wall rather than sitting in a ruled box. */
.js .wall--live .panel { isolation: isolate; }
.js .wall--live .panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* two ramps, one per axis, intersected: every edge feathers out */
  -webkit-mask-image:
    linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent),
    linear-gradient(180deg, transparent, #000 9%, #000 86%, transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent),
    linear-gradient(180deg, transparent, #000 9%, #000 86%, transparent);
  mask-composite: intersect;
}
.js .wall--live .panel--client { left: 0; }
.js .wall--live .panel--client::before {
  background:
    radial-gradient(120% 80% at 12% 0%, rgba(46, 49, 146, 0.10), rgba(46, 49, 146, 0) 62%),
    var(--brick) 0 0 / 120px 48px,
    var(--indigo-050);
}
.js .wall--live .panel--candidate { right: 0; }
.js .wall--live .panel--candidate::before {
  background:
    radial-gradient(120% 80% at 88% 0%, rgba(156, 84, 16, 0.10), rgba(156, 84, 16, 0) 62%),
    var(--brick) 60px 0 / 120px 48px,
    var(--pair-050);
}

/* the heading sits on a plaque of plain plaster so it reads over the bricks */
.js .wall--live .panel__head {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  padding: 0.7rem 1.1rem 0.7rem 0.7rem;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.82);
  box-shadow: 0 6px 18px rgba(27, 32, 80, 0.06), inset 0 0 0 1px rgba(255, 255, 255, 0.9);
}
.panel__head { display: grid; grid-template-columns: auto 1fr; column-gap: 0.7rem; align-items: center; }
.panel__mark {
  grid-row: 1 / 4;
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px;
  border-radius: 13px;
  color: #fff;
}
.panel--client .panel__mark { background: var(--indigo-700); }
.panel--candidate .panel__mark { background: var(--pair-700); }
.panel__title {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 1.7vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
}
.panel--client .panel__title { color: var(--indigo-900); }
.panel--candidate .panel__title { color: var(--pair-700); }
/* the nameplate on each wall */
.panel__tag {
  justify-self: start;
  margin-bottom: 0.25rem;
  padding: 0.14rem 0.55rem;
  border-radius: 6px;
  font-family: var(--font-display);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #fff;
  line-height: 1.5;
}
.panel--client .panel__tag { background: var(--indigo-700); }
.panel--candidate .panel__tag { background: var(--pair-700); }
.panel__note { font-size: 0.84rem; color: var(--ink-soft); line-height: 1.4; }

/* A group is only a wrapper on desktop — the panels are absolute and the
   bubbles are placed against .wall__field, so this must not become a
   containing block or a layout of its own. It earns its keep on mobile. */
.group { display: contents; }

/* ----- the field ----- */

/* Fallback: a plain grid with the whole quote showing. Mobile, no-JS and
   reduced motion get this, and nothing is hidden from them. */
.wall__field {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 0.9rem;
  max-width: 84rem;
  margin: 2rem auto 0;
}
.js .wall--live .wall__field {
  display: block;
  position: relative;
  flex: 1;
  width: 100%;
  max-width: 88rem;
  /* Four rows of bubbles per panel, each cell needing room for the bubble
     plus its drift envelope, plus the panel header. Below this the placement
     starts scaling bubbles down to fit and the type stops being readable —
     initWall shrinks rather than spills. Raise the bubble count and this has
     to rise with it. */
  min-height: 38rem;
  margin-top: clamp(1.75rem, 4vh, 2.5rem);
}

.bubble {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.js .wall--live .bubble {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--bubble-w, 220px);
  will-change: transform, opacity;
  /* 340ms, matched by OPEN_MS in initWall so the box and the transform
     it is driven by ease on the same clock */
  transition: width 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}


/* The mark is the first thing read on a bubble — whose quote is this — so it
   carries real weight rather than sitting as a token beside the text. */
.bubble__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
}
/* a company is a rounded square, a person is a circle */
.bubble--client .bubble__avatar {
  border-radius: 15px;
  background: #fff;
  box-shadow: 0 6px 16px rgba(27, 32, 80, 0.14), inset 0 0 0 1px var(--hairline);
}
.bubble--client .bubble__avatar img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }
.bubble--candidate .bubble__avatar {
  border-radius: 50%;
  background: var(--pair-050);
  color: #fff;
  box-shadow: 0 6px 16px rgba(27, 32, 80, 0.16), inset 0 0 0 1px var(--pair-200);
}
.bubble__avatar--person img { width: 100%; height: 100%; object-fit: cover; }
/* no photo yet: initials, on the side's own colour */
.bubble__avatar--initials { color: #fff; font-size: 1.05rem; letter-spacing: 0.02em; }
.bubble--client .bubble__avatar--initials { background: var(--indigo-700); }
.bubble--candidate .bubble__avatar--initials { background: var(--pair-700); box-shadow: 0 6px 16px rgba(27, 32, 80, 0.16); }


/* a quote with a recording behind it */
.bubble__flag {
  position: absolute;
  right: -5px;
  top: -5px;
  z-index: 4;
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--flag);
  color: var(--flag-ink);
  box-shadow: 0 3px 8px rgba(27, 32, 80, 0.24), 0 0 0 2px #fff;
}

.bubble__body {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.9rem 0.66rem;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 8px 22px rgba(27, 32, 80, 0.10);
  transition: box-shadow 0.25s ease;
}
.bubble--client .bubble__body { border-left: 3px solid var(--indigo-700); }
.bubble--candidate .bubble__body { border-left: 3px solid var(--pair-700); }

.bubble__hook,
.bubble__full {
  font-family: var(--font-display);
  font-size: 0.87rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}
.bubble__full { font-size: 0.85rem; line-height: 1.45; }
.bubble__hook::before, .bubble__full::before { content: "\201C"; }
.bubble__hook::after, .bubble__full::after { content: "\201D"; }

/* Two lengths of the same quote. The grid has room for all of it; a drifting
   bubble only for the hook, with the whole thing arriving when it opens. */
.bubble__hook { display: none; }
.js .wall--live .bubble__hook { display: block; }
.js .wall--live .bubble__full { display: none; }


.bubble__meta {
  margin-top: 0.4rem;
  font-family: var(--font-display);
  font-size: 0.73rem;
  font-weight: 600;
  line-height: 1.3;
}
/* the name on its own line, the title and company after it */
.bubble__who { display: block; font-weight: 700; }
.bubble__meta { font-weight: 500; }
.bubble--client .bubble__meta { color: var(--indigo-700); }
.bubble--candidate .bubble__meta { color: var(--pair-700); }

.js .wall--live .bubble.is-spot .bubble__body { box-shadow: 0 16px 34px rgba(27, 32, 80, 0.2); }

.bubble__open {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  color: transparent;
  cursor: pointer;
  border-radius: 18px;
  text-indent: -9999px;
  overflow: hidden;
  white-space: nowrap;
}
.bubble__open:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 4px; }
html:not(.js) .bubble__open { display: none; }

.wall__hint {
  display: none;
  margin-top: 1rem;
  text-align: center;
  font-family: var(--font-display);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  opacity: 0.7;
}
.js .wall--live .wall__hint { display: block; }


/* The marks: the person's face, with the company logo as a badge on its
   corner (clients). The face leads everywhere now — on the wall a quote is
   a person first. --mark sets the size per layout. */
.bubble__marks {
  --mark: 60px;
  position: relative;
  display: block;
  flex-shrink: 0;
  width: var(--mark);
  height: var(--mark);
}
.bubble__marks > .bubble__avatar,
.bubble__face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.bubble__face { display: block; overflow: hidden; border-radius: 50%; box-shadow: 0 8px 20px rgba(27, 32, 80, 0.2), 0 0 0 3px #fff; }
.bubble__face img { width: 100%; height: 100%; object-fit: cover; }
.bubble--candidate .bubble__avatar { box-shadow: 0 8px 20px rgba(27, 32, 80, 0.2), 0 0 0 3px #fff; }
.bubble__marks:has(.bubble__face) > .bubble__avatar--logo {
  inset: auto -5px -5px auto;
  width: 44%;
  height: 44%;
  z-index: 3;
  border-radius: 28%;
  box-shadow: 0 4px 10px rgba(27, 32, 80, 0.2), 0 0 0 2px #fff;
}
.bubble__marks:has(.bubble__face) > .bubble__avatar--logo img { padding: 3px; }
.bubble__avatar--initials { font-size: calc(var(--mark) * 0.3); }

/* ============================================================
   THE SPOTLIGHT — the large version of a quote (drift and notes)
   Opens beside whatever was hovered; the rest of the wall steps back.
   ============================================================ */

.wall__field.has-spot .bubble:not(.is-spot) { filter: opacity(0.38) saturate(0.7); }
.js .wall--live .bubble { transition: width 0.34s cubic-bezier(0.22, 1, 0.36, 1), filter 0.25s ease; }

.spot {
  position: absolute;
  z-index: 300;
  width: min(27rem, calc(100% - 16px));
  padding: 1.35rem 1.5rem 1.4rem;
  border-radius: 24px;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 30px 70px rgba(12, 14, 40, 0.26), 0 0 0 1px rgba(27, 32, 80, 0.06);
  opacity: 0;
  visibility: hidden;
  transform: scale(0.94);
  transition: opacity 0.22s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 0.22s;
}
.spot.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.22s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.spot[data-side="client"] { box-shadow: inset 0 4px 0 var(--indigo-700), 0 30px 70px rgba(12, 14, 40, 0.26), 0 0 0 1px rgba(27, 32, 80, 0.06); }
.spot[data-side="candidate"] {
  background: linear-gradient(170deg, #FFFCEB, #FDF7D2);
  box-shadow: inset 0 4px 0 var(--pair-700), 0 30px 70px rgba(12, 14, 40, 0.26), 0 0 0 1px rgba(27, 32, 80, 0.06);
}
@media (prefers-reduced-motion: reduce) { .spot, .spot.is-on { transition: none; } }

.spot__head { display: flex; align-items: center; gap: 1rem; }
.spot .bubble__marks { --mark: 104px; }
.spot__meta {
  font-family: var(--font-display);
  font-size: 0.86rem;
  font-weight: 500;
  line-height: 1.35;
  color: var(--indigo-700);
}
.spot[data-side="candidate"] .spot__meta { color: var(--pair-700); }
.spot__meta .bubble__who { display: block; margin-bottom: 0.15rem; font-size: 1.15rem; font-weight: 700; letter-spacing: -0.02em; color: var(--ink); }
.spot__hook {
  margin-top: 1rem;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.2;
}
.spot__media {
  display: block;
  width: 100%;
  margin-top: 1rem;
  border-radius: 16px;
  background: #000;
}
audio.spot__media { background: none; border-radius: 999px; }
.spot__media + .spot__full { font-size: 0.9rem; color: var(--ink-soft); }
.spot__full { margin-top: 0.6rem; font-size: 0.95rem; line-height: 1.6; color: var(--ink); text-wrap: pretty; }
.spot__hook::before, .spot__full::before { content: "\201C"; }
.spot__hook::after, .spot__full::after { content: "\201D"; }

/* ============================================================
   ACT 2 ON A PHONE
   The drifting field cannot survive at 375px, and the straight fallback —
   sixteen cards stacked with their full quotes — is a wall of text. Each side
   becomes a swipeable rail on its own tinted band instead: one quote at a
   time, the two territories still obviously separate, and the whole act
   readable in two gestures rather than two thumb-miles of scrolling.
   ============================================================ */
@media (max-width: 899px) {
  .wall__field { display: block; margin-top: 1.75rem; }

  .group {
    display: block;
    padding: clamp(1.1rem, 4vw, 1.5rem) 0 clamp(1.35rem, 4vw, 1.75rem);
    border-radius: 26px;
  }
  .group + .group { margin-top: 1.25rem; }
  .group--client { background: var(--brick) 0 0 / 120px 48px, var(--indigo-050); box-shadow: inset 0 0 0 1px var(--indigo-100); }
  .group--candidate { background: var(--brick) 60px 0 / 120px 48px, var(--pair-050); box-shadow: inset 0 0 0 1px var(--pair-200); }

  .panel { padding: 0 clamp(1.1rem, 4vw, 1.5rem); margin: 0 0 1rem; }
  .panel__title { font-size: 1.2rem; }

  /* the rail itself */
  .group .bubble { display: none; }
  .group__rail {
    display: flex;
    gap: 0.8rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding: 0.25rem clamp(1.1rem, 4vw, 1.5rem) 0.6rem;
    scrollbar-width: none;
  }
  .group__rail::-webkit-scrollbar { display: none; }
  .group__rail .bubble {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.7rem;
    flex: 0 0 min(76vw, 19rem);
    scroll-snap-align: center;
    padding: 1rem 1.05rem 1.1rem;
    background: #fff;
    border-radius: 18px;
    box-shadow: 0 8px 22px rgba(27, 32, 80, 0.10);
  }
  .group--client .group__rail .bubble { border-top: 3px solid var(--indigo-700); }
  .group--candidate .group__rail .bubble { border-top: 3px solid var(--pair-700); }
  .group__rail .bubble__body { padding: 0; background: none; border: 0; box-shadow: none; }
  .group__rail .bubble__marks { --mark: 64px; }
  /* The card is a stretch column, so a flag with auto width spans the whole
     of it. Pin it to the corner instead. */
  .group__rail .bubble__flag {
    top: 1rem;
    right: 1.05rem;
    left: auto;
    width: 26px;
    height: 26px;
    box-shadow: none;
  }
  /* one card at a time, so the whole quote fits comfortably */
  .group__rail .bubble__hook { display: none; }
  .group__rail .bubble__full { display: block; font-size: 0.88rem; }
  .group__rail .bubble__meta { margin-top: 0.75rem; font-size: 0.78rem; }

  .rail__hint {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0.25rem clamp(1.1rem, 4vw, 1.5rem) 0;
    font-family: var(--font-display);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-soft);
    opacity: 0.65;
  }
  .wall__hint { display: none; }
}

/* ============================================================
   ACT 3 — the team, where we hire, the numbers, the ask.

   White, like the two acts before it, and one screen tall on a desktop: the
   deck, the map and the numbers share a single row under one heading, and a
   slim ask closes it. The only dark surfaces on the page are here — the map's
   panel and the ask — and the ask keeps the stronger gradient and shadow, so
   it still reads as the thing to click.

   The team is a DECK, not a grid: one person face-up, the rest stacked
   behind, dealing through on a timer. That is what makes the headcount free —
   add a seventh and an eighth and no layout changes — and it keeps the act to
   one card instead of a wall of boxes.

   Yellow is the act's accent: the credential chips, the deck's progress, the
   map's regions and labels, and the CTA's primary button.
   ============================================================ */

.act--3 {
  /* The cards fanned out behind the deck reach past a phone's edge, which
     let the whole page pan sideways and made some browsers zoom out to fit.
     clip, not hidden: it trims only what was off-screen anyway, and does not
     turn the act into a scroll container. */
  overflow-x: clip;
  /* At least one screen. The team card is tall enough now that on a laptop
     the ask sits just past the fold, with its top edge showing — an
     invitation to scroll the last few pixels rather than a banner crammed
     in under the row. */
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(1.5rem, 4vh, 3.5rem) var(--pad-x);
  background:
    radial-gradient(70% 45% at 50% 0%, #fff, rgba(255, 255, 255, 0) 70%),
    var(--paper-2);
}
.act3__inner { width: 100%; max-width: 82rem; margin: 0 auto; }
/* the card's height, and so the map panel's — they are a pair */
.act--3 { --member-h: clamp(330px, 46vh, 420px); }

/* The sub runs as one line under a two-line title, and the title gives up a
   little size on short windows rather than the act giving up the ask. */
.act--3 .act__head { max-width: 64rem; }
.act--3 .act__eyebrow { margin-bottom: clamp(0.5rem, 1.4vh, 1rem); }
.act--3 .act__title {
  max-width: 46rem;
  font-size: clamp(2rem, min(4.4vw, 6vh), 3.4rem);
  text-wrap: balance;
}
.act--3 .act__sub { max-width: 64rem; margin-top: clamp(0.4rem, 1.2vh, 0.85rem); }

/* Team and where — two equal halves. The map panel is exactly as tall as
   the card beside it and sits on the same line, so the two read as a pair;
   the map itself is centred in it. Under 1000px they stack. */
.close-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas: "deck reach";
  gap: clamp(1.75rem, 3.4vw, 3.5rem);
  align-items: start;
  margin-top: clamp(1.25rem, 3.2vh, 2.5rem);
}

/* ----- the deck ----- */

/* The card is a tall one: a portrait column down the left, full height, and
   the rest of the card for the person — role, name, credentials, a bio of
   several lines and the LinkedIn link. The portrait is cropped at the
   sides, never the top, so heads stay whole. --member-h is a floor, not a
   cap: a longer bio grows the card.
   Supply ~600px squares and the portraits stay sharp on a 2x screen. */
.deck {
  grid-area: deck;
  --member-shot: clamp(210px, 16vw, 260px);
  width: 100%;
  justify-self: stretch;
}
/* Every card sits in the same grid cell, so the stack is exactly as tall as
   its tallest card — a long bio grows the deck instead of spilling out of
   it. isolation keeps the cards' z-indexes inside the stack. The padding is
   the room the fanned cards behind reach into. */
.deck__stack {
  display: grid;
  isolation: isolate;
  padding: 0 28px 26px 0;
}

.member {
  grid-area: 1 / 1;
  position: relative;
  display: grid;
  grid-template-columns: var(--member-shot) minmax(0, 1fr);
  min-height: var(--member-h);
  border-radius: 26px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 18px 44px rgba(27, 32, 80, 0.16);
  transform-origin: 30% 90%;
  /* JS writes transform/opacity/z-index; this is only how it eases */
  transition:
    transform 0.58s cubic-bezier(0.34, 1.36, 0.44, 1),
    opacity 0.34s ease;
  will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) { .member { transition: none; } }

.member__shot {
  display: block;
  position: relative;
  overflow: hidden;
  background: var(--indigo-050);
}
.member__shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* --face-y overrides this per photo (sync_content.py, from the sheet's
     "Photo focus" column) for a face that sits lower in its frame than
     most — a close portrait or a selfie — and would otherwise have its
     forehead cropped away on a phone, where the card is wide and short. */
  object-position: 50% var(--face-y, 22%);
}

.member__plate {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  padding: clamp(1.1rem, 2.6vh, 1.75rem) clamp(1.2rem, 2vw, 1.9rem);
}
.member__role {
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--indigo-700);
}
.member__name {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1.8vw, 1.7rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin-top: 0.15rem;
}
/* The credentials are the point of this act — they are what the client is
   being asked to trust — so they get the accent colour. */
.member__creds {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.7rem;
}
.member__creds li {
  padding: 0.24rem 0.6rem;
  border-radius: 999px;
  background: var(--yellow);
  color: #6A5410;
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.member__bio p + p { margin-top: 0.5em; }
.member__shot--initials {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 3rem;
  font-weight: 700;
  color: var(--indigo-300);
}
.member__bio {
  margin-top: 0.75rem;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink-soft);
}
.member__in {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.85rem;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--indigo-700);
  text-decoration-color: rgba(46, 49, 146, 0.3);
  text-underline-offset: 3px;
}
.member__in:hover { color: var(--indigo-500); }
/* only the face-up card is reachable */
.member[aria-hidden="true"] { pointer-events: none; }

.deck__nav {
  display: flex;
  align-items: center;
  position: relative;
  z-index: 1;
  gap: 0.85rem;
  margin-top: 0.4rem;
  /* the controls sit under the card, not under the fan behind it */
  margin-right: 28px;
}
.deck__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border: 0;
  border-radius: 50%;
  background: #fff;
  color: var(--indigo-700);
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(27, 32, 80, 0.1), inset 0 0 0 1px var(--hairline);
  transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.deck__btn:hover { background: var(--indigo-700); color: #fff; transform: translateY(-1px); }

.deck__dots { list-style: none; display: flex; align-items: center; gap: 0.4rem; flex: 1; }
/* the li is the flex item, so it carries the sizing — flex on the button
   inside it collapsed the dots to zero width */
.deck__dots li { flex: 1; max-width: 2.4rem; display: flex; }
.deck__dot {
  height: 5px;
  width: 100%;
  border-radius: 999px;
  background: var(--indigo-100);
  border: 0;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  transition: background 0.3s ease, transform 0.3s ease;
}
.deck__dot:hover { background: var(--indigo-300); }
.deck__dot.is-on { background: var(--yellow-deep); transform: scaleY(1.6); }

/* ----- where we hire -----
   A dotted map drawn by initReach, on a panel of its own colour. Every
   colour in it comes from the --map-* tokens, so the panel is re-toned in one
   place: by default the brand indigo, with the regions lit in the accent
   yellow; ?map=light swaps to a pale yellow panel with indigo regions.
   The labels go by the panel's own width, not the screen's — under 500px
   they would crowd each other, so they drop beneath the map as a legend. */

.reach {
  --map-pad-y: clamp(0.8rem, 1.9vh, 1.35rem);
  --map-pad-x: clamp(0.9rem, 1.6vw, 1.5rem);
  --map-land: rgba(170, 177, 212, 0.26);
  --map-area: var(--yellow);
  --map-core: #fff;
  --map-ring: var(--indigo-900);
  --map-pulse: var(--yellow);
  --map-label-bg: var(--yellow);
  --map-label-ink: var(--indigo-900);
  --map-label-shadow: 0 6px 18px rgba(8, 10, 32, 0.4);
  --map-caption: var(--yellow);
  grid-area: reach;
  container: reach / inline-size;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  height: var(--member-h);
  padding: var(--map-pad-y) var(--map-pad-x);
  border-radius: 26px;
  background:
    radial-gradient(90% 120% at 10% 0%, rgba(74, 85, 184, 0.42), rgba(74, 85, 184, 0) 60%),
    var(--indigo-900);
  box-shadow: 0 18px 44px rgba(27, 32, 80, 0.2);
}
[data-map="light"] .reach {
  --map-land: rgba(121, 113, 21, 0.22);
  --map-area: var(--indigo-700);
  --map-core: var(--indigo-900);
  --map-ring: #fff;
  --map-pulse: var(--indigo-500);
  --map-label-bg: #fff;
  --map-label-ink: var(--indigo-900);
  --map-label-shadow: 0 6px 16px rgba(27, 32, 80, 0.14);
  --map-caption: var(--indigo-700);
  background: linear-gradient(160deg, #FCF8E3, var(--yellow));
  box-shadow: inset 0 0 0 1px rgba(179, 160, 46, 0.22);
}
/* On a wide but short window the map is what would push the act past one
   screen, so its height is capped (as a width, to keep the labels aligned). */
.reach__map { position: relative; width: 100%; max-width: calc(40vh * 1.984); margin-inline: auto; }
.reach__svg { display: block; width: 100%; height: auto; overflow: visible; }
.reach__land,
.reach__area { fill: none; stroke-width: 0.62; stroke-linecap: round; }
.reach__land { stroke: var(--map-land); }
.reach__area { stroke: var(--map-area); }
.reach__core,
.reach__pulse { transform-box: fill-box; transform-origin: center; }
.reach__core {
  fill: var(--map-core);
  stroke: var(--map-ring);
  stroke-width: 0.42;
  transform: scale(var(--pin-scale, 1));
}
.reach__pulse { fill: none; stroke: var(--map-pulse); stroke-width: 0.3; opacity: 0; }

/* The panel's title. It used to sit in the empty South Pacific at the size
   of fine print and disappeared into the dots; now it carries a rule and
   enough weight to be read first. */
.reach__caption {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 0.7rem;
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--map-caption);
}
.reach__caption::before {
  content: "";
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: var(--map-area);
}

.reach__regions {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1rem;
}
.reach__region {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3em 0.8em;
  border-radius: 999px;
  background: var(--map-label-bg);
  color: var(--map-label-ink);
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}
/* as a legend, each label carries a pin of its own */
.reach__region::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--map-label-ink);
}

@container reach (min-width: 500px) {
  .reach.has-map .reach__regions { margin: 0; }
  .reach.has-map .reach__region {
    position: absolute;
    left: var(--x);
    top: var(--y);
    /* scale with the panel, so a narrower map is not crowded by its labels */
    font-size: clamp(0.66rem, 1.55cqi, 0.8rem);
    /* above the pin by default; data-side moves it beside */
    transform: translate(-50%, calc(-100% - 12px));
    box-shadow: var(--map-label-shadow);
  }
  .reach.has-map .reach__region[data-side="left"] { transform: translate(calc(-100% - 12px), -50%); }
  .reach.has-map .reach__region[data-side="right"] { transform: translate(12px, -50%); }
  .reach.has-map .reach__region::before { display: none; }
  .reach.has-map .reach__caption {
    position: absolute;
    left: calc(var(--map-pad-x) + 0.6rem);
    top: calc(var(--map-pad-y) + 0.4rem);
    margin: 0;
  }
}
@container reach (max-width: 499px) {
  .reach__map { --pin-scale: 1.8; }
  .reach__land,
  .reach__area { stroke-width: 0.7; }
}

/* entrance: land first, then each region lights, pins drop, labels follow */
.js .reach .reach__land { opacity: 0; }
/* a region starts as plain land, so lighting it up is the only change */
.js .reach .reach__area { stroke: var(--map-land); }
.js .reach .reach__core { transform: scale(0); }
.js .reach .reach__region { opacity: 0; translate: 0 8px; }
.js .reach.is-in .reach__land { opacity: 1; transition: opacity 0.7s ease; }
.js .reach.is-in .reach__area {
  stroke: var(--map-area);
  transition: stroke 0.6s ease calc(250ms + var(--i, 0) * 240ms);
}
.js .reach.is-in .reach__core {
  transform: scale(var(--pin-scale, 1));
  transition: transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) calc(480ms + var(--i, 0) * 240ms);
}
.js .reach.is-in .reach__region {
  opacity: 1;
  translate: 0 0;
  transition: opacity 0.45s ease calc(640ms + var(--i, 0) * 240ms),
              translate 0.6s cubic-bezier(0.22, 1, 0.36, 1) calc(640ms + var(--i, 0) * 240ms);
}
.js .reach.is-in .reach__pulse { animation: reach-pulse 2.8s ease-out calc(1100ms + var(--i, 0) * 240ms) infinite; }
@keyframes reach-pulse {
  0%   { opacity: 0.8; transform: scale(calc(0.45 * var(--pin-scale, 1))); }
  100% { opacity: 0;   transform: scale(calc(1.5 * var(--pin-scale, 1))); }
}
@media (prefers-reduced-motion: reduce) {
  .js .reach .reach__land,
  .js .reach .reach__area,
  .js .reach .reach__core,
  .js .reach .reach__region { transition: none; }
  .js .reach.is-in .reach__pulse { animation: none; }
}

/* ----- the ask: the loudest thing on the page ----- */

/* Slimmer than v3's so the act fits one screen: the copy on the left, and
   everything to act on — the buttons, the deck picker, the candidate note —
   stacked on the right, instead of a third full-width row. */
.ask {
  position: relative;
  overflow: hidden;
  margin-top: clamp(1rem, 2.8vh, 2rem);
  padding: clamp(1rem, 2.6vh, 1.75rem) clamp(1.5rem, 3vw, 2.75rem);
  border-radius: 28px;
  background:
    radial-gradient(90% 150% at 88% 8%, rgba(247, 239, 179, 0.22), rgba(247, 239, 179, 0) 55%),
    radial-gradient(100% 140% at 6% 0%, rgba(74, 85, 184, 0.55), rgba(74, 85, 184, 0) 58%),
    var(--indigo-900);
  color: #fff;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "copy actions"
    "copy extra";
  align-items: center;
  gap: clamp(0.5rem, 1.2vh, 0.8rem) clamp(1.5rem, 3vw, 3rem);
  box-shadow: 0 24px 56px rgba(27, 32, 80, 0.26);
}
.ask__copy { grid-area: copy; }
/* a soft yellow bloom behind the corner, so the block has some depth */
.ask::after {
  content: "";
  position: absolute;
  right: -12%;
  bottom: -55%;
  width: 46%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(247, 239, 179, 0.16), rgba(247, 239, 179, 0) 68%);
  pointer-events: none;
}
.ask > * { position: relative; z-index: 1; }

.ask__kicker {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--yellow);
  margin-bottom: 0.4rem;
}
.ask__title {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, min(2.1vw, 3.8vh), 2.1rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.08;
}
.ask__sub { margin-top: 0.35rem; color: rgba(255, 255, 255, 0.7); }
.ask__actions { grid-area: actions; display: flex; gap: 0.6rem; align-items: center; }
.ask__actions .btn { justify-content: center; }
.ask__actions .btn--lg { min-height: 48px; padding: 0.7rem 1.5rem; font-size: 1rem; }
.ask__actions .btn--light { box-shadow: 0 10px 30px rgba(247, 239, 179, 0.26); }
.ask__extra {
  grid-area: extra;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.45rem;
}

.ask__note { font-size: 0.85rem; color: rgba(255, 255, 255, 0.55); }
.ask__note a { color: rgba(255, 255, 255, 0.9); text-decoration-color: rgba(255, 255, 255, 0.4); }
.ask__note a:hover { color: var(--yellow); }

/* ----- the entrance -----
   The settled state is the default and the class only adds it, so a browser
   that never animates still shows a finished section. */
.js .act--3 .deck,
.js .act--3 .reach,
.js .act--3 .ask {
  opacity: 0;
  transform: translateY(26px);
}
.js .act--3.is-in .deck,
.js .act--3.is-in .reach,
.js .act--3.is-in .ask {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s ease, transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}
.js .act--3.is-in .deck { transition-delay: 60ms; }
.js .act--3.is-in .reach { transition-delay: 240ms; }
.js .act--3.is-in .ask { transition-delay: 780ms; }
@media (prefers-reduced-motion: reduce) {
  .js .act--3 .deck,
  .js .act--3 .reach,
  .js .act--3 .ask { opacity: 1; transform: none; transition: none; }
}

@media (max-width: 1000px) {
  .close-grid { grid-template-columns: 1fr; grid-template-areas: "deck" "reach"; gap: 2.5rem; }
  .deck { justify-self: center; }
}
@media (max-width: 860px) {
  .ask { grid-template-columns: 1fr; grid-template-areas: "copy" "actions" "extra"; }
  .ask__actions { flex-wrap: wrap; }
}
/* A phone has no room for the portrait beside the text, so the card turns
   back into a portrait over its plate. */
@media (max-width: 620px) {
  .deck__stack { padding: 0 16px 18px 0; }
  .deck__nav { margin-right: 16px; }
  .member { grid-template-columns: 1fr; grid-template-rows: auto 1fr; min-height: 0; }
  .member__shot { aspect-ratio: 5 / 4; }
}

/* ============================================================
   THE LEAD POPUP
   The same two surfaces the page closes on: an indigo head like the ask,
   yellow kicker and all, over a white form. The fields are the page's own
   pills and rounded panels at input size.
   ============================================================ */

html.has-lead { overflow: hidden; }

.lead {
  width: min(100% - 2rem, 34rem);
  max-height: calc(100dvh - 2rem);
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: 28px;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 40px 90px rgba(12, 14, 40, 0.35);
  overflow: auto;
}
.lead::backdrop {
  background: rgba(20, 24, 62, 0.5);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.lead[open] { animation: lead-in 0.42s cubic-bezier(0.22, 1, 0.36, 1); }
.lead[open]::backdrop { animation: lead-fade 0.3s ease; }
@keyframes lead-in { from { opacity: 0; transform: translateY(18px) scale(0.98); } }
@keyframes lead-fade { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .lead[open], .lead[open]::backdrop { animation: none; }
}

.lead__card { position: relative; }
.lead [hidden] { display: none !important; }

.lead__close {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  z-index: 2;
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  transition: background 0.2s ease;
}
.lead__close:hover { background: rgba(255, 255, 255, 0.24); }
/* on the thank-you state there is no indigo head to sit on */
.lead:has(.lead__head[hidden]) .lead__close { background: var(--paper-2); color: var(--ink); }

.lead__head {
  position: relative;
  overflow: hidden;
  padding: 1.75rem 3.75rem 1.5rem 1.9rem;
  background:
    radial-gradient(90% 150% at 92% 0%, rgba(247, 239, 179, 0.22), rgba(247, 239, 179, 0) 55%),
    radial-gradient(100% 140% at 0% 0%, rgba(74, 85, 184, 0.55), rgba(74, 85, 184, 0) 58%),
    var(--indigo-900);
  color: #fff;
}
.lead__kicker {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--yellow);
  margin-bottom: 0.45rem;
}
.lead__title {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 3.4vw, 1.85rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.lead__sub { margin-top: 0.5rem; font-size: 0.95rem; line-height: 1.5; color: rgba(255, 255, 255, 0.72); }

.lead__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem 0.9rem;
  padding: 1.5rem 1.9rem 1.7rem;
}
.field--wide, .field--solo { grid-column: 1 / -1; }
.field { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.field__label {
  font-family: var(--font-display);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.field__input {
  width: 100%;
  min-height: 48px;
  padding: 0.65rem 0.95rem;
  border: 0;
  border-radius: 14px;
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1.5px var(--hairline);
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  transition: box-shadow 0.2s ease, background 0.2s ease;
}
.field__input::placeholder { color: rgba(90, 96, 118, 0.55); }
.field__input:hover { box-shadow: inset 0 0 0 1.5px var(--indigo-300); }
.field__input:focus { outline: none; background: #fff; box-shadow: inset 0 0 0 2px var(--indigo-500), 0 0 0 4px rgba(247, 239, 179, 0.9); }
/* Only complain after a failed submit of this form. :user-invalid would
   carry the flag over from the previous mode's submit, so every field of
   the next mode opened already red. */
.lead__form.was-tried .field__input:invalid { box-shadow: inset 0 0 0 2px #C2410C; }
.field__input--select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.5rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232E3192' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 16px;
}
.field__input--select:invalid { color: rgba(90, 96, 118, 0.7); }

.lead__foot {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1rem;
  margin-top: 0.35rem;
}
.lead__submit { border: 0; cursor: pointer; }
.lead__submit svg { transition: transform 0.2s ease; }
.lead__submit:hover svg { transform: translateX(3px); }
.lead__fine { font-size: 0.8rem; color: var(--ink-soft); }

/* already-answered fields collapse into this line */
.lead__known {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.6rem;
  padding: 0.6rem 0.9rem;
  border-radius: 14px;
  background: var(--indigo-050);
  font-size: 0.9rem;
  color: var(--indigo-900);
}
.lead__link {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--indigo-700);
  text-decoration: underline;
  text-decoration-color: rgba(46, 49, 146, 0.35);
  text-underline-offset: 3px;
  cursor: pointer;
}
.lead__link:hover { color: var(--indigo-500); }
/* the hand-off to the other mode, under a rule */
.lead__switch {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.5rem;
  padding-top: 0.95rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.9rem;
  color: var(--ink-soft);
}

/* said out loud when the send fails */
.lead__error {
  grid-column: 1 / -1;
  padding: 0.7rem 0.95rem;
  border-radius: 14px;
  background: #FDF1E7;
  box-shadow: inset 0 0 0 1.5px rgba(194, 65, 12, 0.35);
  font-size: 0.9rem;
  color: #7C2D12;
}
.lead__error a { color: #7C2D12; font-weight: 600; text-underline-offset: 3px; }
.lead__submit[disabled] { opacity: 0.65; cursor: default; }

.lead__done {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 2.4rem 1.9rem 2rem;
}
.lead__done .lead__sub { color: var(--ink-soft); }
.lead__done-title:focus { outline: none; }
.lead__tick {
  display: flex; align-items: center; justify-content: center;
  width: 54px; height: 54px;
  margin-bottom: 1.1rem;
  border-radius: 50%;
  background: var(--yellow);
  color: var(--indigo-900);
}
.lead__done-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.4rem; }
.lead__done-close, .lead__done-next { border: 0; cursor: pointer; }

@media (max-width: 520px) {
  .lead__form { grid-template-columns: 1fr; padding: 1.25rem 1.25rem 1.4rem; }
  .lead__head { padding: 1.5rem 3.5rem 1.3rem 1.25rem; }
  .lead__done { padding: 2rem 1.25rem 1.6rem; }
  .lead__submit { width: 100%; justify-content: center; }
}

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

.footer {
  padding: 2.5rem var(--pad-x) 3rem;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.6rem 2rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  background: var(--paper-2);
}
.footer__logo { width: 110px; }
.footer__line { flex: 1 1 18rem; }
.footer__links a { color: var(--indigo-700); text-decoration-color: rgba(46, 49, 146, 0.35); }
.footer__links a:hover { color: var(--indigo-500); }
.footer__copy { width: 100%; font-size: 0.8rem; opacity: 0.7; }

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .ask { grid-template-columns: 1fr; }
  .ask__actions { flex-direction: row; flex-wrap: wrap; }
}
/* ============================================================
   ON A PHONE — one screen for the first act, one scale for all three
   The first screen has to hold the step, the headline and both buttons
   without scrolling, so every measure here is tied to the small viewport
   height (svh, the height with the browser's own bars showing) rather than
   to a fixed size: a short phone shrinks the act instead of pushing the
   buttons under the fold.
   The type sizes apply to all three acts, not just the first — the same
   headline size throughout, or Act 1 looks stunted beside the others.
   Nothing here touches the desktop.
   ============================================================ */
@media (max-width: 620px) {
  /* the act clears the bar above it: logo, button and a little air */
  .act--1 { padding: clamp(5.4rem, 13svh, 7rem) var(--pad-x) clamp(0.75rem, 2svh, 1.75rem); }

  .act__eyebrow { margin-bottom: clamp(0.35rem, 1svh, 0.9rem); font-size: 0.7rem; }
  .act__num { font-size: clamp(1.5rem, 4.4svh, 2.1rem); margin-bottom: 0.15rem; }
  .hero__title,
  .act__title,
  .act--3 .act__title { font-size: clamp(1.55rem, 5.1svh, 2.3rem); }
  .hero__sub,
  .act__sub {
    max-width: 30rem;
    margin-top: clamp(0.6rem, 2svh, 1rem);
    font-size: clamp(0.85rem, 1.9svh, 0.98rem);
  }

  /* Smaller buttons. They were shouting over the headline, which is the
     thing that has to land first on a small screen. */
  .btn { min-height: clamp(38px, 5svh, 44px); padding: 0.5rem 1.1rem; font-size: 0.88rem; }
  .btn--lg { min-height: clamp(40px, 5.4svh, 46px); padding: 0.55rem 1.25rem; font-size: 0.92rem; }
  .btn--small { min-height: 34px; padding: 0.35rem 0.85rem; font-size: 0.82rem; }
  .ask__actions .btn--lg { min-height: clamp(40px, 5.4svh, 46px); padding: 0.55rem 1.25rem; font-size: 0.92rem; }

  /* nothing overhangs now, so the buttons sit as a pair rather than being
     held apart by the sticker's clearance */
  .hero__ctas { margin-top: clamp(1rem, 3.2svh, 1.6rem); padding: 0 0.5rem 0; }
  .hero__ctas .btn { display: flex; width: 100%; }
  .hero__ctas .btn + .btn { margin-top: clamp(0.5rem, 1.4svh, 0.8rem); }
  .hero__note { margin-top: clamp(0.7rem, 2svh, 1rem); font-size: 0.78rem; }
  .cue { margin-top: clamp(0.5rem, 1.5svh, 1rem); gap: 0.2rem; }
  .cue__arrow { width: clamp(26px, 4.2svh, 32px); height: clamp(26px, 4.2svh, 32px); }

  /* the logo ribbons keep their place, at a weight that leaves room */
  /* The copy sits between the two client bands, and the air above it and
     below it is the same measure — set once here, so the two cannot drift
     apart and leave the block looking off-centre. */
  .act--1 { --band-gap: clamp(1.5rem, 4.4svh, 2.4rem); }
  .js .act--1.is-ribboned .ribbon--top { margin-bottom: var(--band-gap); }
  .js .act--1.is-ribboned .ribbon--bottom { margin-top: var(--band-gap); }
  .ribbon__item { padding: 0.26rem 0.7rem 0.26rem 0.26rem; font-size: 0.74rem; }
  .ribbon__mark { width: 26px; height: 26px; border-radius: 8px; }

  /* the hero says the short version; the rest is desktop's */
  .u-wide { display: none; }

  /* ---- Act 2: the heading and a whole wall on one screen ---- */
  .act--2 { padding: clamp(2rem, 5svh, 3.5rem) var(--pad-x) clamp(1.5rem, 4svh, 3rem); }
  .act--2 .act__head { max-width: 30rem; }
  .wall__field { margin-top: clamp(1rem, 3svh, 1.75rem); }
  .group { padding: clamp(0.85rem, 2.4svh, 1.25rem) 0 clamp(1rem, 2.6svh, 1.4rem); }
  .group + .group { margin-top: 1rem; }
  .panel { margin-bottom: clamp(0.5rem, 1.6svh, 0.9rem); }
  .panel__title { font-size: 1.05rem; }
  .panel__note { font-size: 0.78rem; }
  .group__rail .bubble { flex-basis: min(78vw, 19rem); padding: 0.85rem 0.9rem 0.95rem; gap: 0.55rem; }
  .group__rail .bubble__marks { --mark: 46px; }
  /* the first few lines, then the whole thing on a tap */
  .group__rail .bubble__full {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 5;
    overflow: hidden;
    font-size: 0.82rem;
    line-height: 1.45;
  }
  .group__rail .bubble.is-open .bubble__full { -webkit-line-clamp: unset; }
  .group__rail .bubble { position: relative; padding-bottom: 1.6rem; }
  .group__rail .bubble__meta { margin-top: 0.5rem; font-size: 0.74rem; }

  /* ---- Act 3: the heading and a whole team card on one screen ---- */
  .act--3 { padding: clamp(1.75rem, 4svh, 3.5rem) var(--pad-x) clamp(1.5rem, 4svh, 3rem); }
  .act--3 .act__head { max-width: 30rem; }
  .act--3 .act__title { font-size: clamp(1.4rem, 4.2svh, 2rem); }
  .close-grid { margin-top: clamp(0.7rem, 2svh, 1.75rem); gap: clamp(1.25rem, 4svh, 2.5rem); }
  .member__shot { aspect-ratio: auto; height: clamp(148px, 26svh, 250px); }
  .member__plate { padding: clamp(0.85rem, 2.2svh, 1.15rem) 1rem clamp(0.9rem, 2.4svh, 1.2rem); }
  .member__role { font-size: 0.64rem; }
  .member__name { font-size: clamp(1.15rem, 3svh, 1.4rem); }
  .member__creds { margin-top: 0.5rem; gap: 0.3rem; }
  .member__creds li { font-size: 0.66rem; padding: 0.2rem 0.5rem; }
  /* the bio is a summary here: five lines, and the card stays whole */
  .member__bio {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    margin-top: 0.5rem;
    font-size: 0.82rem;
    line-height: 1.45;
  }
  .member__in { margin-top: 0.6rem; font-size: 0.75rem; }
  .member.is-open .member__bio { -webkit-line-clamp: unset; }
  .member__plate { position: relative; padding-bottom: 1.9rem; }
  .deck__nav { margin-top: 0.5rem; }
}

@media (max-width: 480px) {
  .hero__ctas > span { width: 100%; }
  .hero__ctas .btn { width: 100%; justify-content: center; }
  .ask__actions .btn { width: 100%; }
  .footer { row-gap: 1rem; }
}
