/* >>> hero-lqip (generated by tools/make-hero-lqip.sh) */
:root { --hero-lqip: url("data:image/webp;base64,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"); }
/* <<< hero-lqip */

/* =============================================================
   GYM MEDIA — HERO BACKGROUND
   All tunables arrive as CSS custom properties set from config.js.
   Only compositor-friendly properties are animated (transform).
   ============================================================= */

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

/* ---------- brand ----------
   The four palette colours, plus one derived shade. #8866DE on white only
   reaches 4.2:1, which fails AA for button-sized text, so --violet-deep is a
   darkened version of it (6.6:1) used wherever white text sits on top. */
:root {
  --lav: #e0afff;
  --peri: #c4d6ff;
  --orchid: #dd68e3;
  --violet: #8866de;
  --violet-deep: #6742c7;

  --bg: #000000;          /* overwritten from config.bg on build */
  --ink: #14121a;
  --paper: #fbfaf8;
  --sans: 'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 122px;   /* clears the floating nav */
}

html, body {
  margin: 0;
  background: var(--bg, #000);
}

body {
  font-family: var(--sans);
  color: var(--paper);
  -webkit-font-smoothing: antialiased;
}

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

/* ---------- stage ---------- */

.hero {
  position: relative;
  height: 100svh;
  overflow: hidden;
  background: var(--bg);
  isolation: isolate;
}

/* The wall is scaled a touch past the viewport so column edges never show.
   It lives inside .hero and scrolls away with it — the video is the hero's, and
   the rest of the page is plain black. */
.hero__wall,
.outro__wall {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  gap: var(--gap);
  transform: scale(var(--overscan));
  transform-origin: center center;
}

/* Reveal: fade one small black cover OUT, rather than fading the whole wall in.
   Transitioning the wall would drag its entire composited subtree through the
   animation and it renders unreliably.

   The cover is not black. It was, and on a throttled connection that meant six
   seconds and more of an empty screen — hero.js is a module that pulls config.js
   and media.js behind it, so the wall does not exist until three round trips
   have landed, and no amount of tuning what the wall fetches can fill a window
   that ends before the wall is built.

   So the cover carries the first frames itself: --hero-lqip is a 128x84 mosaic
   of 24 real posters inlined as base64 (about 2.5KB, generated by
   tools/make-hero-lqip.sh), blown up and blurred into a soft wall of footage.
   It is part of the stylesheet, so it paints as early as anything on the page
   can, and it is graded to match the real wall — the handover reads as coming
   into focus rather than as a cut. */
.hero__cover {
  position: absolute;
  inset: 0;
  /* Above the wall, BELOW .hero__grade (z-index 2). It used to sit at 3, on top
     of everything, which was fine while it was flat black and wrong the moment
     it carried a picture: the vignette, the centre dim and the grain are what
     make the wall look like the wall, and the stand-in was rendering without
     any of them — visibly brighter than the thing it hands over to. Under the
     grade, it gets the same treatment and the handover stops being a step. */
  z-index: 1;
  background: var(--bg);
  pointer-events: none;
  transition: opacity 900ms ease;
  overflow: hidden;
}

/* The mosaic sits in two places, and it is the same custom property both times
   so the browser decodes it once.

   Here, on the cover, it is what you see before the wall exists at all.
   And on .hero::before below it stays for good, underneath the cards — so a
   card that hasn't got its poster yet is transparent onto soft footage instead
   of onto a black rectangle. Cards fade in over it one at a time as their
   posters land, which is why the cover no longer has to wait for all of them. */
.hero__cover::after,
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--hero-lqip, none) center / cover no-repeat;
  /* 16px-wide tiles stretched across a viewport are already soft; the blur is
     there to kill the tile edges, and the scale keeps the blur's own feathered
     margin off screen. */
  filter: blur(18px);
  transform: scale(1.15);
}

.hero::before {
  z-index: 0;
  pointer-events: none;
}

/* .hero__grade is driven by custom properties that hero.js sets on build, so
   before the script has run the vignette, the centre dim and the edge fades are
   all still transparent — which is why putting the cover under the grade wasn't
   enough on its own. These are the same four treatments written statically, at
   the config.js defaults, and they exist only on the cover: the permanent
   backdrop above must NOT have them, because by the time anything is showing
   through it the real grade is running and would double up. */
.hero__cover::after {
  background:
    radial-gradient(ellipse 62% 52% at 50% 50%, rgba(0, 0, 0, 0.52), rgba(0, 0, 0, 0) 72%),
    radial-gradient(ellipse 82% 78% at 50% 50%, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, 0.7) 100%),
    linear-gradient(
      to bottom,
      #000 0%, rgba(0, 0, 0, 0) 30%,
      rgba(0, 0, 0, 0) 70%, #000 100%
    ),
    var(--hero-lqip, none) center / cover no-repeat;
}

.hero.is-ready .hero__cover { opacity: 0; }

/* ---------- columns ---------- */

.col {
  position: relative;
  flex: 0 0 var(--card-w);
  width: var(--card-w);
  height: 100%;
  overflow: hidden;
  /* per-column depth, set inline */
  opacity: var(--col-opacity, 1);
  transform: scale(var(--col-scale, 1));
}

.col__track {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  will-change: transform;
  backface-visibility: hidden;
  animation-name: track-up;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: var(--dur);
  animation-delay: var(--delay);
}

/* Two identical copies of the card list live in the track, so translating
   by exactly -50% lands copy 2 where copy 1 began — a seamless loop. */
@keyframes track-up {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -50%, 0); }
}

.col--down .col__track { animation-direction: reverse; }

/* ---------- cards ---------- */

.card {
  position: relative;
  width: 100%;
  aspect-ratio: 9 / 16;
  /* margin (not flex gap) keeps the loop period exact: height = n * (card + gap) */
  margin-bottom: var(--gap);
  border-radius: var(--radius);
  overflow: hidden;
  background: #000;
  opacity: var(--card-opacity);
  /* Each card arrives on its own, as its own poster decodes — see .card--pending
     below. Opacity only, so it stays on the compositor. */
  transition: opacity 520ms ease;
  box-shadow:
    0 18px 38px -22px rgba(0, 0, 0, calc(var(--lift) * 1.6)),
    0 2px 10px -4px rgba(0, 0, 0, calc(var(--lift) * 1.2));
  transform: translateZ(0);
}

/* A card with nothing in it yet. It is transparent rather than black, so what
   shows through is the blurred mosaic on .hero::before — the wall reads as
   soft footage coming into focus card by card, instead of a grid of black
   holes waiting to be filled. */
.card--pending {
  opacity: 0;
}

/* hairline edge — keeps cards reading as objects without a visible border */
.card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, var(--ring));
  pointer-events: none;
}

.card__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter:
    brightness(var(--brightness))
    contrast(var(--contrast))
    saturate(var(--saturation))
    blur(var(--blur));
  transform: scale(1.02);
  backface-visibility: hidden;
}

/* slow drift so stills read as footage */
.card--kb .card__media {
  animation: ken-burns var(--kb-dur) ease-in-out infinite alternate;
  animation-delay: var(--kb-delay);
}

@keyframes ken-burns {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(var(--kb-scale)) translate3d(0, -1.2%, 0); }
}

/* ---------- grade / overlays ----------
   One stacked, static overlay layer. Painted once, never re-rasterised. */

.hero__grade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}

.hero__vignette {
  position: absolute;
  inset: -10%;
  background: radial-gradient(
    ellipse 62% 62% at 50% 50%,
    rgba(0, 0, 0, 0) 38%,
    rgba(0, 0, 0, calc(var(--vignette) * 0.55)) 74%,
    rgba(0, 0, 0, var(--vignette)) 100%
  );
}

.hero__center {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 70% 55% at 50% 50%,
    rgba(0, 0, 0, var(--center-dim)) 0%,
    rgba(0, 0, 0, calc(var(--center-dim) * 0.5)) 45%,
    rgba(0, 0, 0, 0) 78%
  );
}

.hero__fade-top,
.hero__fade-bottom,
.hero__fade-left,
.hero__fade-right {
  position: absolute;
}

.hero__fade-top {
  top: 0; left: 0; right: 0;
  height: calc(var(--fade-top) * 100%);
  background: linear-gradient(to bottom, var(--bg) 0%, rgba(0,0,0,0.75) 35%, rgba(0,0,0,0) 100%);
}

.hero__fade-bottom {
  bottom: 0; left: 0; right: 0;
  height: calc(var(--fade-bottom) * 100%);
  background: linear-gradient(to top, var(--bg) 0%, rgba(0,0,0,0.75) 35%, rgba(0,0,0,0) 100%);
}

.hero__fade-left {
  top: 0; bottom: 0; left: 0;
  width: calc(var(--fade-sides) * 100%);
  background: linear-gradient(to right, var(--bg) 0%, rgba(0,0,0,0) 100%);
}

.hero__fade-right {
  top: 0; bottom: 0; right: 0;
  width: calc(var(--fade-sides) * 100%);
  background: linear-gradient(to left, var(--bg) 0%, rgba(0,0,0,0) 100%);
}

.hero__grain {
  position: absolute;
  inset: 0;
  opacity: var(--grain);
  mix-blend-mode: overlay;
  background-image: var(--grain-src);
  background-size: 180px 180px;
}

/* ---------- headline ----------
   The wall behind this is already loud — dense, moving, full of captions.
   So the type stays quiet: one face, one weight, no accent colour, no eyebrow.
   The only movement is the rotating noun, and it slides upward on the same
   axis as the columns so it reads as part of the wall, not pasted over it. */

.hero__content {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: var(--text-align-items, flex-start);
  padding: 0 clamp(24px, 6vw, 132px);
  pointer-events: none;
}

/* A soft pool of shade under the text. Cheaper and cleaner than blurring the
   videos — a filter on 48 video layers would cost a frame budget we don't have. */
.hero__content::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 58% 46% at var(--scrim-x, 32%) 50%,
    rgba(0, 0, 0, var(--scrim)) 0%,
    rgba(0, 0, 0, calc(var(--scrim) * 0.55)) 45%,
    rgba(0, 0, 0, 0) 78%
  );
  pointer-events: none;
}

.headline {
  position: relative;
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  /* two lines, so the longest line is "Helping Jewish entrepreneurs" — that's
     what caps the size */
  font-size: clamp(2rem, 5.3vw, 5.15rem);
  line-height: 1.04;
  /* heavier weight needs tighter tracking, or the counters open up and the
     line reads loose rather than solid */
  letter-spacing: -0.043em;
  /* a hair warm — reads filmic next to footage, where pure #fff reads clinical */
  color: #fbfaf8;
  /* not a drop shadow — a wide, soft pool that lifts the type off busy frames
     without any visible edge. The scrim handles the broad strokes; this is what
     keeps an letterform readable when a bright clip slides under it. */
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.42);
  text-align: var(--text-align, left);
}

.headline__line {
  display: block;
  /* the line breaks are authored, not reflowed — the rotating noun must stay
     on line one or the block changes shape every few seconds */
  white-space: nowrap;
  /* entrance: each line rises into place, staggered */
  opacity: 0;
  transform: translate3d(0, 0.34em, 0);
  transition: opacity 900ms ease, transform 1100ms cubic-bezier(0.16, 1, 0.3, 1);
}

.headline__line:nth-child(2) { transition-delay: 110ms; }

.hero.is-typeset .headline__line {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ---------- the rotating noun ---------- */

/* Back to two lines, so the rotating word sits inline again.

   Ten nouns run from "brands" to "entrepreneurs" — the slot's width swings by
   ~310px at full size. Centred, that means line one genuinely has to re-centre
   as the word changes. The slot animates its width on the same beat and easing
   as the letter ripple so it reads as one continuous movement rather than a
   jolt; there is no way to have centred text, an inline word, and wildly
   different word lengths without something moving. */
.rotator {
  display: inline-grid;
  /* Always start-aligned, never centred. Centring floats the word inside a slot
     that's mid-resize, so the space after "Jewish" visibly grows and shrinks.
     Left-hugging keeps that gap fixed and puts all the slack on the right. */
  justify-items: start;
  width: var(--rw, auto);
  vertical-align: top;
  /* The gap after "Jewish" is a real space character in the markup now, not
     this margin. It had to be: with the two glued together, the most important
     heading on the site read "Helping Jewishbrands" to anything reading text
     rather than pixels, which fuses both words into one meaningless token. A
     word space in this face measures ~0.25em against the 0.28em it replaces —
     close enough that the line does not move. */
  margin-left: 0;

  /* The mask has to work vertically only. `overflow: hidden` also clips
     horizontally, and since the slot is mid-animation while the word is already
     at full length, that chopped up to 87px off the end of longer words —
     "speakers" rendering as "speake". A negative horizontal inset clips top and
     bottom at the box edge while letting the word run past the sides. */
  clip-path: inset(0 -100vw);
}

.rotator__word {
  grid-area: 1 / 1;
  white-space: nowrap;
}

/* Letters move one after another rather than the word moving as a block. The
   ripple runs left to right, upward — the same direction the columns behind it
   travel — and each letter picks up a little defocus on the way, so a swap
   reads like a rack focus rather than a slide. */
.rotator__char {
  display: inline-block;
  transform: translate3d(0, var(--y, 0), 0);
  opacity: var(--o, 0);
  filter: blur(var(--b, 0px));
  transition:
    transform var(--swap, 380ms) cubic-bezier(0.22, 0.85, 0.24, 1),
    opacity calc(var(--swap, 380ms) * 0.7) linear,
    filter calc(var(--swap, 380ms) * 0.7) linear;
  transition-delay: var(--d, 0ms);
}

.is-current .rotator__char { --y: 0; --o: 1; --b: 0px; }
.is-leaving .rotator__char { --y: calc(var(--lift) * -1); --o: 0; --b: var(--defocus); }

/* parked below the mask, waiting its turn — no transition, so getting there
   is instant and invisible */
.is-waiting .rotator__char { --y: var(--lift); --o: 0; --b: var(--defocus); transition: none; }

/* ---------- call to action ----------
   One button, solid white. Against a black page full of moving footage, the
   only genuinely quiet surface is a flat opaque one — so that's where the
   click goes, and nothing else on the page competes for it. */

.cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-top: clamp(1.75rem, 3.4vw, 2.75rem);
  padding: 1.05em 2.1em;
  border-radius: 999px;
  /* same bright palette gradient as the closing button, so the page's two calls
     to action read as the same thing. Dark ink on it, not white: white on the
     orchid end is only 2.6:1 and fails, ink is 6.2:1. */
  background: linear-gradient(104deg, var(--lav) 0%, var(--orchid) 100%);
  color: var(--ink);
  font-family: inherit;
  font-size: clamp(0.95rem, 1.05vw, 1.0625rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
  pointer-events: auto;   /* the wrapper is click-through; this isn't */
  transition:
    opacity 800ms ease 240ms,
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1) 240ms,
    box-shadow 220ms ease;
  box-shadow: 0 14px 40px -14px rgba(221, 104, 227, 0.5);
}

/* The hidden-until-typeset entrance belongs to the HERO, not to the button.
   While it sat on .cta itself, the same button on the referrals page — which has
   no hero to ever add is-typeset — stayed at opacity 0 forever: a live WhatsApp
   link that took up space and rendered nothing. */
.hero .cta {
  /* entrance, one beat behind the second line */
  opacity: 0;
  transform: translate3d(0, 0.9rem, 0);
}

.hero.is-typeset .cta {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.cta:hover {
  box-shadow: 0 20px 52px -14px rgba(221, 104, 227, 0.7);
  transform: translate3d(0, -2px, 0);
}

.hero.is-typeset .cta:hover {
  box-shadow: 0 20px 52px -14px rgba(221, 104, 227, 0.7);
  transform: translate3d(0, -2px, 0);
}

/* A periodic nudge to pull the eye to the one thing we want clicked. Mostly
   still — the movement happens in the last second of a 5.2s cycle, so it reads
   as an occasional glance rather than a fidget.

   It animates the standalone `scale` property, NOT `transform`: transform is
   already carrying the entrance and hover translate, and an animation on it
   would stamp on both. `scale` composes with transform instead of replacing it. */
@keyframes cta-nudge {
  0%, 68%, 100% { scale: 1; }
  73%           { scale: 1.038; }
  79%           { scale: 0.993; }
  85%           { scale: 1.015; }
  91%           { scale: 0.999; }
}

.hero.is-typeset .cta {
  animation: cta-nudge 5.2s cubic-bezier(0.34, 1.35, 0.5, 1) 1.8s infinite;
}

/* A glare sweeping across the pill, on the same 5.2s cycle as the nudge so the
   two read as one gesture rather than two competing tics. The button clips it,
   which is what keeps it a highlight travelling across a surface instead of a
   white bar flying past. */
.cta { overflow: hidden; }

.cta::after {
  content: '';
  position: absolute;
  top: -60%;
  bottom: -60%;
  left: 0;
  width: 34%;
  background: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.62) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: skewX(-18deg) translateX(-320%);
  opacity: 0;
  pointer-events: none;
}

.hero.is-typeset .cta::after {
  animation: cta-glare 5.2s ease-in-out 1.8s infinite;
}

@keyframes cta-glare {
  0%, 52%   { transform: skewX(-18deg) translateX(-320%); opacity: 0; }
  56%       { opacity: 1; }
  70%       { transform: skewX(-18deg) translateX(360%); opacity: 1; }
  74%, 100% { transform: skewX(-18deg) translateX(360%); opacity: 0; }
}

/* hands the button back to the hover styles while the pointer is on it */
.cta:hover,
.cta:focus-visible { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .hero.is-typeset .cta,
  .hero.is-typeset .cta::after { animation: none; }
}

.cta:focus-visible {
  outline: 2px solid #fbfaf8;
  outline-offset: 4px;
}

/* On a phone there isn't room for "Helping Jewish businesses" on one line, so
   let it wrap and pull the type in tighter. */
/* Vertical: four authored lines instead of two.
     Helping Jewish
     [rotating word]
     grow
     on social media
   The rotator goes block-level so it takes a line of its own, and its word
   centres inside the slot — safe here because nothing sits beside it to have
   its spacing pulse, which is why the slot is start-aligned everywhere else. */
@media (max-width: 640px) {
  .headline {
    font-size: clamp(1.9rem, 9vw, 3rem);
    letter-spacing: -0.03em;
  }
  .headline__line { white-space: normal; }
  .hl-part { display: block; }

  .rotator {
    display: grid;
    width: auto;
    margin-left: 0;
    justify-items: center;
    justify-content: center;
  }

  .cta { padding: 1em 1.85em; }
}

/* ---------- perf / accessibility ---------- */

.is-paused .col__track,
.is-paused .card__media { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .col__track, .card__media { animation: none !important; }
  .headline__line { opacity: 1; transform: none; transition: none; }
  .rotator__word { transition: none; }
}

/* ---------- navigation ----------
   A light pill floating on the black. The two pale palette colours make the
   surface; the deep violet carries the one action. */

.nav {
  position: fixed;
  top: clamp(12px, 1.6vw, 22px);
  left: 0;
  right: 0;
  z-index: 30;
  display: flex;
  justify-content: center;
  padding: 0 clamp(12px, 3vw, 28px);
  pointer-events: none;
}

.nav__pill {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 40px);
  width: min(1180px, 100%);
  padding: 10px 10px 10px clamp(18px, 2vw, 26px);
  border-radius: 999px;
  background: linear-gradient(102deg, var(--lav) 0%, var(--peri) 58%, #eddcff 100%);
  box-shadow:
    0 0 0 6px rgba(224, 175, 255, 0.09),
    0 18px 46px -18px rgba(0, 0, 0, 0.85);
  pointer-events: auto;
}

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

.nav__logo img {
  display: block;
  width: auto;
  height: clamp(30px, 3.1vw, 40px);
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 34px);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.nav__links a {
  position: relative;
  display: inline-block;
  padding: 6px 2px;
  color: var(--ink);
  font-size: clamp(0.875rem, 1vw, 1rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  text-decoration: none;
}

/* underline grows from the left rather than fading in — quieter than a colour change */
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 2px;
  height: 1.5px;
  width: 100%;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 260ms cubic-bezier(0.22, 0.85, 0.24, 1);
}

.nav__links a:hover::after,
.nav__links a:focus-visible::after { transform: scaleX(1); }

.nav__cta {
  flex: 0 0 auto;
  padding: 0.85em 1.6em;
  border-radius: 999px;
  background: var(--violet-deep);
  color: #fff;
  font-size: clamp(0.85rem, 0.95vw, 0.9375rem);
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 220ms ease, transform 220ms cubic-bezier(0.22, 0.85, 0.24, 1);
}

.nav__cta:hover { background: #7551d0; transform: translateY(-1px); }

.nav a:focus-visible,
.cta:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; }

.nav__pill a:focus-visible { outline-color: var(--ink); }

/* Below this width the three links crowd the button; keep the logo and the
   action, which are the two things that matter. */
@media (max-width: 720px) {
  .nav__links { display: none; }
  .nav__pill { justify-content: space-between; }
}

/* ---------- client logos ----------
   A quiet band, not a section: a label and a row. The logos arrive already
   normalised to light monochrome, so no filter is needed here — they sit at
   reduced opacity and come up to full on hover. */

/* The middle of the page is solid black. */
.clients,
.section {
  position: relative;
  z-index: 1;
  background: var(--bg);
}

/* Closer + footer sit on the outro's wall, so they carry no fill of their own. */
.outro {
  position: relative;
  overflow: hidden;
  background: var(--bg);
}

.closer,
.footer {
  position: relative;
  z-index: 2;
}

.clients {
  padding: clamp(52px, 7vw, 88px) clamp(20px, 6vw, 96px) clamp(20px, 3vw, 40px);
}

.clients__label {
  margin: 0 0 clamp(26px, 3.4vw, 44px);
  color: rgba(224, 175, 255, 0.62);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  text-align: center;
}

/* The marquee runs on exactly the same principle as the video wall: the track
   holds two identical copies of the list, and one cycle translates it by
   precisely -50%, so copy two lands where copy one began. Spacing is
   margin-right on every item rather than flex `gap` — with `gap` the two copies
   are separated by one gap less than the internal spacing, and the loop drifts
   by half a gap on every pass. */

.marquee {
  position: relative;
  overflow: hidden;
  /* soft edges instead of a hard cut — the one flourish in this band */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 9%, #000 91%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 9%, #000 91%, transparent 100%);
}

.marquee__track {
  display: flex;
  align-items: center;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  animation: marquee-left var(--marquee-dur, 96s) linear infinite;
  will-change: transform;
  backface-visibility: hidden;
}

@keyframes marquee-left {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track { animation-play-state: paused; }

/* Reduced motion stops the crawl. The track holds repeated copies of the list
   for the loop, so once it isn't moving it also has to stop overflowing and
   wrap instead — otherwise everything past the first screenful is parked out of
   reach with no way to scroll to it. The hero walls and the reel rail already
   honoured this setting; these two rows were the ones still moving. */
@media (prefers-reduced-motion: reduce) {
  .marquee { -webkit-mask-image: none; mask-image: none; }

  .marquee__track {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
    row-gap: clamp(18px, 2.4vw, 30px);
  }

  /* the duplicate copies exist only to make the loop seamless */
  .marquee__track li[data-clone] { display: none; }
}


/* Each item hugs its logo rather than sitting in a fixed-width cell. A fixed
   cell looks tidy in the CSS but reads badly here: a square seal only fills
   half of it, and the leftover cell space becomes gap you can't control. With
   fit-content items the margin below IS the visible gap, and logo strips read
   better on even optical spacing than on an even mechanical grid anyway.

   Consistency instead comes from a shared height plus a max-width, so the wide
   wordmarks and the square seals carry the same visual weight. */
.marquee__track li {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(56px, 5.9vw, 76px);
  margin-right: clamp(36px, 4.3vw, 62px);
}

/* Scoped to the logo strip. Unscoped, `.marquee__track img` outranks
   `.voice__face` on specificity and blew the testimonial avatars up to 178px. */
.clients .marquee__track img {
  max-height: 100%;
  max-width: clamp(124px, 12.5vw, 178px);
  width: auto;
  height: auto;
  object-fit: contain;
  /* flatten every mark to pure white, whatever tone it arrived in. The source
     files keep their internal transparency, so holes stay holes — CTeen was
     rebuilt to knock its lettering out of its box precisely so it survives
     this rather than flattening into a solid rectangle. */
  filter: brightness(0) invert(1);
  opacity: 0.78;
  transition: opacity 300ms ease;
}

.clients .marquee__track img:hover { opacity: 1; }

/* ---------- the proof ----------
   Three numbers as a full moment. No cards, no icons, no boxes — the weight
   comes from scale and space. One hairline runs the width of the block and the
   figures hang from it, so the three read as a single composition rather than
   three unrelated facts; a highlight travels that rail, borrowing the same
   vocabulary as the buttons. */

.proof {
  position: relative;
  z-index: 1;
  overflow: hidden;
  padding: clamp(56px, 7vw, 98px) clamp(20px, 6vw, 96px);
  background: var(--bg);
}

/* One soft bloom behind the figures — low enough to read as depth rather than a
   colour wash. It drifts with --p, which JS sets from scroll position. */
.proof__bloom {
  position: absolute;
  inset: -20% -10%;
  pointer-events: none;
  background: radial-gradient(
    ellipse 46% 52% at 50% 50%,
    rgba(136, 102, 222, 0.17) 0%,
    rgba(136, 102, 222, 0.05) 45%,
    transparent 74%
  );
  transform:
    translate3d(0, calc(var(--p, 0) * -30px), 0)
    scale(calc(1 + var(--p, 0) * 0.05));
  will-change: transform;
}

.proof__inner {
  position: relative;
  /* same 1180 column as How it works, so the rail lines up with the page */
  max-width: 1180px;
  margin: 0 auto;
  /* the group drifts against the bloom — parallax, barely felt */
  transform: translate3d(0, calc(var(--p, 0) * 12px), 0);
}

.proof__rail {
  position: relative;
  height: 1px;
  margin-bottom: clamp(28px, 3.2vw, 44px);
  background: linear-gradient(
    90deg,
    rgba(224, 175, 255, 0.05) 0%,
    rgba(224, 175, 255, 0.3) 18%,
    rgba(224, 175, 255, 0.3) 82%,
    rgba(224, 175, 255, 0.05) 100%
  );
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1200ms cubic-bezier(0.16, 1, 0.3, 1);
}

.proof.is-in .proof__rail { transform: scaleX(1); }

.proof__spark {
  position: absolute;
  top: 0;
  left: 0;
  height: 1px;
  width: 20%;
  background: linear-gradient(90deg, transparent, var(--lav), transparent);
  opacity: 0;
}

.proof.is-in .proof__spark { animation: proof-spark 7.5s ease-in-out 1.4s infinite; }

@keyframes proof-spark {
  0%, 6%    { transform: translateX(-40%); opacity: 0; }
  12%       { opacity: 0.85; }
  44%       { transform: translateX(440%); opacity: 0.85; }
  50%, 100% { transform: translateX(440%); opacity: 0; }
}

.proof__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 64px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.proof__item {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.2vw, 16px);
}

/* The three-up centres each figure over its own column. Not applied to
   .proof--single on the referrals page: there the figure hangs off the left of
   the masthead above it, and centring one lone number would break that line. */
.proof:not(.proof--single) .proof__item {
  align-items: center;
  text-align: center;
}

.proof__num {
  font-size: clamp(2.5rem, 4.6vw, 4.25rem);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: -0.055em;
  /* The gradient is painted with background-clip: text, so anything the glyphs
     draw outside the element's box simply isn't painted — and the tracking above
     is negative, which pulls the box in 3.7px tighter than the last character's
     advance. That was enough to slice the bottom-right ring off the % in 100%.
     Equal padding on both sides widens the paint area without moving the
     centring. */
  padding: 0 0.1em;
  /* tabular figures, or the width jitters while it counts */
  font-variant-numeric: tabular-nums;
  background: linear-gradient(150deg, var(--paper) 6%, var(--lav) 62%, var(--orchid) 108%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0;
  transform: translate3d(0, 0.14em, 0);
  transition:
    opacity 900ms ease,
    transform 1100ms cubic-bezier(0.16, 1, 0.3, 1);
}

.proof__label {
  font-size: clamp(0.75rem, 0.86vw, 0.8125rem);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(251, 250, 248, 0.55);
  opacity: 0;
  transform: translate3d(0, 0.8rem, 0);
  transition:
    opacity 800ms ease,
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* the three arrive in sequence, reading left to right, label behind its figure */
.proof__item:nth-child(2) .proof__num { transition-delay: 120ms; }
.proof__item:nth-child(3) .proof__num { transition-delay: 240ms; }
.proof__label { transition-delay: 220ms; }
.proof__item:nth-child(2) .proof__label { transition-delay: 340ms; }
.proof__item:nth-child(3) .proof__label { transition-delay: 460ms; }

.proof.is-in .proof__num,
.proof.is-in .proof__label {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

@media (max-width: 760px) {
  /* stacked, each figure its own beat */
  .proof__list { grid-template-columns: 1fr; gap: clamp(30px, 7vw, 46px); }
  .proof__num { font-size: clamp(2.6rem, 11.5vw, 3.6rem); }
}

@media (prefers-reduced-motion: reduce) {
  .proof__bloom,
  .proof__inner { transform: none; }
  .proof__rail { transform: scaleX(1); transition: none; }
  .proof__spark { display: none; }
  .proof__num,
  .proof__label { opacity: 1; transform: none; transition: none; }
}

/* ---------- content that stops the scroll ----------
   Asymmetric on purpose: the rail runs off the left edge of the screen rather
   than sitting in a tidy box, and the copy holds the same right-hand margin as
   every other section so the page still lines up down that side.

   Same loop maths as everything else on this page — two identical copies of the
   card list, one cycle translating exactly 50% of the track, and margin rather
   than flex gap so the period stays exact. The only difference is the direction:
   this one runs -50% → 0, which reads as left-to-right travel. */

.stopper {
  position: relative;
  z-index: 1;
  overflow: hidden;
  padding: clamp(20px, 2.2vw, 30px) 0 clamp(22px, 2.4vw, 34px);
  background: var(--bg);
}

/* Three tall blocks in a row, each with a full section's padding on both sides
   of every join, left ~270px of black between them — and even trimmed once, the
   rail still sat in ~155px of empty black top and bottom. These hold each join
   to about 100px. (:has is a progressive nicety — without it the spacing is
   merely generous, not broken.) */
.proof:has(+ .stopper) { padding-bottom: clamp(24px, 2.8vw, 38px); }
.stopper + .section { padding-top: clamp(20px, 2.4vw, 34px); }

.stopper__inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  align-items: center;
  gap: clamp(28px, 4vw, 64px);
  /* Matches the 1180 column the rest of the page centres on, so the copy's
     right edge sits exactly under How it works'.

     The LEFT padding has to match it, and that is not cosmetic. The grid's
     columns are fractions of the box inside this padding, while the rail behind
     it is absolutely positioned across the whole viewport and masked at a fixed
     45% of that full width. Pad only the right and the two drift apart as the
     screen grows: at 1920 the copy column started at 822px while the mask did
     not finish clearing until 864px, so a card sat behind "We make content that
     stops the scroll". Padding both sides keeps the copy in the same 1180
     column at every width, well clear of the mask. */
  padding-right: max(clamp(20px, 6vw, 96px), calc((100vw - 1180px) / 2));
  padding-left: max(clamp(20px, 6vw, 96px), calc((100vw - 1180px) / 2));
  /* the rail is absolute, so the block stands on its own height */
  min-height: clamp(300px, 26vw, 380px);
}

/* the first column is the rail's; the copy takes the second */
.stopper__copy { grid-column: 2; }

/* The rail runs the full width of the screen behind the copy, and the mask
   decides how much of it you see: a full column of cards travelling in on the
   left, nothing behind the words, and a sliver at the right edge — just enough
   that the strip reads as carrying on past the text rather than stopping at
   it. */
.reel {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0%, #000 3%, #000 38%, transparent 45%,
    transparent 89.5%, #000 96%, #000 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0%, #000 3%, #000 38%, transparent 45%,
    transparent 89.5%, #000 96%, #000 100%
  );
}

.reel__track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: reel-right var(--reel-dur, 44s) linear infinite;
  will-change: transform;
  backface-visibility: hidden;
}

@keyframes reel-right {
  from { transform: translate3d(-50%, 0, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

.reel:hover .reel__track { animation-play-state: paused; }
.is-paused .reel__track { animation-play-state: paused; }

/* The wall's grade is a background treatment. Down here the footage IS the
   subject, so these cards run clean and at full opacity. */
.reel .card {
  width: clamp(150px, 15vw, 224px);
  margin: 0 clamp(10px, 1.1vw, 16px) 0 0;
  opacity: 1;
}

.reel .card__media { filter: none; }

.stopper__copy {
  position: relative;
  z-index: 2;
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition:
    opacity 900ms ease,
    transform 1100ms cubic-bezier(0.16, 1, 0.3, 1);
}

.stopper.is-in .stopper__copy {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Heavier than the other section headings on the page, and deliberately so —
   this is the claim the section exists to make, and the type is carrying it. */
.stopper__title {
  margin: 0;
  /* no measure cap — at 13ch the lines broke ~200px short of the column and left
     a band of black down the right of the page */
  font-size: clamp(2.15rem, 4.3vw, 3.75rem);
  font-weight: 700;
  line-height: 1.02;
  /* tightened as the weight goes up: heavy type at default tracking reads loose */
  letter-spacing: -0.042em;
  color: var(--paper);
}

/* the one phrase the section exists for — same gradient as the figures above,
   so the two moments on this page that carry colour are related */
.stopper__accent {
  background: linear-gradient(120deg, var(--lav) 8%, var(--orchid) 92%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .stopper__accent { color: var(--lav); background: none; }
}

.stopper__body {
  margin: clamp(18px, 2vw, 26px) 0 0;
  /* wide enough to reach across its column, still inside a comfortable measure */
  max-width: 56ch;
  font-size: clamp(0.9688rem, 1.05vw, 1.0625rem);
  line-height: 1.62;
  letter-spacing: -0.006em;
  color: rgba(251, 250, 248, 0.62);
}

/* Under ~900px there's no room for a column of cards beside the words, so the
   composition stacks: the rail becomes a full-bleed band above the copy and
   fades evenly at both ends rather than trailing off to one side. */
@media (max-width: 900px) {
  /* The desktop trim is aggressive because that layout has the rail and the copy
     side by side, filling the width. Stacked, the same numbers read as cramped —
     these joins stay closer to the rest of the page's phone rhythm. */
  .stopper { padding: clamp(34px, 7vw, 46px) 0 clamp(36px, 7.5vw, 50px); }
  .proof:has(+ .stopper) { padding-bottom: clamp(34px, 8vw, 48px); }
  .stopper + .section { padding-top: clamp(34px, 8vw, 48px); }

  .stopper__inner {
    grid-template-columns: 1fr;
    /* Both sides, not just the right. Stacked, .stopper__copy supplies its own
       gutter — leaving the desktop padding-left on would add a second one and
       push this section's text in twice as far as every other section's. */
    padding-right: 0;
    padding-left: 0;
    min-height: 0;
  }

  .reel {
    position: relative;
    inset: auto;
    /* explicit: out of the absolute layout it otherwise sizes to the track,
       which is thousands of pixels wide */
    width: 100%;
    margin-bottom: clamp(30px, 6vw, 48px);
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
  }

  .stopper__copy {
    grid-column: auto;
    padding: 0 clamp(20px, 6vw, 96px);
  }

  .stopper__title { max-width: 16ch; }
}

@media (prefers-reduced-motion: reduce) {
  .reel__track { animation: none; }
  .stopper__copy { opacity: 1; transform: none; transition: none; }
}

/* ---------- skip link ----------
   Off screen until focused, then lands over the nav pill. The first thing a
   keyboard or screen-reader user meets, so it can't be a decorative afterthought. */
.skip {
  position: fixed;
  top: 14px;
  left: 50%;
  z-index: 60;
  transform: translate(-50%, -160%);
  padding: 0.85em 1.6em;
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
  transition: transform 180ms ease;
}

.skip:focus { transform: translate(-50%, 0); }

/* the target takes focus programmatically; it should never show a stray ring */
main:focus { outline: none; }

/* ---------- legal pages ----------
   Privacy, terms, accessibility. Same editorial frame as the booking page — a
   masthead on a rule, then a single measured column. Long prose, so the measure
   is tighter than anywhere else on the site and the type is a shade larger. */

/* Sits in the page's 1180 column like the masthead above it, but the prose
   inside is held to a reading measure. Constraining .legal itself to 68ch
   centred it instead, so its left edge drifted 75px off the headline's. */
.legal {
  max-width: 1180px;
  margin: 0 auto;
}

.legal > * { max-width: 68ch; }

.legal h2 {
  margin: clamp(38px, 4.4vw, 58px) 0 clamp(12px, 1.4vw, 18px);
  font-size: clamp(1.25rem, 1.7vw, 1.5rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.022em;
  color: var(--paper);
}

.legal h3 {
  margin: clamp(24px, 2.6vw, 32px) 0 0.5em;
  font-size: clamp(1rem, 1.15vw, 1.0625rem);
  font-weight: 600;
  letter-spacing: -0.012em;
  color: rgba(251, 250, 248, 0.9);
}

.legal p,
.legal li {
  font-size: clamp(1rem, 1.08vw, 1.0625rem);
  line-height: 1.68;
  letter-spacing: -0.006em;
  color: rgba(251, 250, 248, 0.68);
}

.legal p { margin: 0 0 1.1em; }

.legal ul { margin: 0 0 1.3em; padding-left: 1.15em; }
.legal li { margin-bottom: 0.6em; }
.legal li::marker { color: rgba(224, 175, 255, 0.5); }

.legal a {
  color: rgba(224, 175, 255, 0.92);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(224, 175, 255, 0.35);
}

.legal a:hover { color: var(--lav); text-decoration-color: var(--lav); }

.legal strong { color: var(--paper); font-weight: 600; }

/* the "last updated" line, and the standing note that this isn't legal advice */
.legal__meta {
  margin: 0 0 clamp(30px, 3.4vw, 44px);
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  color: rgba(251, 250, 248, 0.55);
}

/* a fact the reader should not have to hunt for */
.legal__callout {
  margin: clamp(26px, 3vw, 38px) 0;
  padding: clamp(20px, 2.2vw, 28px) clamp(22px, 2.4vw, 30px);
  border-left: 2px solid rgba(224, 175, 255, 0.4);
  background: rgba(255, 255, 255, 0.028);
  border-radius: 0 12px 12px 0;
}

.legal__callout p:last-child { margin-bottom: 0; }

/* ---------- book a call ----------
   Its own page (book-call.html), in the site's clothes rather than Calendly's.

   Deliberately NOT the split-hero this started as — gradient headline on the
   left, glowing card on the right, three feature rows underneath. That layout is
   the house style of every AI-built SaaS page going, and the violet bloom behind
   it was the giveaway. This is an editorial page instead: a plain headline
   across the top, the scheduler as the body copy, and the practical notes set
   small in a margin column beside it, the way a printed page handles asides.

   The gradient stays on the home page, where it appears twice. Three times and
   it stops being a signature and starts being a tic. */

.booking {
  position: relative;
  z-index: 1;
  /* clears the fixed nav pill */
  padding: clamp(104px, 12vw, 150px) clamp(20px, 6vw, 96px) clamp(56px, 7vw, 96px);
  background: var(--bg);
}

.booking__head {
  max-width: 1180px;
  margin: 0 auto clamp(30px, 4vw, 52px);
  /* a rule the head sits on, like a masthead */
  padding-bottom: clamp(22px, 2.6vw, 34px);
  border-bottom: 1px solid rgba(224, 175, 255, 0.16);
}

.booking__title {
  margin: 0;
  max-width: 22ch;
  /* smaller and lighter than the home page's headline: this page is a form,
     not a claim, and shouting at someone already committed to booking is noise */
  font-size: clamp(1.85rem, 3.1vw, 2.65rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.032em;
  color: var(--paper);
}

.booking__lead {
  margin: clamp(14px, 1.6vw, 20px) 0 0;
  max-width: 56ch;
  font-size: clamp(1rem, 1.1vw, 1.0625rem);
  line-height: 1.58;
  letter-spacing: -0.008em;
  color: rgba(251, 250, 248, 0.62);
}

.booking__body {
  display: grid;
  /* the scheduler is the page; the notes are a margin */
  grid-template-columns: minmax(0, 1fr) minmax(220px, 300px);
  align-items: start;
  gap: clamp(28px, 4vw, 60px);
  max-width: 1180px;
  margin: 0 auto;
}

/* Calendly renders in a cross-origin iframe, so none of it can be styled from
   here — and its background_color / text_color params are a paid-plan feature a
   free account silently ignores, so the scheduler arrives white whatever we pass
   it. The panel leans into that: one light surface, squared off against the
   page, with a plain shadow rather than a coloured glow. */
.booking__panel {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 30px 70px -45px rgba(0, 0, 0, 0.95);
}

.calendly-inline-widget { min-height: 720px; }

/* Asides, set the way a margin note is set: small, quiet, no rules between
   them, no bold-label-plus-grey-line card rhythm. */
.booking__notes dl {
  margin: 0;
  padding-top: 0.35rem;
}

.booking__notes div + div { margin-top: clamp(20px, 2.2vw, 28px); }

.booking__notes dt {
  margin-bottom: 0.35em;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: rgba(251, 250, 248, 0.92);
}

.booking__notes dd {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.55;
  color: rgba(251, 250, 248, 0.5);
}

.booking__notes a {
  color: rgba(224, 175, 255, 0.9);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(224, 175, 255, 0.35);
}

.booking__notes a:hover { color: var(--lav); text-decoration-color: var(--lav); }

/* Shown only if the widget script never arrives — an ad blocker, or no network.
   A booking page that silently shows an empty box is worse than a plain link. */
.booking__fallback {
  padding: clamp(28px, 4vw, 44px);
  text-align: center;
  font-size: 1rem;
  color: rgba(20, 18, 26, 0.7);
}

.booking__fallback a { color: var(--violet-deep); }

/* ---------- referrals ----------
   Shares the booking page's editorial frame. The one thing it adds is the
   figure, which reuses the home page's own treatment for numbers rather than
   inventing a third look for one — hung off a hairline, counted up on arrival. */

/* On this page the figure follows the masthead directly, so the masthead drops
   its rule and the figure's own rail becomes the divider — one hairline doing
   both jobs instead of two sitting 100px apart. */
.booking__head:has(+ .proof--single) {
  padding-bottom: 0;
  border-bottom: 0;
  margin-bottom: clamp(26px, 3.4vw, 44px);
}

.proof--single {
  padding: 0 0 clamp(26px, 3.4vw, 44px);
}

.proof--single .proof__inner { max-width: 1180px; }
.proof--single .proof__list { grid-template-columns: 1fr; }
.proof--single .proof__num { font-size: clamp(3rem, 6.4vw, 5.75rem); }

.proof--single .proof__label {
  /* a caption on the figure, not a column header — sentence case, no tracking */
  font-size: clamp(0.9375rem, 1.05vw, 1.0625rem);
  font-weight: 400;
  letter-spacing: -0.006em;
  text-transform: none;
  color: rgba(251, 250, 248, 0.58);
}

.refer__heading {
  margin: 0 0 clamp(12px, 1.4vw, 18px);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(224, 175, 255, 0.7);
}

.refer__line {
  margin: 0;
  max-width: 54ch;
  font-size: clamp(1.0625rem, 1.25vw, 1.25rem);
  line-height: 1.58;
  letter-spacing: -0.012em;
  color: rgba(251, 250, 248, 0.78);
}

.refer__line b { font-weight: 600; color: var(--paper); }

.refer__line--muted {
  margin-top: clamp(14px, 1.6vw, 20px);
  font-size: clamp(0.9375rem, 1vw, 1rem);
  color: rgba(251, 250, 248, 0.5);
}

.refer__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(14px, 1.8vw, 24px);
  margin-top: clamp(26px, 3.2vw, 40px);
}

/* the button carries a hero-sized top margin of its own; the row sets the gap */
.refer__actions .cta { margin-top: 0; }

/* the quiet second option — a link, not a second button competing with the first */
.refer__alt {
  font-size: 0.9375rem;
  color: rgba(251, 250, 248, 0.55);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(251, 250, 248, 0.25);
}

.refer__alt:hover { color: var(--paper); text-decoration-color: rgba(251, 250, 248, 0.6); }

@media (max-width: 900px) {
  .booking__body { grid-template-columns: 1fr; gap: clamp(26px, 6vw, 40px); }
  .calendly-inline-widget { min-height: 960px; }
  .booking__notes dl { display: grid; gap: clamp(18px, 4vw, 24px); }
  .booking__notes div + div { margin-top: 0; }

  /* Stacked, the notes belong ahead of the SCHEDULER — below it they landed
     1350px down the page, where "why some days are missing" is no use to someone
     who already went hunting for a date. Scoped to the page that has a panel:
     on referrals the notes are footnotes to the explanation and the button, and
     hoisting them pushed the actual call to action below the small print. */
  .booking__body:has(.booking__panel) .booking__notes { order: -1; }
}

/* ---------- sections ---------- */

.section {
  padding: clamp(72px, 11vw, 148px) clamp(20px, 6vw, 96px);
}

.section + .section { padding-top: 0; }

.section__head {
  max-width: 1180px;
  margin: 0 auto clamp(34px, 5vw, 62px);
}

.eyebrow {
  margin: 0 0 0.75rem;
  color: var(--lav);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.section__title {
  margin: 0;
  max-width: 18ch;
  font-size: clamp(1.85rem, 4vw, 3.25rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--paper);
}

/* ---------- how it works ----------
   Same treatment as before: plain columns divided by a hairline, no cards, no
   icons. The one addition is a small step number — this IS a sequence now, and
   the order is information the reader needs. */

.section__lead {
  margin: clamp(18px, 2vw, 26px) 0 0;
  max-width: 58ch;
  font-size: clamp(1.0625rem, 1.3vw, 1.3125rem);
  line-height: 1.55;
  letter-spacing: -0.012em;
  color: rgba(251, 250, 248, 0.66);
}

.pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
  gap: clamp(30px, 4vw, 62px);
  max-width: 1180px;
  margin: 0 auto;
}

.pillar {
  padding-top: clamp(18px, 2vw, 26px);
  border-top: 1px solid rgba(224, 175, 255, 0.22);
}

.pillar__step {
  display: block;
  margin-bottom: 0.9em;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
  color: rgba(224, 175, 255, 0.72);
}

.pillar__title {
  margin: 0 0 0.6em;
  font-size: clamp(1.05rem, 1.25vw, 1.25rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.022em;
  color: var(--paper);
}

.pillar__body {
  margin: 0;
  /* held to a comfortable measure — long lines are the main thing that makes
     body copy on a dark background tiring to read */
  max-width: 38ch;
  font-size: clamp(0.9375rem, 1vw, 1rem);
  line-height: 1.62;
  letter-spacing: -0.004em;
  color: rgba(251, 250, 248, 0.62);
}


/* ---------- testimonials ----------
   Two rows of cards crawling right to left, on the same marquee engine as the
   client logos: two copies of the list, an exact -50% translate, and margin
   rather than flex gap so the period stays exact. */

/* the rows run edge to edge, so the section's side padding only holds the head */
.section--flush { padding-left: 0; padding-right: 0; }

/* The row runs straight into the closing block, so this section's tail is
   trimmed hard — a full section's worth of padding on both sides of that join
   left ~400px of dead black between them. */
#testimonials { padding-bottom: clamp(26px, 3.2vw, 48px); }
.section--flush .section__head { padding: 0 clamp(20px, 6vw, 96px); }

.voices {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.1vw, 15px);
}

/* the two rows run at different speeds, so they never lock into step */
.marquee--voices .marquee__track { align-items: stretch; }

.marquee--voices .marquee__track li {
  width: clamp(300px, 28vw, 440px);
  height: auto;
  margin-right: clamp(12px, 1.3vw, 18px);
  align-items: stretch;
}

/* Every card is the same height by construction, not by luck: the header is
   pinned to the avatar's height and the quote always occupies exactly three
   lines whether it fills them or not. */
.voice {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  width: 100%;
  height: 100%;
  padding: clamp(18px, 1.6vw, 24px) clamp(19px, 1.7vw, 26px);
  border-radius: 18px;
  background: #0c0c10;
  box-shadow: inset 0 0 0 1px rgba(224, 175, 255, 0.1);
}

.voice__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: 44px;   /* = the avatar, so a one- or two-line role reads the same */
}

.voice__face {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(224, 175, 255, 0.2);
}

.voice__who { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }

.voice__name {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--paper);
}

.voice__role {
  font-size: 0.8rem;
  line-height: 1.3;
  color: rgba(224, 175, 255, 0.64);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Quotes run in full — no clamp. The row stretches every card to the tallest,
   so the longest testimonial sets the height and the rest match it exactly. */
.voice__text {
  margin: 0;
  font-size: 0.9625rem;
  line-height: 1.55;
  letter-spacing: -0.008em;
  color: rgba(251, 250, 248, 0.7);
}

/* ---------- closing CTA ----------
   The one light surface on the page besides the nav. Using the same palette
   gradient as the nav pill makes the page open and close on the same note, and
   after a long stretch of black it's the strongest possible cue that this is
   where you act. */

/* Transparent, so the fixed wall behind shows through. The scrim is heavier
   here than in the hero: at the foot of the page the footage is texture, not
   the subject, and the type has to win outright. */
.closer {
  padding: clamp(44px, 5.4vw, 82px) clamp(16px, 4vw, 48px) clamp(40px, 5vw, 72px);
}

/* The transition. Full black where the content above ends, opening up through
   the CTA and staying open behind the footer — the footer card is opaque, so it
   reads as a panel floating on the footage. */
.outro__fade {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(
      to bottom,
      var(--bg) 0%,
      rgba(0, 0, 0, 0.95) 9%,
      rgba(0, 0, 0, 0.72) 24%,
      rgba(0, 0, 0, 0.58) 50%,
      rgba(0, 0, 0, 0.6) 100%
    ),
    /* edges pulled down so the wall never meets a hard border */
    radial-gradient(ellipse 82% 88% at 50% 45%, rgba(0,0,0,0) 44%, rgba(0,0,0,0.5) 100%);
}

/* No panel — the type sits straight on the page black. A very soft violet bloom
   behind it gives the block presence without drawing a hard edge. */
.closer__panel {
  position: relative;
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(26px, 3.6vw, 54px) clamp(16px, 4vw, 48px);
  text-align: center;
}

.closer__panel::before {
  content: '';
  position: absolute;
  inset: -20% -10%;
  background: radial-gradient(
    ellipse 52% 58% at 50% 50%,
    rgba(136, 102, 222, 0.16) 0%,
    rgba(136, 102, 222, 0.05) 48%,
    transparent 76%
  );
  pointer-events: none;
}

.closer__panel > * { position: relative; }

.closer__eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  margin: 0 0 clamp(18px, 2.2vw, 30px);
  color: rgba(224, 175, 255, 0.72);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.closer__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--orchid);
}

.closer__title {
  margin: 0 auto;
  max-width: 16ch;
  font-size: clamp(2rem, 5vw, 4.25rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: var(--paper);
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.42);
}

.closer__title em {
  font-style: normal;
  /* orchid rather than the deep violet: on black the deep shade goes muddy,
     and this reaches 6.2:1 against the page at display size */
  color: var(--orchid);
}

/* A bright palette gradient with near-black text. White text on the orchid end
   only reaches 2.6:1 and fails outright; dark ink on it reaches 6.2:1 — so the
   button gets to be genuinely colourful and still readable. */
.closer__cta {
  display: inline-flex;
  align-items: center;
  margin-top: clamp(28px, 3.6vw, 48px);
  padding: 1.1em 2.4em;
  border-radius: 999px;
  background: linear-gradient(104deg, var(--lav) 0%, var(--orchid) 100%);
  color: var(--ink);
  font-size: clamp(0.95rem, 1.05vw, 1.0625rem);
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  box-shadow: 0 14px 40px -14px rgba(221, 104, 227, 0.5);
  transition: transform 240ms cubic-bezier(0.22, 0.85, 0.24, 1), box-shadow 240ms ease;
}

.closer__cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 20px 52px -14px rgba(221, 104, 227, 0.7);
}

.closer__cta:focus-visible { outline: 2px solid var(--lav); outline-offset: 4px; }

/* ---------- scroll reveal ----------
   The closing block was the one part of the page with no movement at all. Its
   three pieces rise into place when the section comes into view, staggered, and
   only once — a reveal that replays on every scroll-past reads as a nervous tic. */

.closer__eyebrow,
.closer__title,
.closer__cta {
  opacity: 0;
  transform: translate3d(0, 1.15rem, 0);
  transition:
    opacity 850ms ease,
    transform 950ms cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 240ms ease;
}

.closer__title { transition-delay: 90ms; }
.closer__cta { transition-delay: 190ms; }

.closer.is-in .closer__eyebrow,
.closer.is-in .closer__title,
.closer.is-in .closer__cta {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.closer.is-in .closer__cta:hover { transform: translate3d(0, -2px, 0); }

@media (prefers-reduced-motion: reduce) {
  .closer__eyebrow,
  .closer__title,
  .closer__cta { opacity: 1; transform: none; transition: none; }
}

/* ---------- footer ---------- */

/* A rounded panel rather than a full-bleed band. Your reference has a black card
   on a light page; here the page is already black, so the card is lifted a shade
   ABOVE it with a hairline ring — otherwise the corners would be invisible. */
.footer {
  padding: 0 clamp(16px, 4vw, 48px) clamp(24px, 3vw, 44px);
}

.footer__card {
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(34px, 4.4vw, 62px) clamp(26px, 3.6vw, 60px) clamp(24px, 2.8vw, 40px);
  border-radius: clamp(20px, 2.4vw, 34px);
  /* solid black — it reads as a panel because the wall is running behind it */
  background: var(--bg);
  box-shadow: inset 0 0 0 1px rgba(224, 175, 255, 0.08);
}

.footer__inner {
  display: grid;
  /* brand, then three link columns — quick links, legal, contact */
  grid-template-columns: 1.25fr 0.85fr 0.85fr 1fr;
  gap: clamp(28px, 3.6vw, 56px);
}

/* four columns is tight at tablet widths; two rows of two reads better than
   four cramped ones */
@media (max-width: 1080px) and (min-width: 721px) {
  .footer__inner { grid-template-columns: 1fr 1fr; }
}

/* contact rows carry a small line icon, as in the reference */
.footer__contact a {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
}

.footer__contact svg {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.75;
}

.footer__logo {
  display: block;
  width: auto;
  height: clamp(38px, 4vw, 52px);
}

.footer__social {
  display: flex;
  gap: 14px;
  margin: clamp(20px, 2.4vw, 30px) 0 0;
  padding: 0;
  list-style: none;
}

.footer__social a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  opacity: 0.72;
  transition: opacity 220ms ease, background-color 220ms ease;
}

.footer__social a:hover { opacity: 1; background: rgba(224, 175, 255, 0.14); }
.footer__social img { width: 21px; height: 21px; }

.footer__heading {
  margin: 0 0 clamp(14px, 1.6vw, 20px);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--paper);
}

.footer__col ul {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer__col a {
  color: rgba(251, 250, 248, 0.6);
  font-size: 0.9375rem;
  text-decoration: none;
  transition: color 200ms ease;
}

.footer__col a:hover { color: var(--paper); }

.footer__base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
  margin: clamp(36px, 4.4vw, 60px) 0 0;
  padding-top: clamp(20px, 2.4vw, 30px);
  border-top: 1px solid rgba(224, 175, 255, 0.14);
}

.footer__base p {
  margin: 0;
  color: rgba(251, 250, 248, 0.55);
  font-size: 0.875rem;
}

.footer a:focus-visible,
.closer a:focus-visible { outline-offset: 3px; }

@media (max-width: 720px) {
  .footer__inner { grid-template-columns: 1fr; }
  .footer__base { justify-content: flex-start; }
}

/* ---------- dev panel (press D) ---------- */

.dev {
  position: fixed;
  top: 16px; right: 16px;
  z-index: 20;
  width: 250px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: 14px 16px;
  border-radius: 12px;
  background: rgba(14, 14, 16, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(12px);
  font-size: 11px;
  letter-spacing: 0.02em;
  display: none;
}
.dev.is-open { display: block; }
.dev h4 { margin: 0 0 10px; font-size: 10px; text-transform: uppercase; letter-spacing: .14em; color: #8b8b93; font-weight: 600; }
.dev label { display: flex; justify-content: space-between; gap: 8px; margin: 7px 0 2px; color: #b9b9c2; }
.dev label b { color: #fff; font-weight: 500; font-variant-numeric: tabular-nums; }
.dev input[type=range] { width: 100%; accent-color: #fff; margin: 0 0 4px; }
.dev p { margin: 12px 0 0; color: #6c6c76; line-height: 1.5; }

/* ---------- FAQ ----------
   <details>/<summary> rather than JS accordions: it opens with no script, it is
   keyboard and screen-reader correct for free, and — the reason that matters
   here — the answer text is in the DOM whether the item is open or not, so a
   crawler reads all seven answers. A JS accordion that injects its answers on
   click would hide the entire section from search, which is the one thing this
   section exists to avoid. */
/* The outer width matches .pillars so the items start on the same left edge as
   the section heading above them; the inner cap is a comfortable measure to
   read an answer at, not a container width. */
.faq {
  max-width: 1180px;
  margin: 0 auto;
}

.faq__item + .faq__item,
.faq__item { max-width: 860px; }

.faq__item {
  border-top: 1px solid rgba(224, 175, 255, 0.22);
}

.faq__item:last-child { border-bottom: 1px solid rgba(224, 175, 255, 0.22); }

.faq__q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding: clamp(18px, 2.2vw, 26px) 0;
  font-family: var(--sans);
  font-size: clamp(1rem, 1.35vw, 1.18rem);
  font-weight: 500;
  line-height: 1.4;
  color: #fff;
  cursor: pointer;
  list-style: none;            /* Safari keeps a disclosure triangle without this */
  transition: color 200ms ease;
}

.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: #e6c2ff; }

/* The +/− is drawn rather than typed, so it can rotate instead of swapping
   glyphs and shifting the baseline. */
.faq__q::after {
  content: '';
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin-top: 0.34em;
  background:
    linear-gradient(currentColor, currentColor) center / 100% 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1.5px 100% no-repeat;
  transition: transform 260ms ease;
}

.faq__item[open] .faq__q::after { transform: rotate(45deg); }

.faq__a {
  padding: 0 clamp(30px, 4vw, 52px) clamp(20px, 2.4vw, 28px) 0;
  color: rgba(255, 255, 255, 0.66);
  font-size: clamp(0.94rem, 1.1vw, 1.02rem);
  line-height: 1.66;
}

.faq__a p { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .faq__q, .faq__q::after { transition: none; }
}

/* ---------- FAQ page ----------
   The homepage shows five questions; faq.html carries all fifteen, grouped, so
   a long list stays scannable instead of becoming a wall of disclosure arrows. */
.faq-page {
  padding-top: clamp(120px, 14vh, 190px);   /* clears the fixed nav pill */
}

.faq-group {
  max-width: 1180px;
  margin: 0 auto clamp(42px, 5vw, 70px);
}

.faq-group__title {
  max-width: 860px;
  margin: 0 0 clamp(6px, 1vw, 12px);
  font-family: var(--sans);
  font-size: clamp(0.78rem, 1vw, 0.86rem);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #e0afff;
}

.faq__more {
  max-width: 1180px;
  margin: clamp(26px, 3vw, 40px) auto 0;
}

.faq__more a,
.faq-page__cta a {
  color: #e0afff;
  text-decoration: none;
  border-bottom: 1px solid rgba(224, 175, 255, 0.4);
  padding-bottom: 2px;
  transition: border-color 200ms ease, color 200ms ease;
}

.faq__more a:hover,
.faq-page__cta a:hover { color: #fff; border-color: #fff; }

.faq-page__cta {
  max-width: 1180px;
  margin: 0 auto;
  color: rgba(255, 255, 255, 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .faq__more a, .faq-page__cta a { transition: none; }
}





/* ---------- audience chips ----------
   Who the work is for, as a row of tags rather than six icons with labels. It
   says more in a third of the height: eleven audiences instead of six, every
   one of them a term somebody actually searches, and none of it hidden behind
   an interaction or an illustration.

   Outlined rather than filled, in the page's lavender: these are labels, not
   buttons, and nothing here is clickable — a filled pill would promise a link
   that isn't there. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 0.9vw, 12px);
  max-width: 1180px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.chips li {
  /* padding moved onto the <a> for the linked ones so the whole pill is the
     hit area, not just the text inside it */
  padding: 0;
  border: 1px solid rgba(224, 175, 255, 0.34);
  border-radius: 999px;
  font-family: var(--sans);
  font-size: clamp(0.82rem, 0.95vw, 0.92rem);
  font-weight: 500;
  line-height: 1;
  color: var(--lav);
  white-space: nowrap;
}

.chips li > * { display: block; padding: 0.62em 1.15em; }
.chips li:not(:has(a)) { padding: 0.62em 1.15em; }

/* Four of these lead to an audience page. The hover is deliberately quiet —
   the row is a list of who we work with first and a menu second, and making
   four of eleven look like buttons would break the set. */
.chips a {
  color: inherit;
  text-decoration: none;
  border-radius: inherit;
  transition: background-color 200ms ease, color 200ms ease;
}

.chips a:hover,
.chips a:focus-visible {
  background: rgba(224, 175, 255, 0.14);
  color: #fff;
}

/* The section's own call to action. .cta brings the gradient pill from the
   hero; this only has to place it, and cancel the hero's centring context. */
.cta--inline {
  margin-top: clamp(30px, 4vw, 48px);
  /* Every sibling here is a 1180px block centred with `margin: 0 auto`, but an
     inline-flex button cannot be. Percentage margins resolve against the
     containing block, which IS the section's content box, so this reproduces
     the same left edge without wrapping the button in a div. */
  margin-left: max(0px, calc((100% - 1180px) / 2));
}

@media (max-width: 520px) {
  .chips li { font-size: 0.8rem; padding: 0.58em 1em; }
}

/* Five steps, not three. The generic .pillars grid has a 272px minimum, which
   fits four across the 1180 column and would have wrapped the fifth onto a row
   of its own — a pipeline that breaks after "Editing" reads as if the process
   stops there. A smaller minimum keeps all five in one line on desktop and
   folds them to three then two as the space goes. */
.pillars--five {
  grid-template-columns: repeat(auto-fit, minmax(186px, 1fr));
  gap: clamp(22px, 2.6vw, 38px);
}

@media (max-width: 1080px) {
  .pillars--five { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
}

/* The old #who-we-work-with anchor still lives in the markup so links to it do
   not dead-end. Offset by the sticky nav's height so a jump does not land with
   the heading hidden behind the pill. */
.anchor {
  position: relative;
  top: calc(-1 * clamp(96px, 14vh, 140px));
  height: 0;
}

/* ---------- FAQ: centred heading ----------
   The heading centres, and the accordion centres with it as a block — its text
   stays left-aligned inside, because centred question-and-answer copy is hard
   to read and the answers run several lines. Centring the head and leaving the
   list hard against the left edge is the mismatch this avoids. */
#faq .section__head { text-align: center; }
#faq .section__title { margin-inline: auto; }

#faq .faq { max-width: 880px; }
#faq .faq__item { max-width: none; }
#faq .faq__more { max-width: 880px; text-align: center; }

/* Sub-heading inside a page that already has its h1 — used on the audience
   pages for "How it works". Was removed when the homepage stopped needing it;
   the audience pages brought it back. */
.section__sub {
  max-width: 1180px;
  margin: clamp(56px, 7vw, 92px) auto clamp(24px, 3vw, 36px);
}

.section__subtitle {
  margin: 0 0 clamp(10px, 1.4vw, 18px);
  font-family: var(--sans);
  font-size: clamp(1.45rem, 2.5vw, 2.1rem);
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.02em;
  color: #fff;
}

.section__sub .section__lead + .section__lead { margin-top: 0.9em; }
