/* ==========================================================================
   /v3 — design direction 3, forked from /ecomm/v2.
   Contains ONLY copy present in "eCOMM _ SO SHALL WE LANDING PAGE COPY
   RECOMMENDATIONS.docx". Self-contained page stylesheet, loaded after
   styles.css, so it is not coupled to /ecomm/v2's v2.css.
   ========================================================================== */

/* ---------- Hero ---------- */

.hero--noVideo { padding-bottom: 88px; }
/* Matched to the section h2 (.section .eyebrow) so the two share a scale. */
.hero__h1--long { font-size: clamp(32px, 4.4vw, 54px); line-height: .98; }
/* No gap between the two sentences: with the leading set to the type size
   plus 4px, zero gap puts the second line exactly one line below the
   first — the same step a <br> would give. */
.hero__sub-stack { display: flex; flex-direction: column; gap: 0; max-width: 760px; }

/* ---------- Capability grid ---------- */

.caps__inner { display: flex; flex-direction: column; gap: 32px; }
.caps__head { display: flex; flex-direction: column; gap: 14px; max-width: 820px; }
.caps__head p { max-width: 740px; }

.caps__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 16px;
  overflow: hidden;
}
.cap { background: var(--surface); padding: 28px 24px; display: flex; flex-direction: column; gap: 10px; }

/* Capability icon ----------------------------------------------------------
   One line weight, one colour, no fill — the same hairline language the rest
   of the page uses. Sits above the label with its own tighter gap so the
   icon and the label read as one unit. */
.cap__icon {
  width: 26px;
  height: 26px;
  color: var(--green);
  margin-bottom: 2px;
  flex: none;
}
.cap__label {
  font-family: var(--mono); font-weight: 700; font-size: 9.5px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--green);
}
.cap__name {
  font-family: var(--display); font-weight: 800; font-size: 24px; line-height: 1.06;
  letter-spacing: -.025em; color: var(--white);
}
.cap__body {
  font-family: var(--body); font-size: 14px; line-height: 1.58;
  letter-spacing: -.012em; color: var(--cream);
}

.closer {
  display: flex; gap: 16px; align-items: flex-start;
  padding: 20px 24px; border-radius: 12px;
  background: radial-gradient(ellipse at 0% 0%, rgb(26, 32, 14) 0%, rgb(17, 17, 17) 100%);
  border: .83px solid rgba(195, 217, 70, .4);
}
.closer i {
  flex: none;
  /* Vertical rule, sized to the text block rather than a fixed dash, so it
     still reads as a rule when the sentence wraps on narrow screens. */
  align-self: stretch;
  width: 2px;
  min-height: 22px;
  background: var(--green);
  display: block;
}
.closer span {
  font-family: var(--body); font-size: 16px; line-height: 1.55;
  letter-spacing: -.012em; color: var(--bone);
}

/* The eyebrow + h2 need a wrapper: .problem__grid is a two-column grid, so a
   bare eyebrow would take a cell of its own and push the copy out. 14px gap
   matches every other section head. */
.problem__head { display: flex; flex-direction: column; gap: 14px; }

/* ---------- Checklist card footer ---------- */

.instead__foot {
  padding-top: 16px; border-top: 1px solid rgba(195, 217, 70, .24);
  font-family: var(--body); font-weight: 500; font-size: 15.5px;
  line-height: 1.45; color: var(--green);
}

/* ---------- Process chain ---------- */

.chain { flex: 1 1 460px; display: flex; flex-direction: column; gap: 10px; }
.chain__label {
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim-2);
}
.chain__steps { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.chain__step {
  font-family: var(--display); font-weight: 800; font-size: 19px; line-height: 1;
  letter-spacing: -.02em; color: var(--white);
}
.chain__arrow { font-family: var(--mono); font-size: 12px; color: var(--green); }

/* ---------- The nine areas (Days 3–5) ------------------------------------
   v1 showed a scored table and radar chart. Every number in those was
   invented, so v2 shows the nine area names exactly as the doc lists them,
   plus the doc's own example-constraint callout. Nothing else. */

.nine {
  border: 1px solid rgba(195, 217, 70, .24);
  border-radius: 16px;
  background: #080808;
  padding: 30px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.nine__label {
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--green);
}
.nine__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 12px; overflow: hidden;
}
.nine__item {
  background: var(--surface); padding: 18px 20px;
  display: flex; align-items: baseline; gap: 12px;
}
.nine__n { flex: none; font-family: var(--mono); font-weight: 700; font-size: 10px; color: var(--dim-3); }
.nine__name {
  font-family: var(--body); font-weight: 500; font-size: 15px;
  letter-spacing: -.012em; color: var(--bone);
}

/* ---------- Featured result (Paragon) ---------- */

.feature {
  border: 1px solid rgba(195, 217, 70, .24);
  border-radius: 16px; background: #1F1F1F; padding: 30px;
  display: flex; flex-direction: column; gap: 26px;
}
.feature__top {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 18px; padding-bottom: 22px; border-bottom: 1px solid rgba(255, 255, 255, .13);
}
.feature__id { display: flex; flex-direction: column; gap: 8px; }
.feature__label {
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim-3);
}
.feature__name {
  font-family: var(--display); font-weight: 800; font-size: clamp(30px, 3.6vw, 44px); line-height: .96;
  letter-spacing: -.03em; color: var(--white); text-transform: uppercase;
}

/* The client logo now stands alone where the name used to be, so it carries the
   name as its alt text and has to be big enough to read on its own. Height is a
   multiple of the heading scale rather than a fixed px, so the marks ride the
   same clamp as the page's type -- but each mark packs its box differently
   (MDRN is glyphs edge to edge, SoCal stacks a sub-line under the script, IDX
   runs a tagline above a long wordmark), so one shared multiplier would read as
   eight different sizes. Each is tuned to carry the same optical weight. */
.feature__id { --fname: clamp(30px, 3.6vw, 44px); }
.feature__logo {
  align-self: flex-start;
  height: calc(var(--fname) * var(--logo-k, 1));
  width: auto;
  max-width: 100%;
  /* contain, so a narrow card letterboxes the mark instead of squashing it */
  object-fit: contain;
  object-position: left center;
  margin-bottom: 2px;
}
.feature__logo--rhino    { --logo-k: 1.8; }
.feature__logo--socal    { --logo-k: 2.15; }
.feature__logo--vanish   { --logo-k: 1.6; }
.feature__logo--idx      { --logo-k: 1.3; }
.feature__logo--mdrn     { --logo-k: .7; }
.feature__logo--lifeline { --logo-k: 1.15; }
.feature__logo--paragon  { --logo-k: 1; }
.feature__logo--femme    { --logo-k: .72; }
.feature__logo--uhp      { --logo-k: .7; }
.feature__logo--onyx     { --logo-k: .68; }
/* Carries the treatment the "Illustrative / verify before launch" badge used
   to have — a bordered mono chip — now that the badge itself is gone. Applies
   to both case studies. */
.feature__meta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border-radius: 2px;
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  background: transparent;
  color: var(--blue);
  border: 1px solid rgba(0, 211, 255, .5);
}
.feature__headline { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; text-align: right; }
.feature__jump {
  font-family: var(--display); font-weight: 800; font-size: clamp(28px, 3.4vw, 42px); line-height: .95;
  letter-spacing: -.03em; color: var(--green);
}

.feature__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }

.fcard {
  display: flex; flex-direction: column; gap: 10px;
  padding: 22px 20px; border-radius: 12px;
  background: var(--surface-3); border: 1px solid rgba(255, 255, 255, .13);
}
.fcard--constraint {
  background: radial-gradient(ellipse at 0% 0%, rgb(10, 32, 38) 0%, rgb(17, 17, 17) 100%);
  border-color: rgba(0, 211, 255, .4);
}
.fcard--result {
  background: radial-gradient(ellipse at 0% 0%, rgb(26, 32, 14) 0%, rgb(17, 17, 17) 100%);
  border-color: rgba(195, 217, 70, .4);
}
/* The four cards in a case study share one format — eyebrow, headline, body —
   so they share one type scale. Colour is the only thing that varies between
   them: blue marks the constraint, green marks the result. Headlines were
   34 / 26 / clamp(28-48)px across the four; they are one size now, whether
   the headline is a figure (.fcard__stat) or a sentence (.fcard__title). */

/* eyebrow */
.fcard__label { font-family: var(--mono); font-size: 9px; letter-spacing: .13em; text-transform: uppercase; color: var(--dim-2); }
.fcard--constraint .fcard__label { color: var(--blue); }
.fcard--result .fcard__label { color: var(--green); }

/* headline */
.fcard__stat,
.fcard__title {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(22px, 2.4vw, 28px);
  line-height: 1.06;
  letter-spacing: -.025em;
  color: var(--white);
}
.fcard--result .fcard__stat { color: var(--green); }
.fcard--constraint .fcard__title { color: var(--blue); }

/* body */
.fcard__body { font-family: var(--body); font-size: 14.5px; line-height: 1.58; letter-spacing: -.012em; color: var(--cream); }
.fcard__list { display: flex; flex-direction: column; gap: 7px; }
.fcard__list span { display: flex; gap: 9px; align-items: flex-start; }
.fcard__list i { flex: none; margin-top: 7px; width: 5px; height: 5px; border-radius: 999px; background: var(--green); display: block; }
.fcard__list em { font-family: var(--body); font-style: normal; font-size: 14.5px; line-height: 1.58; color: var(--bone); }

/* One slot the Paragon card didn't need: the summary that closes a case
   study. Everything else reuses .feature / .fcard. */
.feature__outcome {
  padding-top: 22px; border-top: 1px solid rgba(255, 255, 255, .13);
  display: flex; flex-direction: column; gap: 10px;
}
.feature__outcome .fcard__label { color: var(--green); }
.feature__outcome p {
  font-family: var(--body); font-size: 15.5px; line-height: 1.6;
  letter-spacing: -.012em; color: var(--cream);
}

/* ---------- Why the audit matters ---------- */

.audit-why {
  display: flex; flex-direction: column; gap: 16px;
  padding: 28px 26px; border-radius: 16px;
  background: var(--surface); border: .83px solid rgba(195, 217, 70, .3);
}
.audit-why__label { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase; color: var(--green); }
.audit-why p { font-family: var(--body); font-size: 15.5px; line-height: 1.6; letter-spacing: -.012em; color: var(--cream); max-width: 760px; }

.areas { display: flex; flex-wrap: wrap; gap: 8px; }
.areas span {
  display: inline-flex; align-items: center; padding: 7px 13px; border-radius: 999px;
  background: rgba(195, 217, 70, .07); border: 1px solid rgba(195, 217, 70, .22);
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--bone);
}
.punch {
  font-family: var(--display); font-weight: 800; font-size: clamp(24px, 2.8vw, 34px); line-height: 1.04;
  letter-spacing: -.025em; color: var(--green); padding-top: 4px;
}

/* ---------- Two-up testimonials ---------- */

@media (min-width: 760px) { .testi-grid--2 { grid-template-columns: repeat(2, 1fr); } }

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

@media (max-width: 999.98px) {
  .caps__grid { grid-template-columns: repeat(2, 1fr); }
  .nine__grid { grid-template-columns: repeat(2, 1fr); }
  .feature__headline { align-items: flex-start; text-align: left; }
}
@media (max-width: 759.98px) {
  .caps__grid { grid-template-columns: 1fr; }
  .nine__grid { grid-template-columns: 1fr; }
  .feature__grid { grid-template-columns: 1fr; }
  .chain__step { font-size: 17px; }
}
@media (max-width: 639.98px) {
  .feature { padding: 22px 18px; }
  .nine { padding: 22px 18px; }
  .closer { padding: 18px 18px; }
  .audit-why { padding: 22px 20px; }
}

/* ==========================================================================
   Section rhythm + hero sub-headline
   ========================================================================== */

/* Uniform vertical padding on every section, so the gap between any two of
   them reads the same: 48px top and bottom on desktop, 32px on mobile, and
   8px at the top of the hero. This replaces the base system's mixed values
   (88/64, plus bespoke figures on trust, journey, faq and apply) and lives
   here rather than in styles.css, so /v2 and /ecomm/v2 keep the base rhythm. */

.section,
.trust,
.journey,
.faq,
.apply { padding-top: 48px; padding-bottom: 48px; }

.hero { padding-top: 14px; }
.hero--noVideo { padding-bottom: 48px; }

/* The spine carries its own bottom padding inside the journey section and the
   last step adds a little more. Zero both, so the section's 48px is the whole
   gap rather than 48 + 20 + 8. */
.spine { padding-bottom: 0; }
.step--last { padding-bottom: 0; }

/* Wider measure (+48px), and the same two-tone convention the rest of the
   body copy uses: --cream for running text, #fff at 600 for the key phrase
   (from the base `strong` rule). The hero was the one block still setting
   --bone across the whole paragraph. */
.hero__sub-stack { max-width: 808px; }
.hero__sub {
  max-width: 768px;
  color: var(--cream);
  /* Fill the measure and wrap only at the boundary. `balance` (and the base
     system's `pretty`) both shorten lines to even them out, which made the
     sub read narrower than the h1 — most obviously on mobile, where the h1
     runs the full container width. NB: `normal` is not a valid text-wrap
     value, so it silently falls back to the inherited `pretty`; `wrap` is
     the actual initial value. */
  text-wrap: wrap;
}

@media (max-width: 759.98px) {
  .section,
  .trust,
  .journey,
  .faq,
  .apply { padding-top: 32px; padding-bottom: 32px; }

  .hero { padding-top: 14px; }
  .hero--noVideo { padding-bottom: 32px; }
}

/* ---------- Featured result: let the headline figure carry the card --------
   "The result" shares a grid row with "What we changed", which is much taller,
   so it was sitting on ~160px of dead space. The figure now scales to fill it
   and the three parts spread across the full height. */

/* Centred rather than spread edge to edge, so the group fills roughly 80% of
   the card. The figure and its unit are one span at one size — splitting them
   into a giant number plus a small mono label is what made this card read
   differently from the other three. */
/* The result card centres in the row rather than sitting at the top of a tall
   box. Its figure is the payoff of the whole case study, so it breaks the
   shared card scale deliberately — bigger, and the one place on the page set
   at 900 rather than the 800 the display tier uses. */
.fcard--result { justify-content: center; gap: 20px; }
.fcard--result .fcard__stat {
  font-size: clamp(34px, 4.4vw, 56px);
  font-weight: 900;
  line-height: .98;
  letter-spacing: -.035em;
}

/* ---------- Team cards: looser rhythm between name, role and bio ---------- */

.member__info { gap: 10px; }
.member__bio { margin-top: 2px; }

/* The tag wrapped to its own line only on "Agency leadership", whose label is
   long enough to push it there. Forcing a full-basis break makes all three
   cards two lines, so the name/tag block is the same height across the row. */
.member__years { flex: 0 0 100%; }

/* ---------- v2-only components ---------- */

.feature__chart { display: flex; flex-direction: column; gap: 11px; }
.feature__chart-label { font-family: var(--mono); font-size: 9px; letter-spacing: .12em; text-transform: uppercase; color: var(--dim-2); }

/* ==========================================================================
   Mobile: centred running text
   --------------------------------------------------------------------------
   Below 760px the page is a single column, so section headings, leads and
   closing copy read better centred. Three things deliberately stay left:

     1. anything inside a card, box or bordered container — the panels keep
        their own internal alignment, which is what makes them read as
        objects rather than as more page copy;
     2. the whole "how it works" section (#journey) — it is a left-anchored
        timeline and centring it breaks the spine;
     3. the FAQ questions and answers — long-form Q&A is unreadable centred.
        The FAQ heading above them still centres.

   Everything is scoped to this one query, so desktop is untouched.
   ========================================================================== */

@media (max-width: 759.98px) {

  .hero,
  .section,
  .trust,
  .faq,
  .apply,
  .footer { text-align: center; }

  /* Buttons and inline groups that centre with their block */
  .hero__cta,
  .caps__head,
  .problem__head,
  .start__head,
  .book__head,
  .results__head,
  .team__head,
  .proof__head,
  .apply__head,
  .footer__inner { align-items: center; }

  /* ---- Exception 1: cards, boxes and bordered containers ---- */
  .trust__cell,
  .cap,
  .closer,
  .quote-bar,
  .instead,
  .panel,
  .plan,
  .act,
  .decision,
  .audit,
  .audit-why,
  .nine,
  .nine__item,
  .prow,
  .fcard,
  .feature,
  .ledger,
  .ledger__row,
  .testi,
  .member,
  .chip,
  .disclaimer--audit,
  .modal__frame,
  .stickybar,
  .menu,
  .step__col,
  .step__row { text-align: left; }

  /* ---- Exception 2: how it works ----
     The steps below still read left; the intro above them is centred, so it
     is excluded from the exception rather than the exception being dropped. */
  #journey,
  #journey .journey__head { text-align: left; }
  #journey .journey__head { align-items: flex-start; }
  #journey .journey__intro { text-align: center; align-items: center; }

  /* ---- Exception 3: FAQ questions and answers ---- */
  .faq__item,
  .faq__q,
  .faq__q-text,
  .faq__a { text-align: left; }
}

/* ==========================================================================
   Booking dialog
   --------------------------------------------------------------------------
   The one form on the page. Every CTA opens it.

   The scrim is a 5% black wash over a heavy blur, so the section you were
   reading stays legible behind the panel — you can see where you left off
   rather than losing your place in a solid overlay.
   ========================================================================== */

.formdlg {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 24px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* visibility flips instantly on open and is only delayed on close. If it
     eased in like opacity does, the panel would still be visibility:hidden on
     the frame we try to focus the close button, and the focus call would be
     dropped. */
  transition: opacity .28s cubic-bezier(.2, .6, .2, 1), visibility 0s linear .28s;
}
.formdlg.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .28s cubic-bezier(.2, .6, .2, 1), visibility 0s linear 0s;
}

.formdlg__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .05);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
  backdrop-filter: blur(18px) saturate(120%);
}

/* The panel itself no longer scrolls — .formdlg__body does. That keeps the
   close button pinned to the panel instead of scrolling away with the form,
   so it is reachable at any point in the booking flow. */
.formdlg__panel {
  position: relative;
  width: min(940px, 100%);
  max-height: calc(100dvh - 48px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #0e0e0e;
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 18px;
  padding: 16px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, .6);
  transform: translateY(10px) scale(.99);
  transition: transform .28s cubic-bezier(.2, .6, .2, 1);
}
.formdlg.is-open .formdlg__panel { transform: none; }

.formdlg__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.formdlg__close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 3;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: rgba(0, 0, 0, .55);
  border: 1px solid rgba(255, 255, 255, .16);
  color: var(--white);
  cursor: pointer;
  transition: background 140ms linear, border-color 140ms linear, color 140ms linear;
}
.formdlg__close svg { width: 16px; height: 16px; }
.formdlg__close:hover { background: var(--green); border-color: var(--green); color: #000; }
.formdlg__close:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

@media (max-width: 639.98px) {
  .formdlg { padding: 12px; }
  .formdlg__panel { padding: 10px; border-radius: 14px; max-height: calc(100dvh - 24px); }
  .formdlg__close { top: 10px; right: 10px; }
}

/* Buttons carry the .btn styles now that the CTAs are real buttons rather
   than links to a section. */
button.btn {
  border: 0;
  font-family: var(--display);
  -webkit-appearance: none;
  appearance: none;
}

/* ==========================================================================
   Hero: radial glows
   --------------------------------------------------------------------------
   Two low washes on the hero ground — green rising behind the headline, a
   cooler blue offset to one side so the pair does not read as a single lamp.
   Both sit under the content and are pointer-transparent. Kept well under the
   value where they would count as a coloured section background.
   ========================================================================== */

.hero { position: relative; isolation: isolate; overflow: hidden; }
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
  border-radius: 50%;
}
.hero::before {
  width: 118vw;
  max-width: 1500px;
  aspect-ratio: 16 / 10;
  top: -34%;
  left: 50%;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(195, 217, 70, .23), rgba(195, 217, 70, .07) 55%, transparent 78%);
  filter: blur(28px);
}
.hero::after {
  width: 66vw;
  max-width: 820px;
  aspect-ratio: 1 / 1;
  bottom: -30%;
  right: -12%;
  background: radial-gradient(closest-side, rgba(0, 211, 255, .15), rgba(0, 211, 255, .05) 55%, transparent 76%);
  filter: blur(34px);
}
.hero__inner { position: relative; z-index: 1; }

/* On a phone the first glow sat so high it was mostly above the fold and the
   second was pushed off the right edge, so the pair read as one faint wash at
   the top. They are repositioned to opposite corners — green top-right, blue
   bottom-left — so both are actually in the section. */
@media (max-width: 759.98px) {
  .hero::before {
    /* stronger on a phone: the section is short and the wash reads much
       fainter at this size than it does across a desktop hero */
    background: radial-gradient(closest-side, rgba(195, 217, 70, .34), rgba(195, 217, 70, .11) 55%, transparent 78%);
    width: 108vw;
    max-width: none;
    aspect-ratio: 1 / 1;
    top: 2%;
    left: auto;
    right: -26%;
    transform: none;
  }
  .hero::after {
    background: radial-gradient(closest-side, rgba(0, 211, 255, .24), rgba(0, 211, 255, .08) 55%, transparent 76%);
    width: 104vw;
    max-width: none;
    aspect-ratio: 1 / 1;
    bottom: 4%;
    right: auto;
    left: -30%;
  }
}


/* ==========================================================================
   Footer: centred
   ========================================================================== */

.footer__inner {
  flex-direction: column;
  justify-content: center;
  text-align: center;
  gap: 26px;
}
.footer__brand,
.footer__partners,
.footer__meta { align-items: center; justify-content: center; }
.footer__meta { justify-content: center; }

/* ==========================================================================
   Why So Shall We: copy, then the chart, then the rest
   --------------------------------------------------------------------------
   Was a two-column lock: copy on the left, a tall chart sticky on the right,
   and a stretch of extra section height acting as the runway that scrolling
   scrubbed the series through. All of that is gone.

   The section is one column now. The chart drops into the copy at its turn —
   after "So we built a different way to start", before the trial is described
   — at the same measure as the copy around it. Six bars fit that width
   whole, so nothing slides and nothing is scrubbed: it draws itself on
   arrival instead.
   ========================================================================== */

.problem__grid { display: block; }

.problem__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  text-align: center;
}
.problem__head,
.problem__copy { align-items: center; text-align: center; }
.problem__copy .lead { margin-inline: auto; }
.problem__text--after { margin-top: 0; }

/* The chart keeps to the same measure as the copy around it rather than
   breaking out to the viewport edge. Height comes down with it: held at the
   width it had when it ran full bleed, the band would have read as taller
   relative to a narrower chart, so the vw figure is cut to hold roughly the
   proportions it had. */
.problem__viz {
  margin-block: 40px;
  height: clamp(300px, 36.5vw, 480px);
}

@media (max-width: 999.98px) {
  .problem__viz { height: clamp(290px, 44vw, 400px); }
}

@media (max-width: 759.98px) {
  .problem__viz { margin-block: 28px; height: clamp(290px, 74vw, 370px); }
  .problem__text { gap: 20px; }
  .problem__head,
  .problem__copy { text-align: center; align-items: center; }
}

/* ==========================================================================
   Purchase ROAS, Paragon Dive: six two-month averages
   --------------------------------------------------------------------------
   One series, one job: show what the account did either side of the takeover.
   Twelve months bucketed into six two-month averages, September 2025 through
   August 2026, with the takeover landing exactly on a bucket edge — which is
   why the window starts in September rather than August. Bars before the
   takeover are drawn muted and unfilled; bars after take the accent.

   Three things sit behind the bars, in rising order of loudness: calendar
   quarters as hairlines, a wash over every month the takeover covers, and the
   takeover date itself as a rule. Quarters are placed by true date rather
   than on a bar edge — two-month buckets and three-month quarters do not
   share boundaries — and Q1 is left out because the takeover rule already
   marks 1 January.

   Colours come from four locals set per page, so this block is identical on
   the dark and the light build.
   ========================================================================== */

.vchart {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 26px 18px;
  background: var(--surface);
  border: 1px solid var(--chart-rule);
  border-radius: 16px;
}

.viz__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--chart-rule);
}
.vchart__key {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--chart-ink);
}
.vchart__sub,
.viz__range {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--dim-2);
}
.viz__range { margin-left: auto; }   /* the labels read left, the span right */

/* ---------- plot ----------
   The bottom 10% is the gutter the dates sit in, so everything measured from
   the axis is measured from 10%. */

.vchart__plot { position: relative; flex: 1; min-height: 0; }

.vchart__axis {
  position: absolute;
  left: 0;
  right: 0;
  top: 90%;
  border-top: 1px solid var(--chart-rule);
  z-index: 1;
}

/* ---------- what sits behind the bars ---------- */

/* quarters: a hairline at the true date, named in the gutter */
.vchart__q {
  position: absolute;
  top: 14px;
  bottom: 10%;
  left: var(--at);
  width: 1px;
  background: var(--chart-rule);
  opacity: 0;
  transition: opacity .5s linear .15s;
}
/* Named at the top of its line rather than the foot: down by the axis the
   label fell behind a bar and read through the fill as a smudge. Up here the
   plot is empty — the tallest bar stops a tenth short of the ceiling. */
.vchart__q s {
  position: absolute;
  top: 0;
  left: 5px;
  text-decoration: none;
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: .06em;
  color: var(--dim-3);
  white-space: nowrap;
}

/* every month the takeover covers */
.vchart__since {
  position: absolute;
  top: 0;
  bottom: 10%;
  left: var(--at);
  right: 0;
  background: var(--chart-wash);
  opacity: 0;
  transition: opacity .6s linear .1s;
}

/* the date itself */
.vchart__takeover {
  position: absolute;
  top: 0;
  bottom: 10%;
  left: var(--at);
  width: 2px;
  margin-left: -1px;
  background: var(--chart-accent);
  opacity: 0;
  transform: scaleY(.6);
  transform-origin: 50% 100%;
  transition: opacity .45s linear, transform .55s cubic-bezier(.2, .6, .2, 1);
  z-index: 3;
}
.vchart__takeover s {
  position: absolute;
  top: 0;
  left: 9px;
  text-decoration: none;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 9px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--chart-ink);
  white-space: nowrap;
}

.is-drawn .vchart__q,
.is-drawn .vchart__since { opacity: 1; }
.is-drawn .vchart__takeover { opacity: 1; transform: scaleY(1); }

/* ---------- the bars ---------- */

.vchart__track {
  list-style: none;
  margin: 0;
  padding: 0;
  height: 100%;
  display: flex;
  position: relative;
  z-index: 2;
}

.vcol {
  flex: 1 1 0;
  position: relative;
  height: 100%;
}

.vcol__bar {
  position: absolute;
  left: 16%;
  right: 16%;
  bottom: 10%;
  height: 0;
  border: 1px solid var(--chart-accent);
  border-bottom: 0;
  border-radius: 6px 6px 0 0;
  background: var(--chart-fill);
  transition: height .7s cubic-bezier(.2, .6, .2, 1);
}
.vcol.is-on .vcol__bar { height: var(--bh); }

/* The figure rides the top of its own bar, so it travels up with it. */
.vcol__bar b {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 9px);
  transform: translateX(-50%);
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(15px, 1.7vw, 22px);
  letter-spacing: -.02em;
  color: var(--chart-ink);
  white-space: nowrap;
  opacity: 0;
  transition: opacity .3s linear .3s;
}
.vcol.is-on .vcol__bar b { opacity: 1; }

/* Before the takeover: outlined and neutral, so the accent means "us". */
.vcol[data-phase="before"] .vcol__bar {
  border-color: var(--chart-muted);
  background: var(--chart-muted-fill);
}
.vcol[data-phase="before"] .vcol__bar b { color: var(--dim-2); }

.vcol__date {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(90% + 9px);
  text-align: center;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .04em;
  color: var(--dim-3);
}

@media (max-width: 759.98px) {
  .vchart { padding: 16px 14px 14px; gap: 10px; border-radius: 12px; }
  .viz__head { gap: 3px 10px; padding-bottom: 7px; }
  .vchart__key,
  .vchart__sub,
  .viz__range { font-size: 8px; letter-spacing: .06em; }
  .viz__range { margin-left: 0; }
  .vcol__bar { left: 12%; right: 12%; border-radius: 4px 4px 0 0; }
  .vcol__bar b { font-size: 13px; bottom: calc(100% + 6px); }
  .vcol__date { font-size: 7.5px; letter-spacing: 0; top: calc(90% + 7px); }
  .vchart__takeover s { font-size: 7.5px; left: 6px; letter-spacing: .06em; }
  /* At this width the last bar's figure reaches the ceiling and Q3's name
     lands on top of it. The quarter rules stay — they are the subtle mark —
     but they go unnamed here; the takeover keeps its label, and the dates
     under the axis already say which months are which. */
  .vchart__q s { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .vcol__bar,
  .vcol__bar b,
  .vchart__q,
  .vchart__since,
  .vchart__takeover { transition: none; }
}
/* the chart's palette on this build */
.vchart {
  --chart-accent: var(--green);
  --chart-ink: var(--green);
  --chart-fill: rgba(195, 217, 70, .16);
  --chart-wash: rgba(195, 217, 70, .06);
  --chart-rule: rgba(255, 255, 255, .12);
  --chart-muted: rgba(255, 255, 255, .30);
  --chart-muted-fill: rgba(255, 255, 255, .05);
}

/* ==========================================================================
   Testimonials: quote bubble + attribution
   --------------------------------------------------------------------------
   A speech bubble with a tail, the quote mark set in the accent, and the
   person credited underneath rather than inside — the arrangement that reads
   as a testimonial at a glance. Dark surface and hairline border rather than
   the white card it replaces, so the section belongs to the page.
   ========================================================================== */

.testi {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
}

.testi__bubble {
  position: relative;
  flex: 1;
  margin: 0;
  padding: 22px 22px 24px;
  background: #1f1f1f;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 16px;
}
/* The tail: a rotated square carrying two of the bubble's borders, so the
   corner reads as a continuation of the bubble outline rather than a patch. */
.testi__bubble::after {
  content: "";
  position: absolute;
  left: 30px;
  bottom: -8px;
  width: 15px;
  height: 15px;
  background: #1f1f1f;
  border-right: 1px solid rgba(255, 255, 255, .2);
  border-bottom: 1px solid rgba(255, 255, 255, .2);
  border-bottom-right-radius: 3px;
  transform: rotate(45deg);
}


.testi__text {
  font-family: var(--body);
  font-size: 14.5px;
  line-height: 1.6;
  letter-spacing: -.012em;
  color: var(--white);
}
/* Only the marks are the accent, and only they are set heavy — the words
   inside read as the page's own body copy. */
.testi__q { font-weight: 900; color: var(--green); }

.testi__by {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 20px 0 0 14px;
}
.testi__pics { display: inline-flex; align-items: center; }
/* Side by side with air between them: they are two different things, a face
   and a mark, so overlapping them read as one stacked group rather than two. */
.testi__pic + .testi__pic { margin-left: 6px; }

.testi__pic {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: rgba(195, 217, 70, .12);
  border: 1px solid rgba(195, 217, 70, .4);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--green);
}
.testi__pic svg { width: 18px; height: 18px; display: block; }

/* Placeholders for now: swap each span's contents for an <img> and the
   circle crops it. */
.testi__pic img {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  display: block;
}
.testi__name {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--dim-1);
}

/* ==========================================================================
   Highlighted phrases
   --------------------------------------------------------------------------
   One treatment, one colour. Used sparingly: only inside long paragraphs,
   never more than a short phrase at a time, and never in "How it works", the
   case studies or the FAQ — those three carry their own emphasis systems and
   a second one competes with them.

   This sits alongside <strong> (white, 700) rather than replacing it: strong
   is stress inside a sentence, .hl is the line worth taking away from the
   paragraph.
   ========================================================================== */

.hl {
  color: var(--white);
  font-weight: 700;
}


/* ==========================================================================
   Late adjustments
   ========================================================================== */

/* A little more air between the bubbles than the base 14px. */
.testi-grid { gap: 26px; }

/* An open question sits on a low green wash, so the expanded item reads as a
   block rather than only as a longer run of text. The wash extends past the
   question's own padding to cover the answer with it. */
.faq__item.is-open {
  background: rgba(195, 217, 70, .1);
  border-radius: 10px;
}
/* The inset is applied to every item, open or not — putting it only on the
   open one made the question jump 10px sideways the moment it expanded. Only
   the background changes on open now, so nothing moves. */
.faq__q { padding-left: 14px; padding-right: 14px; }
.faq__a { padding-left: 14px; padding-right: 66px; }

/* ==========================================================================
   Buy box: sliding carousel
   --------------------------------------------------------------------------
   Three creatives on screen at a time on a desktop, one on a phone, with a
   thumbnail strip below flanked by arrows. It advances on its own every 4s
   until the visitor takes over.

   The track is moved by the offsetLeft of the target slide rather than a
   percentage, so the gaps are accounted for exactly at any --per.
   ========================================================================== */

.buybox {
  --per: 3;
  --slide-gap: 18px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.buybox__viewport { overflow: hidden; }

.buybox__track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: var(--slide-gap);
  transition: transform .55s cubic-bezier(.2, .6, .2, 1);
}

.buybox__slide {
  flex: 0 0 calc((100% - (var(--per) - 1) * var(--slide-gap)) / var(--per));
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  border-radius: 18px;
  background: var(--surface);
}
.buybox__slide img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------- thumbnails and arrows ---------- */

.buybox__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
}

.buybox__arrow {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: none;
  color: var(--dim-1);
  cursor: pointer;
  transition: border-color 160ms linear, color 160ms linear, background 160ms linear;
}
.buybox__arrow svg { width: 20px; height: 20px; }
.buybox__arrow:hover { border-color: var(--pink); color: var(--pink); }
.buybox__arrow:focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; }
.buybox__arrow[disabled] { opacity: .3; cursor: default; }
.buybox__arrow[disabled]:hover { border-color: var(--hairline); color: var(--dim-1); }

/* the strip sits in its own pill, the way the reference has it */
.buybox__thumbs {
  display: flex;
  gap: 10px;
  padding: 10px;
  border-radius: 16px;
  border: 1px solid var(--hairline);
  background: var(--surface);
}

.buybox__thumb {
  flex: none;
  width: 62px;
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  padding: 0;
  border-radius: 9px;
  border: 1px solid var(--hairline);
  background: none;
  cursor: pointer;
  opacity: .55;
  transition: opacity 160ms linear, border-color 160ms linear;
}
.buybox__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.buybox__thumb:hover { opacity: .85; }
.buybox__thumb.is-on { opacity: 1; border-color: var(--pink); }
.buybox__thumb:focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; }

@media (max-width: 999.98px) {
  .buybox { --per: 2; }
  .buybox__thumb { width: 52px; }
}



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

/* ==========================================================================
   Contribution chart: drawn on arrival
   --------------------------------------------------------------------------
   The line strokes itself in from left to right, the area fills in behind it
   and the three quarter markers land in turn — the same idea as the audit
   nonagon, sized down to a card. About 1.2s end to end.

   The stroke length is measured in script and written to --len, because the
   dash trick needs a real number and this polyline is scaled by its viewBox.
   ========================================================================== */

.plan__chart[data-plan] [data-plan-line] {
  stroke-dasharray: var(--len, 1200);
  stroke-dashoffset: var(--len, 1200);
}
.plan__chart[data-plan] [data-plan-fill] {
  transform-box: fill-box;
  transform-origin: left center;
  transform: scaleX(0);
  opacity: 0;
}
.plan__chart[data-plan] [data-plan-dot] {
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0);
}

.plan__chart.is-drawn [data-plan-line] {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.05s cubic-bezier(.35, .1, .25, 1);
}
.plan__chart.is-drawn [data-plan-fill] {
  transform: none;
  opacity: 1;
  transition: transform 1.05s cubic-bezier(.35, .1, .25, 1), opacity .4s linear;
}
.plan__chart.is-drawn [data-plan-dot] {
  transform: none;
  transition: transform .42s cubic-bezier(.2, 1.5, .4, 1);
}
/* each marker lands as the line reaches it */
.plan__chart.is-drawn [data-plan-dot]:nth-of-type(1) { transition-delay: .34s; }
.plan__chart.is-drawn [data-plan-dot]:nth-of-type(2) { transition-delay: .64s; }
.plan__chart.is-drawn [data-plan-dot]:nth-of-type(3) { transition-delay: .94s; }

/* the label fades up under it */
.plan__chart[data-plan] .plan__chart-label { opacity: 0; }
.plan__chart.is-drawn .plan__chart-label { opacity: 1; transition: opacity .5s linear .2s; }

@media (prefers-reduced-motion: reduce) {
  .plan__chart[data-plan] [data-plan-line] { stroke-dasharray: none; stroke-dashoffset: 0; }
  .plan__chart[data-plan] [data-plan-fill],
  .plan__chart[data-plan] [data-plan-dot] { transform: none; opacity: 1; }
  .plan__chart[data-plan] .plan__chart-label { opacity: 1; }
  .plan__chart.is-drawn [data-plan-line],
  .plan__chart.is-drawn [data-plan-fill],
  .plan__chart.is-drawn [data-plan-dot] { transition: none; }
}

/* --------------------------------------------------------------------------
   Certified-partner badge: 4px lower
   -------------------------------------------------------------------------- */

.partner__cert { margin-top: 4px; }

/* --------------------------------------------------------------------------
   Hero: the decision half of the headline in the accent
   --------------------------------------------------------------------------
   <em> only because the copy needs an inline element to wrap in; the italic
   is turned off. Size and weight come from the h1, so colour is the only
   thing that changes.
   -------------------------------------------------------------------------- */

.hero__accent {
  font-style: normal;
  color: var(--green);
}

/* --------------------------------------------------------------------------
   The buy box on a phone
   --------------------------------------------------------------------------
   The carousel is a desktop arrangement. On a phone it goes back to what it
   was: one square image with the six thumbnails in a row underneath, no
   arrows and no pill around them. Same markup, different clothes — one slide
   per view is the single stage, so nothing needs rebuilding.
   -------------------------------------------------------------------------- */

@media (max-width: 759.98px) {
  .buybox { --per: 1; --slide-gap: 0px; gap: 14px; }
  .buybox__slide { border-radius: 16px; }
  .buybox__arrow { display: none; }
  .buybox__nav { display: block; }
  /* Four across, so eight thumbs fall as two even rows rather than 6 + 2. */
  .buybox__thumbs {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
    padding: 0;
    border: 0;
    background: none;
    border-radius: 0;
  }
  .buybox__thumb { width: auto; border-radius: 8px; }
}

/* --------------------------------------------------------------------------
   "Why the audit matters"
   -------------------------------------------------------------------------- */

.audit-why { background: #1F1F1F; }   /* matching the case study cards */

/* --------------------------------------------------------------------------
   Trust section: buy box above the stats
   --------------------------------------------------------------------------
   The rule lived inside the old buy box block and went with it when the
   carousel replaced that block, which left the two stacked with no gap.
   -------------------------------------------------------------------------- */

.trust__inner { display: flex; flex-direction: column; gap: 48px; }
.buybox { width: 100%; }

@media (max-width: 899.98px) { .trust__inner { gap: 36px; } }
@media (max-width: 759.98px) { .trust__inner { gap: 30px; } }

/* ==========================================================================
   Wordmark
   --------------------------------------------------------------------------
   Nav and footer both carry the white SSW wordmark SVG in place of the
   set text. Sized by height so the 3.8:1 artwork keeps its own ratio.
   ========================================================================== */

.nav__logo { display: inline-flex; align-items: center; line-height: 0; }
.nav__logo img { height: 27.6px; width: auto; display: block; }
.nav__logo:hover img { opacity: .85; transition: opacity 140ms linear; }

.footer__logo { line-height: 0; }
.footer__logo img { height: 31.9px; width: auto; display: block; }

@media (max-width: 759.98px) {
  .nav__logo img { height: 23.4px; }
  .footer__logo img { height: 27.6px; }
}

/* ==========================================================================
   Hero eyebrow: one line on a phone
   --------------------------------------------------------------------------
   The pill wants ~412px at its set size. Inside a 339px hero column it hit
   the wall, filled the column and wrapped — and once it fills the column the
   dot sits at the far left while the centred text starts a third of the way
   in, which is the gap that read as wasted space.

   Two changes buy the line back. The pill is allowed past the hero gutter to
   within 8px of each screen edge, and the type steps down with the viewport:
   min() caps it at the desktop 13.5px and the calc keeps roughly 28px of pill
   per 1px of type, which is what the string costs to set plus the dot,
   padding and border. Tighter padding and gap fund the rest. Below the cap
   the pill shrink-wraps its content again, so the dot returns to the copy.
   ========================================================================== */

@media (max-width: 759.98px) {
  .pill {
    font-size: min(13.5px, calc((100vw - 62px) / 28));
    gap: 8px;
    padding: 6px 12px;
    width: max-content;
    max-width: calc(100vw - 16px);
  }
}

/* ==========================================================================
   Certified-partner badge
   --------------------------------------------------------------------------
   Was: each platform set as a 22px light wordmark with a Facebook-blue tick
   floating underneath, the pair held together by a rule. The blue belonged to
   Meta's palette rather than this page's, and at that size the two wordmarks
   read as headings competing with the h1 below them.

   Now it is one chip on one line — Google, Meta, then the claim they share —
   built from the page's own parts: hairline border, barely-there surface,
   pill radius, the claim in the mono micro-label used for every other label
   here and ticked in the accent. Only the glyphs stay in their native colour,
   which is the part that actually carries the recognition; the wordmarks drop
   to body weight beside them. Rules separate who from who, and who from what.
   ========================================================================== */

.partner {
  flex-direction: row;
  align-items: center;
  gap: 12px;
  padding: 8px 15px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: rgba(255, 255, 255, .025);
}

.partner__brand { gap: 7px; }
.partner__brand--google img { height: 16px; }
.partner__brand--meta img { height: 15px; }

.partner__name {
  font-family: var(--body);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -.01em;
}

.partner__cert { margin-top: 0; gap: 6px; }
.partner__cert span:last-child {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--green);
  white-space: nowrap;
}

/* Accent tick, black glyph — the same figure/ground the primary button uses. */
.check {
  width: 13px;
  height: 13px;
  background: var(--green);
  color: #000;
  font-size: 8px;
  font-weight: 800;
}

/* The rules now sit inside the chip, so they take its inner height rather
   than stretching the row they used to divide. */
.partners__divider {
  align-self: auto;
  height: 15px;
  background: rgba(255, 255, 255, .16);
}

@media (max-width: 759.98px) {
  .partner { gap: 9px; padding: 7px 11px; }
  .partner__brand { gap: 5px; }
  .partner__brand--google img { height: 13px; }
  .partner__brand--meta img { height: 12px; }
  .partner__name { font-size: 12.5px; }
  .partner__cert { gap: 5px; }
  .partner__cert span:last-child { font-size: 8px; letter-spacing: .04em; }
  .check { width: 11px; height: 11px; font-size: 7px; }
  .partners__divider { height: 13px; }
}

/* --------------------------------------------------------------------------
   Certified badges: centred in the space above the eyebrow
   --------------------------------------------------------------------------
   The hero's 8px top padding put the badges 8px under the nav's rule while
   the hero__inner gap left 24px between them and the eyebrow below — the
   block sat high in its own space. Half the difference is added above and
   taken straight back below, so the badges drop 8px and land with 16px on
   each side while the eyebrow, the headline and everything under them stay
   exactly where they were.
   -------------------------------------------------------------------------- */

/* The badges no longer need their own nudge: the hero's own rhythm is 16px
   now, which is exactly what the pair of them used to be centred to. */

/* ==========================================================================
   Capability icons: each one pops in
   --------------------------------------------------------------------------
   Each icon is watched on its own rather than the grid as a whole, so a card
   pops when the scroll reaches that card — the top row on the way in, the
   bottom row a screen later — instead of all six firing off the first one to
   appear. The observer's root is stretched 12% past the fold so a card starts
   just before it arrives rather than just after.

   Each pops from nothing: up past full size, back under it, and a smaller
   overshoot before it settles. Overshooting twice rather than once is what
   keeps it reading as a pop rather than a zoom, and the amount halves each
   time so it lands rather than wobbles.

   The icons are only hidden once script.js has armed the grid, so with JS
   off — or under prefers-reduced-motion, where the arming is skipped — they
   are simply there.
   ========================================================================== */

.caps__grid.is-armed .cap__icon { opacity: 0; }

.cap.is-drawn .cap__icon {
  animation: ssw-cap-pop 1.15s cubic-bezier(.34, 1.4, .64, 1) both;
  will-change: transform, opacity;
}

@keyframes ssw-cap-pop {
  0%   { transform: scale(0)    rotate(-8deg); opacity: 0; }
  40%  { transform: scale(1.32) rotate(3deg);  opacity: 1; }
  62%  { transform: scale(.88)  rotate(-2deg); }
  80%  { transform: scale(1.08) rotate(1deg); }
  92%  { transform: scale(.97)  rotate(0deg); }
  100% { transform: scale(1)    rotate(0deg); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .caps__grid.is-armed .cap__icon { opacity: 1; }
  .cap.is-drawn .cap__icon { animation: none; }
}

/* ==========================================================================
   Testimonial bubbles: each one pops in
   --------------------------------------------------------------------------
   Watched per card, like the capability icons, so a bubble pops when its own
   card is reached rather than all three firing off the first one to appear.

   It grows from the tail rather than the middle — transform-origin sits on
   the tail's own offset — so it reads as the bubble coming out of the person
   underneath instead of inflating in place. The overshoot is far gentler than
   the icons': at this size 1.06 is already plenty, where a 1.3 would throw a
   300px card around. Only the bubble moves; the name and avatar below stay
   put and give the motion something to sit against.

   As with the icons, the bubbles are only hidden once script.js has armed the
   grid, so with JS off — or under prefers-reduced-motion, where arming is
   skipped — they are simply there.
   ========================================================================== */

.testi-grid.is-armed .testi__bubble { opacity: 0; }

.testi.is-drawn .testi__bubble {
  animation: ssw-testi-pop 1.25s cubic-bezier(.34, 1.28, .64, 1) both;
  transform-origin: 30px 100%;      /* the tail, at its own left offset */
  will-change: transform, opacity;
}

@keyframes ssw-testi-pop {
  0%   { transform: scale(0);     opacity: 0; }
  45%  { transform: scale(1.06);  opacity: 1; }
  68%  { transform: scale(.975); }
  85%  { transform: scale(1.015); }
  100% { transform: scale(1);     opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .testi-grid.is-armed .testi__bubble { opacity: 1; }
  .testi.is-drawn .testi__bubble { animation: none; }
}

/* --------------------------------------------------------------------------
   The team: copy inset from the frame on a phone
   --------------------------------------------------------------------------
   Stacked, the text sits directly under the portrait and lines up hard with
   its left edge. 2px of padding breaks that alignment just enough to read as
   deliberate. Padding rather than a transform, so the measure moves with the
   text instead of the last 2px hanging off the end of it.
   -------------------------------------------------------------------------- */

@media (max-width: 759.98px) {
  .member__info { padding-left: 2px; }
}

/* ==========================================================================
   The nine areas: status, not score
   --------------------------------------------------------------------------
   Replaces the radar and the scored table. Nine areas stacked one per row,
   each carrying a single status dot, and the card is the width of the step
   rather than a two-column split.

   The signal colours are deliberately NOT the brand's. The accent here is a
   yellow-green, which is the one hue a semantic green must not be confused
   with, and there is no brand red or amber at all — so a viewer reading the
   dots would be guessing. These three are picked to be unmistakable as
   good / small issue / wrong on sight:

     ok    a true green, pulled well round the wheel from the accent's yellow
     warn  amber, orange enough that it cannot read as the accent either
     bad   red, desaturated just enough not to vibrate on the ground

   They stay quiet because they are only ever a 10px dot and a hairline tint
   on the row — never type, never a fill. The area names stay in the page's
   own ink, so the card reads as the page with a signal on it rather than as
   a traffic light bolted to the page.
   ========================================================================== */

.aud__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--aud-rule);
}
.aud__title {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim-2);
}
.aud__legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-left: auto; }
.aud__key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim-3);
}
.aud__key i {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  flex: none;
  display: block;
}

.aud__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.aud__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 14px;
  border-bottom: 1px solid var(--aud-rule);
  border-radius: 8px;
  background: transparent;
  transition: background 420ms linear;
}
.aud__item:last-child { border-bottom: 0; }

.aud__name {
  font-family: var(--body);
  font-weight: 600;
  font-size: 15.5px;
  letter-spacing: -.012em;
  color: var(--white);
}

/* The dot is the whole signal, so it is given a ring of its own colour to
   read at a glance without growing. Off, it is a hollow grey. */
.aud__dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: var(--aud-off);
  box-shadow: 0 0 0 3px var(--aud-off-ring);
  transform: scale(.72);
  transition:
    background 380ms linear,
    box-shadow 380ms linear,
    transform 420ms cubic-bezier(.34, 1.5, .64, 1);
}

/* ---------- states ---------- */

/* Arrived but undecided: the dot comes up to full size and stays the neutral
   grey it started as. Nothing tints the row yet — the verdict has not landed. */
.aud__item[data-state="idle"] .aud__dot { transform: scale(1); }

.aud__item[data-state="ok"]   .aud__dot { background: var(--aud-ok);   box-shadow: 0 0 0 3px var(--aud-ok-ring);   transform: scale(1); }
.aud__item[data-state="warn"] .aud__dot { background: var(--aud-warn); box-shadow: 0 0 0 3px var(--aud-warn-ring); transform: scale(1); }
.aud__item[data-state="bad"]  .aud__dot { background: var(--aud-bad);  box-shadow: 0 0 0 3px var(--aud-bad-ring);  transform: scale(1); }

/* Every state carries its own tint, green included, so a settled area reads
   as checked-and-clear rather than merely not-flagged. */
.aud__item[data-state="ok"]   { background: var(--aud-ok-ring); }
.aud__item[data-state="warn"] { background: var(--aud-warn-ring); }
.aud__item[data-state="bad"]  { background: var(--aud-bad-ring); }

/* The name is coloured by its state too, so the signal is not carried by a
   7px dot alone — and the one that must be fixed is set heavier than the
   rest, so it is the first thing found when the card is scanned. Green is
   left in the page's own ink: colouring seven of nine names would turn the
   card into a block of colour and cost the two flags their contrast. */
.aud__item[data-state="warn"] .aud__name { color: var(--aud-warn-ink); font-weight: 700; }
.aud__item[data-state="bad"]  .aud__name { color: var(--aud-bad-ink); font-weight: 800; }

.aud__key i[data-state="ok"]   { background: var(--aud-ok); }
.aud__key i[data-state="warn"] { background: var(--aud-warn); }
.aud__key i[data-state="bad"]  { background: var(--aud-bad); }

/* ---------- the card ---------- */

.audit {
  display: block;
  padding: 24px 22px 20px;
}
.disclaimer--audit { display: block; margin-top: 16px; }

/* Three across once there is room for it, as separate cards rather than rows
   in a list: at a third of the width the hairline rule between rows stops
   reading as a divider and starts reading as an underline, so each area takes
   its own border instead. Source order is already the reading order — the top
   row is Tracking, Creatives, Messaging — so the sweep still runs left to
   right and down. */
@media (min-width: 760px) {
  .aud__list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }
  .aud__item {
    border: 1px solid var(--aud-rule);
    border-radius: 12px;
    padding: 16px 16px 17px;
    align-items: flex-start;
  }
  .aud__item:last-child { border: 1px solid var(--aud-rule); }
  .aud__dot { margin-top: 4px; }
}

@media (max-width: 759.98px) {
  .audit { padding: 18px 14px 16px; }
  .aud__head { gap: 6px 12px; padding-bottom: 10px; }
  .aud__legend { margin-left: 0; }
  .aud__key { font-size: 8px; letter-spacing: .04em; }
  .aud__item { padding: 12px 8px; gap: 12px; }
  .aud__name { font-size: 14.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .aud__item, .aud__dot { transition: none; }
}
/* Signal palette on the dark ground. The accent is #C3D946, a yellow-green,
   so "ok" is pushed to a true green with a blue lean — no chance of reading
   as the brand. All three are lifted for a near-black background. */
.audit {
  --aud-off:        rgba(255, 255, 255, .22);
  --aud-off-ring:   rgba(255, 255, 255, .05);
  --aud-ok:         #35C77E;
  --aud-ok-ring:    rgba(53, 199, 126, .16);
  --aud-warn:       #FFCC00;
  --aud-warn-ring:  rgba(255, 241, 3, .13);
  --aud-warn-ink:   #FFCC00;
  --aud-bad:        #F0303C;
  --aud-bad-ring:   rgba(240, 48, 60, .14);
  --aud-bad-ink:    #F0303C;
  --aud-rule:       rgba(255, 255, 255, .10);
}

/* ==========================================================================
   Hero VSL
   --------------------------------------------------------------------------
   A placeholder holding the shape the Wistia player will take. The 16:9 box
   is set on the frame rather than left to the embed, so the hero does not
   reflow when the script loads and the rest of the fold does not jump.

   The player is the Wistia custom element; its loader and its
   undefined-state style sit in <head>.
   ========================================================================== */

.vsl { width: 100%; display: flex; justify-content: center; }

.vsl__frame {
  position: relative;
  width: 100%;
  max-width: 560px;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--vsl-rule);
  border-radius: 14px;
  background: var(--vsl-bg);
  overflow: hidden;
}

/* The frame owns the 16:9 box, so the player fills it rather than sizing
   itself — including the padding-top Wistia's own undefined-state rule sets,
   which would otherwise stack a second aspect box inside this one. */
.vsl__frame > wistia-player,
.vsl__frame > iframe,
.vsl__frame > .wistia_embed,
.vsl__frame > .wistia_responsive_padding {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  padding-top: 0;
}


@media (max-width: 759.98px) {
  .vsl__frame { border-radius: 12px; }
}
.vsl {
  --vsl-rule: rgba(255, 255, 255, .16);
  --vsl-bg:   rgba(255, 255, 255, .04);
  --vsl-play: rgba(255, 255, 255, .06);
  --vsl-ink:  var(--green);
}

/* --------------------------------------------------------------------------
   How it works: the intro reads centred
   -------------------------------------------------------------------------- */

.journey__intro {
  align-items: center;
  text-align: center;
  margin-inline: auto;
}
.journey__intro p { margin-inline: auto; }

/* --------------------------------------------------------------------------
   Primary CTA: one line on a phone
   --------------------------------------------------------------------------
   "Get my free growth audit" is 347px set at the hero button's 25px, against
   275px of room inside a 375px screen, so it broke to two lines — and the
   buttons have fixed heights, so the second line pushed out of the pill.

   The type steps down with the viewport instead of taking a single smaller
   value: the label costs about 14.5px of width per 1px of type, and the room
   is the screen less the container gutter and the button's own padding. min()
   caps it at the desktop size so nothing changes above the breakpoint.
   -------------------------------------------------------------------------- */

@media (max-width: 759.98px) {
  .btn--primary { font-size: min(var(--cta-size), calc((100vw - 112px) / 14.5)); }

  /* The one in the decision card has far less room than the rest: it sits
     inside a padded card, inside a step indented for the spine. Its own room
     is the screen less all of that, so it gets its own step-down and gives up
     half its side padding. */
  .btn--md {
    padding: 0 16px;
    /* The label gained a trailing arrow, so it now costs ~14.8px of width per
       1px of type rather than 14 and wrapped to two lines at 320px. */
    font-size: min(var(--cta-size), calc((100vw - 160px) / 15));
  }
}

/* --------------------------------------------------------------------------
   v2: the checklist card moved into the how-it-works head
   --------------------------------------------------------------------------
   .journey__head is a column flex, and on mobile it ranges its children left
   (align-items: flex-start). The card is a bordered block, not a line of
   text, so it has to keep the full column width there.
   -------------------------------------------------------------------------- */
.journey__head > .instead { align-self: stretch; width: 100%; }

/* ==========================================================================
   v2 typography and measure
   ========================================================================== */

/* Section eyebrow takes the h2 treatment and the headline takes the h3, so
   the two swap places in the scale. The uppercase and .1em tracking go with
   the old size — that treatment reads as a label at 15px, not at 54px. The
   hero is untouched: it carries a pill, not an .eyebrow. */
.section .eyebrow {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(32px, 4.4vw, 54px);
  line-height: 1;
  letter-spacing: -.02em;
  text-transform: none;
}
.section .h2,
.section .h2--wide,
.section .faq__h2 {
  font-size: clamp(27px, 3.4vw, 40px);
  letter-spacing: -.025em;
}

@media (min-width: 760px) {
  /* One body measure across the page. .step__body and the card copy are
     already narrower than this and keep their own width. */
  .lead,
  .faq__a,
  .disclaimer,
  .caps__head p,
  .journey__intro p,
  .results__head p,
  .team__head p { max-width: 800px; }

  /* What we actually do: head centred on desktop, as it already is on mobile. */
  .caps__head { align-items: center; text-align: center; margin-inline: auto; }
  .caps__head p { margin-inline: auto; }
}

/* Hero qualifier — the doc places a fit statement directly above the CTA
   buttons. Smaller and dimmer than the subheadline so it reads as a caveat,
   not a second promise, and it takes the hero's own uniform gap. */
.hero__qualifier {
  font-family: var(--body);
  font-size: clamp(13px, 1.15vw, 14.5px);
  line-height: calc(1em + 4px);
  letter-spacing: -.01em;
  color: var(--dim-2);
  max-width: 660px;
  margin-inline: auto;
}

/* Closing CTA note — the doc's last line under the button. Matches the hero's
   micro note so both CTAs close the same way. */
.apply__note {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim-2);
}

/* --------------------------------------------------------------------------
   Section heading hierarchy
   --------------------------------------------------------------------------
   The eyebrow (h2 role) and the headline (h3 role) were both weight 800 and
   only ~14px apart, so they read as two competing titles. The eyebrow takes
   caps and keeps the heavy weight; the headline steps down to 700 so the two
   separate by weight and case, not size alone.
   -------------------------------------------------------------------------- */
.section .eyebrow {
  text-transform: uppercase;
  letter-spacing: -.005em;
}
.section .h2,
.section .h2--wide,
.section .faq__h2 { font-weight: 700; }

/* Hero headline: five lines maximum on a phone.
   The floor is now 32px to match the h2, and 32px only holds 5 lines down to
   375px — below that it breaks to 6. So under the floor the type scales with
   the viewport instead: the line costs roughly 10.6px of width per 1px of
   type against the container's 36px gutter, which lands a 375px screen on
   32px exactly and a 320px screen on ~26.8px. At 375px and above min()
   returns the 32px floor. */
@media (max-width: 759.98px) {
  .hero__h1--long { font-size: min(32px, calc((100vw - 36px) / 10.6)); }
}

/* Final CTA: same two-tier heading as every section — the eyebrow carries the
   h2 treatment, the line beneath it the h3. .apply is not a .section, so the
   rules above don't reach it and it gets its own pair. */
.apply .eyebrow {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(32px, 4.4vw, 54px);
  line-height: 1;
  letter-spacing: -.005em;
  text-transform: uppercase;
}
.apply__h2 {
  font-size: clamp(27px, 3.4vw, 40px);
  font-weight: 700;
  letter-spacing: -.025em;
}

/* Footer: no sticky-bar clearance.
   The 120px base and the 172px phone value existed so the bar could never sit
   over footer content. The bar now stands down once the final CTA is on
   screen, and the CTA is still visible at the bottom of the page, so that
   space is dead. Bottom padding matches the top. Overrides both the base rule
   and the phone override in styles.css by source order. */
.footer { padding-bottom: 38px; }

/* Hero headline: the phone breaks are set by hand.
   The line shape is spelled out in the markup rather than left to the wrap
   algorithm — after "Brands", "$250K", "Paid" and "Wall". The breaks are
   inert above the phone breakpoint, where the headline runs to the 960px
   container as before. text-wrap: balance is switched off while they apply,
   or it re-balances the segments against the forced breaks. */
.hero__br { display: none; }

@media (max-width: 759.98px) {
  .hero__br { display: inline; }
  .hero__h1--long { text-wrap: wrap; }
}

/* Hero subheadline: 1px smaller than the base scale at every width. The
   offset sits inside the clamp's middle term as well as its bounds, so the
   reduction is uniform rather than only applying where the clamp is pinned.
   line-height is calc(1em + 4px), so the leading follows it automatically. */
.hero__sub { font-size: clamp(14px, calc(1.4vw - 1px), 17px); }

/* Bulleted list inside a journey step — the deliverables under Deploy the
   Team. Same dot-and-text figure the case-study lists use, sized to the
   step's own body copy so it sits in the paragraph rhythm around it. */
.step__list { display: flex; flex-direction: column; gap: 7px; margin: 0; padding: 0; list-style: none; }
.step__list li { display: flex; gap: 9px; align-items: flex-start; }
.step__list i {
  flex: none; margin-top: 7px; width: 5px; height: 5px;
  border-radius: 999px; background: var(--green); display: block;
}
.step__list span {
  font-family: var(--body); font-size: 15.5px; line-height: 1.62;
  letter-spacing: -.012em; color: var(--cream);
}

/* Step tags: bigger, but never two lines.
   9.5px was quiet enough to disappear next to a 40px step title. The size
   scales with the viewport and caps at 17px — well short of the headline, but
   roughly double what it was. The binding case is "FIND THE CONSTRAINT" on a
   320px screen, which is the longest of the three and the least room; the
   divisor is set from that measurement, and the em letter-spacing follows the
   size down on its own. Padding grows a little so the pill keeps its shape. */
.status { padding: 7px 15px; gap: 10px; }
.status__text { font-size: clamp(12px, calc((100vw - 36px) / 23.5), 17px); }

/* Desktop headline and badges pill matched to /grow-vsl/v2 ---------------------
   The headline drops 8px at every width above the phone breakpoint. The
   offset sits inside the clamp's middle term as well as its bounds, so it is
   a flat 8px rather than only applying where the clamp is pinned: 54 -> 46
   from 1228px up, 33.4 -> 25.4 at the breakpoint itself.

   The pill scales to 90% on every dimension it is built from, not just its
   type, so the chip's footprint shrinks with it — a transform: scale() would
   have left the original box behind in the layout. The 1px border and the
   999px radius stay: a hairline has no meaningful 90%, and the radius is
   already past the pill's half-height. Letter-spacing is in em and follows
   its font-size down on its own.

   Mobile is untouched on both counts. */
@media (min-width: 760px) {
  .hero__h1--long { font-size: clamp(24px, calc(4.4vw - 8px), 46px); }

  .partner { gap: 10.8px; padding: 7.2px 13.5px; }
  .partner__brand { gap: 6.3px; }
  .partner__brand--google img { height: 14.4px; }
  .partner__brand--meta img { height: 13.5px; }
  .partner__name { font-size: 13.5px; }
  .partner__cert { gap: 5.4px; }
  .partner__cert span:last-child { font-size: 8.55px; }
  .check { width: 11.7px; height: 11.7px; font-size: 7.2px; }
  .partners__divider { height: 13.5px; }
}

/* Tighten 4px above and below the certified-badges pill, desktop only.

   The pill is the first child of .hero__inner, so the space above it is the
   hero's own top padding and the space below is the stack's shared gap. The
   gap is one rhythm for every item in the hero, so it stays at 14px and the
   pill pulls its own 4px back with a negative margin — otherwise the whole
   stack would tighten rather than this one seam. Mobile keeps 14/14. */
@media (min-width: 760px) {
  .hero { padding-top: 10px; }
  .partners { margin-bottom: -4px; }
}

/* --------------------------------------------------------------------------
   Team carousel
   --------------------------------------------------------------------------
   --tcar-per is the number of cards on screen: 3 on a desktop, 1 on a phone.
   Everything else — slide width, how far a step moves, how many steps exist,
   the size of the scrollbar thumb — is derived from it in CSS or read from it
   in the script, so the breakpoint is declared in exactly one place.
   -------------------------------------------------------------------------- */
/* The card ratio is locked to the source art (646x840) rather than left to
   the image's intrinsic size, so a future swap at a different size crops to
   the same shape instead of changing every card's height.

   Height is capped, not set: --tcar-card-h is the tallest a card may get, and
   the carousel's own max-width is derived from it — cap x ratio for the card,
   times however many are on screen, plus the gaps between them. Capping the
   carousel rather than the image keeps the cards flush against each other, so
   the row has no slack inside it. Below the cap nothing binds and the cards
   stay fluid. */
.tcar {
  --tcar-per: 3;
  --tcar-gap: 18px;
  --tcar-card-h: 460px;
  --tcar-ratio: 646 / 840;
  /* width:100% first, then the cap. Without it the carousel is a flex item in
     a centred column, so it shrinks to fit — and its own slides are sized as a
     percentage of it, which makes that circular and collapses the row to a
     fraction of its width. */
  width: 100%;
  max-width: calc(
    var(--tcar-per) * (var(--tcar-card-h) * var(--tcar-ratio))
    + (var(--tcar-per) - 1) * var(--tcar-gap)
  );
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.tcar__viewport { overflow: hidden; }

.tcar__track {
  display: flex;
  gap: var(--tcar-gap);
  margin: 0;
  padding: 0;
  list-style: none;
  /* The offset is set by the script as a plain px value. It was a calc() over
     --tcar-at here, but multiplying a unitless custom property through a
     percentage-plus-length group was rejected and the whole transform was
     dropped to identity — the index advanced and nothing moved. Measuring the
     slide is also more honest: it accounts for whatever the flex basis
     actually resolved to. */
  transition: transform 420ms cubic-bezier(.22, .61, .36, 1);
}

.tcar__slide {
  flex: 0 0 calc((100% - (var(--tcar-per) - 1) * var(--tcar-gap)) / var(--tcar-per));
}
.tcar__slide img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--tcar-ratio);
  object-fit: cover;
  /* The source PNGs carried their rounded corners in an alpha channel that
     cost 6MB across nine files. They ship as JPEG and the corners are redrawn
     here — 46px on a 646px-wide card, so the radius tracks the render width. */
  border-radius: 7.1cqw;
  border-radius: clamp(10px, 7.1%, 26px);
}

.tcar__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}

.tcar__arrow {
  flex: none;
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: transparent;
  color: var(--white);
  cursor: pointer;
  transition: border-color 140ms linear, color 140ms linear, opacity 140ms linear;
}
.tcar__arrow svg { width: 18px; height: 18px; }
.tcar__arrow:hover:not(:disabled) { border-color: var(--green); color: var(--green); }
.tcar__arrow:disabled { opacity: .32; cursor: default; }

/* The indicator is a track with a thumb sized to the share of the set on
   screen, so it reads as a scrollbar rather than as dots — nine dots would be
   a lot of furniture for a row of three. */
.tcar__rail {
  position: relative;
  flex: 1 1 260px;
  max-width: 260px;
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .16);
  overflow: hidden;
}
.tcar__thumb {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--tcar-thumb, 33%);
  transform: translateX(var(--tcar-thumb-at, 0));
  border-radius: 999px;
  background: var(--green);
  transition: transform 420ms cubic-bezier(.22, .61, .36, 1);
}

@media (max-width: 759.98px) {
  .tcar { --tcar-per: 1; --tcar-gap: 14px; --tcar-card-h: 400px; }
  .tcar__rail { flex: 1 1 auto; max-width: none; }
}

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