/* ==========================================================================
   BULLETPROOF PERFORMANCE ACADEMY — subdomain stylesheet

   Loaded AFTER style.css. It adds nothing to the design system and overrides
   nothing in it: every colour, radius, shadow and space below resolves from
   the tokens style.css already declares, so a change made on the main site
   carries straight through to this page.

   New here, and only here:
     .h-caps        uppercase editorial headline treatment the brief asks for
     .ahero         hero that carries the VSL rather than sitting above it
     .askew         the analysis grid that backs the dark bands
     .pyramid       infographic 1, the boxing knowledge pyramid
     .versus        infographic 2, train harder vs train with understanding
     .feel          infographic 3, what you feel vs what is happening
     .pills         the "what is inside" cards
     .price         membership
     .joinbar       mobile sticky CTA
   ========================================================================== */

/* --------------------------------------------------------------------------
   BLOCK RHYTHM
   The shared stylesheet sets --block-gap to 64px, and it drives the gap under
   every .section-head as well as the space around the question cards, the
   comparison, the verdict line, the analysis row and the pyramid. Measured on
   this page that was leaving 64px above AND below the four question cards and
   again under the closing line, which reads as a hole rather than a pause.

   Redefined here rather than in style.css, which is the main site's file and
   is not ours to change. All three steps have to be restated: academy.css
   loads after style.css, so a bare :root here would win over the responsive
   values in style.css's media queries as well as its base one, and the page
   would carry 40px all the way down to 320.
   -------------------------------------------------------------------------- */
:root { --block-gap: 40px; }
@media (max-width: 1024px) { :root { --block-gap: 32px; } }
@media (max-width: 767px)  { :root { --block-gap: 24px; } }

/* --------------------------------------------------------------------------
   HEADLINE TREATMENT
   The brief writes every headline in capitals. Display type set in capitals
   needs its tracking opened back up — Archivo at -.025em is drawn for mixed
   case, and caps at that setting run into each other.
   -------------------------------------------------------------------------- */
/* Set in title case, so no transform and no tracking override: these
   inherit the main site's -.025em for h1 to h4 like every other heading. */
.h-caps {
  text-wrap: balance;
}
.h-caps em { letter-spacing: inherit; }

/* A headline's quiet half. Used where the brief pairs a hard statement with a
   softer second clause: "IT'S NOT JUST ABOUT DOING MORE. it's about doing what
   actually works." */
.h-soft { font-weight: 600; color: var(--text); }

/* --------------------------------------------------------------------------
   ANALYSIS GRID
   The coach's-whiteboard ground. A faint ruled grid plus one diagonal sweep,
   drawn in gradients so it costs no request and scales to any band height.
   -------------------------------------------------------------------------- */
.askew { position: relative; isolation: isolate; overflow: hidden; }
.askew::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 96px 96px;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 72%);
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 72%);
  opacity: .8;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   1. HERO + VSL
   The brief wants the headline first and the 90 second VSL "prominently near
   the top". Rather than stack them and push the video under the fold on a
   phone, the hero carries the player: headline, player, then the supporting
   copy and both CTAs directly beneath it.
   -------------------------------------------------------------------------- */
.ahero {
  position: relative;
  /* The header is sticky and transparent at rest, so the hero runs up behind
     it exactly as the main site’s does. Without the negative margin the
     photograph started below the header and the top of the section read as a
     flat band of page colour. */
  margin-top: calc(var(--header-h) * -1);
  padding-block: calc(var(--header-h) + var(--space-2xl)) var(--section-y);
  overflow: hidden;
  isolation: isolate;
}
.ahero__bg {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.ahero__bg img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 32%; }
.ahero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(7, 11, 16, .82) 0%, rgba(7, 11, 16, .93) 46%, var(--page-bg) 100%),
    radial-gradient(90% 70% at 50% 8%, rgba(128, 192, 224, .16), transparent 62%);
}

.ahero__head { max-width: 60rem; margin: 0 auto var(--space-2xl); text-align: center; }
/* The count pill sits above the eyebrow, so it needs its space below rather
   than above: .proof--head is written for the main site, where the same pill
   sits under a heading and carries margin-top instead.

   It centres itself rather than inheriting the alignment of the block it is
   in. .proof is inline-flex, so it was placed by .ahero__head's text-align:
   centred from 768 up and hard left below it, where the head switches to
   text-align: left. Measured, the pill sat 51px off centre at 320 and 450px
   off at 767. Auto margins need a block level box to work on, hence display:
   flex, and a block level flex container with width:auto would fill the
   column, hence width: fit-content. max-width comes from .proof, so a label
   too long for the line still wraps inside the pill instead of overflowing.
   This also replaces the old width:auto, which existed only to undo
   style.css's `.proof { width: 100% }` below 768. */
.ahero__proof {
  display: flex;
  width: fit-content;
  margin: 0 auto var(--space-lg);
}
/* .proof sets text-align: left for the main site, where this label is always
   one line. Here it wraps to two at 320 and 360, and a left ragged pair inside
   an otherwise centred hero was the one thing left pointing the wrong way.
   A no op at every width where the label fits on one line. */
.ahero__proof .proof__line { text-align: center; }
.ahero__head h1 { margin-bottom: var(--space-lg); }
.ahero__lede {
  color: var(--text);
  font-size: clamp(1.02rem, 1.5vw, 1.18rem);
  max-width: 46rem;
  margin-inline: auto;
}

/* The player. A 16:9 frame with corner brackets, so even before it is played
   it reads as footage under analysis rather than an embed. */
.vsl {
  position: relative;
  max-width: 980px;
  margin: 0 auto;
}
/* The 980px cap suits the hero, where the player sits under a centred headline
   and a narrower measure. The analysis player leads its section above two full
   width card columns, and at 980px it sat visibly narrower than everything
   beneath it. Full shell width lines it up with the cards. */
.vsl--wide { max-width: none; }
.vsl__frame {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  box-shadow: var(--shadow-lg), var(--glow);
}
.vsl__frame .video,
.vsl__frame .vplay { aspect-ratio: 16 / 9; width: 100%; }

/* No aspect-ratio on this player: its shape comes from the media itself, the
   poster at rest and the <video> once it replaces it. Both are now the clip's
   own 16:9, so the thumbnail shows exactly what plays and pressing play does
   not change the frame's height.

   The poster was briefly cut to the sharp band of the clip, 2419x672. That
   looked better standing still, but the file is not cropped, so playback
   jumped to nearly twice the height and brought back the padding the still
   had hidden. Matching the still to the clip is the honest version of the two.

   Still worth saying: the source is a phone recording of a side by side
   comparison padded out to 16:9 with a blurred blow up of itself, 56% of every
   frame. A re-export cropped to its own picture would be sharper and would let
   the player take any shape without this trade off. */
.vsl--wide .vsl__frame .vplay { aspect-ratio: auto; }
.vsl--wide .vplay__poster { height: auto; object-fit: fill; }
.vsl--wide .vsl__frame video { aspect-ratio: auto; height: auto; }

/* Corner brackets, drawn outside the frame so they never sit over the picture */
.vsl__frame::before,
.vsl__frame::after {
  content: "";
  position: absolute;
  width: 34px;
  height: 34px;
  border: 2px solid var(--brand-300);
  opacity: .55;
  pointer-events: none;
  z-index: 3;
}
.vsl__frame::before { top: 10px; left: 10px; border-right: 0; border-bottom: 0; border-radius: 6px 0 0 0; }
.vsl__frame::after { bottom: 10px; right: 10px; border-left: 0; border-top: 0; border-radius: 0 0 6px 0; }

.vsl__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-sm) var(--space-lg);
  margin-top: var(--space-md);
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.vsl__meta span { display: inline-flex; align-items: center; gap: 8px; }
.vsl__meta span::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand-300);
}

/* Click-to-play facade, local to this page so its wording is its own */
.vplay {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--surface-2);
  cursor: pointer;
  overflow: hidden;
}
.vplay__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s var(--ease), filter .5s var(--ease);
}
.vplay:hover .vplay__poster { transform: scale(1.03); filter: brightness(.86); }
.vplay__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(7, 11, 16, .1) 0%, rgba(7, 11, 16, .62) 100%);
}
/* The hero player carries the client's own thumbnail artwork and no longer has
   a corner label, so the heavy foot of that gradient is dimming a graphic
   designed to be vivid, "TRAINING" and "FIGHT IQ | TECHNIQUE | ANALYSIS" most
   of all. A light even wash is enough to seat the play button, which is a
   filled disc with its own contrast. The analysis player keeps the full
   gradient: its label still sits in the bottom left. */
.ahero .vplay__scrim {
  background: linear-gradient(180deg, rgba(7, 11, 16, .1) 0%, rgba(7, 11, 16, .24) 100%);
}
/* ...and for the same reason the disc comes off dead centre, here only. The
   artwork sets its headline in the left half and dead centre is exactly where
   "PERFORMING" ends, so a centred disc covered the G at every width and took
   the ampersand of "TRAINING &" as well below 430, where it is 23% of the
   frame against 8% at 1440. 75% is the centre of the half the artwork left
   free, not a nudge: measured clearance from the type is 39px at 320 rising to
   208px at 1440, and the disc stays clear of the raised glove everywhere but
   320, where it laps 6px of it. Only `left` moves, so the hover transform
   above still applies. The analysis player keeps its disc centred: its still
   is a video frame with nothing written on it. */
.ahero .vplay__btn { left: 75%; }
.vplay__btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 82px;
  height: 82px;
  border-radius: 50%;
  background: var(--brand-600);
  color: #fff;
  box-shadow: 0 0 0 10px rgba(128, 192, 224, .16);
  transition: transform .26s var(--ease), box-shadow .26s var(--ease), background-color .26s var(--ease);
}
.vplay__btn svg { width: 30px; height: 30px; margin-left: 4px; }
.vplay:hover .vplay__btn {
  transform: translate(-50%, -50%) scale(1.07);
  background: var(--brand-500);
  box-shadow: 0 0 0 16px rgba(128, 192, 224, .14);
}
.vplay__label {
  position: absolute;
  left: var(--space-lg);
  bottom: var(--space-lg);
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font: 600 .84rem/1 var(--font-body);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #fff;
}
/* Drawn inside: .vsl__frame clips, and the button fills it exactly, so an
   outline drawn outside the border box never appears. Mirrors what
   style.css does for .accordion__btn in its clipped parent. */
.vplay:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 3px var(--brand-300), inset 0 0 0 6px rgba(7, 11, 16, .85);
}
.vplay[disabled] { cursor: default; }
.vplay[disabled] .vplay__btn { display: none; }
.vplay__note {
  margin: var(--space-sm) 0 0;
  text-align: center;
  font-size: .86rem;
  color: var(--muted);
}
.vsl__frame video { display: block; width: 100%; aspect-ratio: 16 / 9; background: #000; }

/* Supporting copy + CTAs, immediately under the player as specified */
.ahero__under {
  /* 46rem, matching .ahero__lede above it. At 54rem this block set a 107
     character measure in the smaller of the two type sizes, so the supporting
     copy was given a wider column than the lede it supports. */
  max-width: 46rem;
  /* --space-xl, not --space-2xl: this copy supports the player directly above
     it, so it sits closer to the player than the headline block does. */
  margin: var(--space-xl) auto 0;
  text-align: center;
}
.ahero__under p { color: var(--text); font-size: 1.02rem; }
.ahero__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}

/* The central message, carried as a marquee strip under the hero */
.creed {
  border-block: 1px solid var(--line);
  background: var(--page-bg-2);
  padding-block: var(--space-lg);
  overflow: hidden;
}
.creed__row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-lg);
  flex-wrap: wrap;
  text-align: center;
}
.creed p {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  /* Title case like every other title on the page. This band repeats the
     central message word for word from the h1, so it cannot be set in
     capitals while the h1 above it is not. */
  letter-spacing: -.01em;
  font-size: clamp(.95rem, 1.9vw, 1.35rem);
  color: var(--heading);
}
.creed em { color: var(--accent); }

/* --------------------------------------------------------------------------
   3. THE GAP
   -------------------------------------------------------------------------- */
/* ch, not rem. 62rem is 992px, about 124 characters a line at this size,
   roughly double a readable measure. The main site caps in ch throughout. */
.gap__copy { max-width: 62ch; }
/* 1.02rem, matching .section-head__note, which is what the other five
   sections already use for the same job. It was 1.05rem, which put four
   different sizes on the page for one kind of paragraph. */
.gap__copy p { font-size: 1.02rem; }

/* The four questions. Each is a card, because on the page they are the moment
   the visitor tests themselves, not a list to skim.
   Explicit columns rather than auto-fit: auto-fit with a 240px floor produced
   THREE columns between roughly 860 and 1100 wide, which left the fourth card
   alone in a row filled 32% of the way across. Four divides evenly into four,
   two and one, and never into three. */
.asks {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin: var(--block-gap) 0;
}
.ask {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding: var(--card-pad);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  /* A direction of light rather than a flat fill. The second stop sits past
     100% so only the last of the band colour reaches the bottom corner. */
  background: linear-gradient(165deg, var(--surface) 0%, var(--surface-2) 190%);
  display: flex;
  flex-direction: column;
  /* Top aligned with one fixed gap, NOT space-between. Anchoring the question
     to the foot lined the four bottoms up, but it also meant the gap between
     the number and the question changed from card to card: measured, 32px in
     the two cards with a three line question and 56px in the two with a two
     line question, which is the largest space anywhere in this section and
     reads as a hole. A constant gap gives every card the same internal
     rhythm; the leftover height falls to the bottom of the shorter cards
     where the card edges are already aligned, so it is not seen. */
  justify-content: flex-start;
  gap: var(--space-lg);
  transition: border-color .24s var(--ease), transform .24s var(--ease), box-shadow .24s var(--ease);
}
/* Drawn left to right on hover, like a marker run along the top of the card.
   This section asks the visitor four questions about themselves, so the card
   should answer back when they reach it. */
.ask::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--brand-600), var(--brand-300));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .42s var(--ease);
}
.ask:hover {
  border-color: var(--line-strong);
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.ask:hover::before { transform: scaleX(1); }

/* Outlined, so the numeral reads as a marker on a diagram rather than as a
   label sitting next to the text. The filled low opacity rule comes first and
   is the fallback: without it, a browser that does not support text stroke
   would paint the numeral in transparent and show nothing at all. */
.ask__q {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 3.2vw, 3.2rem);
  font-weight: 800;
  line-height: .82;
  letter-spacing: -.03em;
  color: var(--brand-400);
  opacity: .34;
  transition: opacity .24s var(--ease);
}
@supports (-webkit-text-stroke: 1px currentColor) {
  .ask__q {
    color: transparent;
    -webkit-text-stroke: 1.5px var(--brand-500);
    opacity: .58;
  }
}
.ask:hover .ask__q { opacity: .9; }

.ask p {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.12rem;
  line-height: 1.32;
  color: var(--heading);
  letter-spacing: -.015em;
  /* four short questions, so balance keeps one word from hanging alone */
  text-wrap: balance;
}
.gap__close {
  max-width: 62ch;
  font-size: 1.02rem;
  color: var(--text);
}
.gap__close strong { color: var(--heading); }

/* ---- INFOGRAPHIC 1: THE BOXING KNOWLEDGE PYRAMID ------------------------ */
.pyramid {
  /* A <figure>, so the browser supplies margin: 1em 40px. Without resetting
     the inline sides the card sat 40px in from the shell on every width while
     everything around it ran flush, which at 390 left it 270px wide inside a
     350px column. The shorthand kills all four. */
  margin: var(--block-gap) 0 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: var(--card-pad);
  overflow: hidden;
}
.pyramid__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding-bottom: var(--space-lg);
  margin-bottom: var(--space-xl);
  border-bottom: 1px solid var(--line);
}
.pyramid__head h3 { margin: 0; }

/* Two tracks sharing one set of rows: the tiers on the left, their meaning on
   the right. Because both columns are children of the same grid, a tier and
   its description are always on the same line with no measuring. */
.pyramid__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 var(--space-2xl);
  align-items: stretch;
}
.pyramid__stack { display: grid; gap: var(--space-xs); align-content: center; }
.pyramid__legend { display: grid; gap: var(--space-xs); }

.tier {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 84px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--heading);
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: clamp(.82rem, 1.2vw, 1.02rem);
  transition: background-color .3s var(--ease), color .3s var(--ease);
}
/* Trapezoids. Each tier's top edge is the previous tier's bottom edge, so the
   four clips together draw one continuous pyramid with no seams. */
.tier--1 { clip-path: polygon(35% 0, 65% 0, 73% 100%, 27% 100%); }
.tier--2 { clip-path: polygon(27% 0, 73% 0, 82% 100%, 18% 100%); }
.tier--3 { clip-path: polygon(18% 0, 82% 0, 91% 100%, 9% 100%); }
.tier--4 { clip-path: polygon(9% 0, 91% 0, 100% 100%, 0 100%); }

/* The three tiers the Academy adds carry the brand; the foundation stays
   neutral, which is the whole point the graphic is making.
   The text flips from white to ink as the blue lightens, because measured
   against the real tokens white on --brand-500 is only 3.59:1 and these
   labels are 13 to 16px bold, which is not large text, so they need 4.5.
   Ink on --brand-500 is 5.19:1. The foundation label was --muted on
   --surface-2 at 4.26:1 and is now --text at 6.92:1. The blue ramp itself is
   untouched. */
.tier--1 { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }
.tier--2 { background: var(--brand-500); border-color: var(--brand-500); color: var(--heading); }
.tier--3 { background: var(--brand-400); border-color: var(--brand-400); color: var(--heading); }
.tier--4 { background: var(--surface-2); color: var(--text); }

.tier__label { padding-inline: 8px; text-align: center; }

.rung {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  align-items: center;
  gap: var(--space-md);
  min-height: 84px;
  padding-inline: 0;
  border-bottom: 1px dashed var(--line);
}
.rung:last-child { border-bottom: 0; }
.rung__arrow {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-chip);
  border: 1px solid var(--line-strong);
  color: var(--accent-ink);
}
.rung__arrow svg { width: 16px; height: 16px; transform: rotate(-90deg); }
/* The bottom rung is where the ladder starts, so its marker points nowhere */
.rung--base .rung__arrow svg { transform: none; opacity: .45; }
.rung__name {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: .95rem;
  color: var(--heading);
  margin-bottom: 2px;
}
.rung__text { margin: 0; font-size: .95rem; line-height: 1.55; color: var(--text); }

.pyramid__foot {
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--line);
}
.pyramid__foot p { margin: 0; }
.pyramid__claim {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.005em;
  font-size: clamp(1.1rem, 2.1vw, 1.5rem);
  color: var(--heading);
  line-height: 1.15;
  margin-bottom: 6px !important;
}
.pyramid__claim em { color: var(--accent); }

/* --------------------------------------------------------------------------
   4. TRAINING WITH PURPOSE — INFOGRAPHIC 2
   -------------------------------------------------------------------------- */
.versus {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: stretch;
  margin-top: var(--block-gap);
}
/* Flex rather than grid, purely so the outcome line can be pushed to the
   bottom with margin-top:auto. The two columns hold a different number of
   steps — three against five — and the comparison only lands if both verdicts
   sit on the same line as each other. */
.vcol {
  position: relative;      /* lifts the card above the band's .edge-label */
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--card-pad);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
/* .vcol__steps carries flex:1 and absorbs the spare height, so the outcome is
   already at the foot and does not need margin-top:auto to push it there. */
/* The losing column is deliberately colourless. The palette has one saturated
   hue and it belongs to the approach being recommended; draining this side is
   what makes the comparison read at a glance. */
.vcol--grind { background: var(--page-bg-2); }
.vcol--grind .vcol__title { color: var(--muted); }
.vcol--smart { border-color: var(--line-strong); box-shadow: var(--glow); }

.vcol__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  /* set in title case in the markup; the positive tracking went with the
     capitals and closes back up for mixed case */
  letter-spacing: -.01em;
  /* floor raised from .98rem: 1.6vw does not clear it until a 980px viewport,
     so below that the title computed 15.68px against 15.2px items and read as
     bold body copy rather than a heading. Desktop value unchanged. */
  font-size: clamp(1.12rem, 1.6vw, 1.18rem);
  color: var(--heading);
}
/* The steps sit in a right hand track; the left gutter carries the spine.
   34px of padding leaves room for a 2px line at 12px with air either side. */
.vcol__steps {
  position: relative;
  display: grid;
  /* start, not stretch: the chips stay at the top and the container takes the
     spare height below them, which is where the spine continues */
  align-content: start;
  /* absorb whatever the two columns' different step counts leave over. This
     replaces a filler element that sat between the steps and the outcome and
     brought a flex gap on both sides of itself. */
  flex: 1;
  /* the visible separation, now that .vstep no longer smuggles in a 16px
     paragraph margin on top of it */
  gap: var(--space-lg);
  padding-left: 34px;
}
/* From the vertical centre of the first chip to the centre of the last. A
   chip is 12px padding, a 26px dot and 12px padding, plus a 1px border top and
   bottom, so 52px tall, and 26px is half of it. */
.vcol__steps::before {
  content: "";
  position: absolute;
  left: 12px;
  top: 26px;
  bottom: 26px;
  width: 2px;
  border-radius: 2px;
}
/* Down to the foot of the container, not to the last chip: past the final
   step the line carries on and stops, which is the column's whole argument.
   The padding guarantees a visible run even where the two columns are stacked
   and there is no spare height to absorb. */
.vcol--grind .vcol__steps { padding-bottom: var(--space-lg); }
.vcol--grind .vcol__steps::before {
  bottom: 0;
  background: repeating-linear-gradient(to bottom, var(--muted) 0 6px, transparent 6px 13px);
  opacity: .5;
}
/* the terminator: the line stops here and goes nowhere */
.vcol--grind .vcol__steps::after {
  content: "";
  position: absolute;
  left: 4px;
  bottom: 0;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--muted);
  opacity: .5;
}
.vcol--smart .vcol__steps::before {
  background: linear-gradient(to bottom, var(--brand-600), var(--brand-300));
}


.vstep {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  align-items: center;
  gap: var(--space-sm);
  /* p carries a 16px bottom margin from the shared stylesheet, the same trap
     .vcol__out and .vcol__loop already guard against. Left in, it stacked on
     top of the container's own gap, so chips declared 12px apart actually sat
     28px apart and the five step column ran 64px taller than designed. The
     separation now comes from .vcol__steps { gap } alone. */
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  /* a direction of light, so the chip reads as a surface rather than a fill */
  background: linear-gradient(140deg, var(--surface-2) 0%, var(--surface) 130%);
  font-size: .95rem;
  color: var(--text);
  transition: border-color .22s var(--ease), transform .22s var(--ease);
}
/* Each chip meets the spine with a short connector, so the line reads as
   feeding the step rather than running behind it. */
.vstep::before {
  content: "";
  position: absolute;
  left: -22px;
  top: 50%;
  width: 22px;
  height: 2px;
  margin-top: -1px;
  background: var(--muted);
  opacity: .5;
}
.vcol--smart .vstep::before { background: var(--brand-500); opacity: 1; }
.vcol--smart .vstep:hover { border-color: var(--line-strong); transform: translateX(2px); }
.vcol--grind .vstep { opacity: .82; }
.vstep__dot {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font: 700 .72rem/1 var(--font-body);
  background: rgba(255, 255, 255, .08);
  color: var(--muted);
}
.vcol--smart .vstep {
  border-color: var(--line-strong);
  color: var(--heading);
  font-weight: 500;
}
.vcol--smart .vstep__dot { background: var(--brand-600); color: #fff; }

/* The row that closes each column. It keeps the dashed rule and the spine's
   34px inset, so the divider still spans the card; the verdict itself sits in
   a pill inside it. Both columns' pills therefore still start on the same
   line as each other, which is the whole point of the equal card heights. */
.vcol__out {
  padding-left: 34px;
  margin-bottom: 0;
  padding-top: var(--space-md);
  border-top: 1px dashed var(--line);
}
/* The verdict pill. Replaces a literal "=" the client asked to lose: the icon
   carries the "adds up to" sense and the pill gives the line somewhere to sit
   rather than floating as one more paragraph. Built from the same parts as
   .vstep and the price panel's tick chips, so it reads as the same family. */
.vcol__verdict {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  /* balance, or "BOXING THAT ACTUALLY IMPROVES" leaves IMPROVES alone on a
     second line at phone width while the opposite column's verdict sits on
     one, which makes the two sides of the comparison look unequal */
  text-wrap: balance;
  padding: var(--space-sm) var(--space-md);
  border-radius: 999px;
  border: 1px solid var(--line);
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .03em;
  font-size: .95rem;
  line-height: 1.25;
}
.vcol__verdict-icon {
  /* flex:none, or the glyph is the first thing squeezed when the label wraps */
  flex: none;
  width: 19px;
  height: 19px;
}
/* The losing column stays colourless: the palette has one saturated hue and it
   belongs to the approach being recommended. */
.vcol--grind .vcol__verdict {
  color: var(--muted);
  background: var(--surface-2);
}
.vcol--smart .vcol__verdict {
  color: var(--accent);
  border-color: var(--line-strong);
  background: rgba(128, 192, 224, .1);
}

/* The loop marker on the smart column: Review feeds Learn */
.vcol__loop {
  /* aligned to the spine's track so the loop marker closes the line rather
     than floating beside it */
  padding-left: 34px;
  /* p carries a 16px bottom margin from the shared stylesheet, which stacked
     on top of the column's own gap */
  margin-bottom: 0;
  display: flex;
  /* flex-start, not center: below about 430px the label wraps to two lines and
     centring floated the glyph into the leading between them, aligned with
     neither. Anchored to the top it stays on the first line at every width. */
  align-items: flex-start;
  gap: var(--space-xs);
  /* balance, or this drops the last word onto a line by itself at 390 */
  text-wrap: balance;
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
/* em derived, not a pixel: optically centres the 15px glyph on the cap height
   of the first line and keeps doing so if the label's font size changes. */
.vcol__loop svg { width: 15px; height: 15px; margin-block-start: .18em; flex: none; color: var(--accent-ink); }

.versus__split {
  position: relative;
  display: grid;
  place-items: center;
  align-self: center;
}
/* hairlines reaching for each column, so the badge reads as the hinge of the
   comparison rather than a disc dropped between two cards */
.versus__split::before,
.versus__split::after {
  content: "";
  position: absolute;
  top: 50%;
  width: var(--space-xl);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong));
}
.versus__split::before { right: 100%; }
.versus__split::after { left: 100%; transform: scaleX(-1); }
.versus__split span {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: .86rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

.verdict {
  margin-top: var(--block-gap);
  text-align: center;
  max-width: 54rem;
  margin-inline: auto;
}
.verdict h3 {
  font-size: clamp(1.4rem, 3.2vw, 2.3rem);
  letter-spacing: -.01em;
  /* balance, or the second sentence's opening "It's" is left stranded at the
     end of the first line, split from the clause it belongs to */
  text-wrap: balance;
  margin: 0;
}
.verdict h3 em { color: var(--accent); }

/* --------------------------------------------------------------------------
   5. WHAT IS INSIDE — PILL CARDS
   -------------------------------------------------------------------------- */
/* Three across, not four. Four fitted, but at this shell width it left each
   card about 200px of measure, and several of these descriptions run to four
   or five lines — the cards turned into columns of thin text. Three gives a
   readable measure and still shows the whole offer without much scrolling.
   Nine cards fill three clean rows; the tenth is the community card, which
   spans the row on purpose (see below). */
.pills {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--grid-gap);
}
.pill-card {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--space-md);
  padding: var(--card-pad);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  transition: border-color .24s var(--ease), transform .24s var(--ease), box-shadow .24s var(--ease);
}
.pill-card:hover {
  border-color: var(--line-strong);
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
/* The pill itself: number, icon and name on one rounded bar */
.pill-card__pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 8px 18px 8px 8px;
  border-radius: 999px;
  background: var(--accent-chip);
  border: 1px solid var(--line-strong);
  width: fit-content;
  max-width: 100%;
}
.pill-card__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--brand-600);
  color: #fff;
  flex: 0 0 auto;
}
.pill-card__icon svg { width: 18px; height: 18px; }
.pill-card__num {
  font: 700 .74rem/1 var(--font-body);
  letter-spacing: .14em;
  color: var(--accent-ink);
  flex: 0 0 auto;
}
.pill-card h3 {
  margin: 0;
  font-size: 1.12rem;
  letter-spacing: .01em;
}
.pill-card p { margin: 0; font-size: .95rem; line-height: 1.55; color: var(--text); }

/* The community card. It is the tenth of nine rows of three, so rather than
   leave it stranded alone on a last row it takes the full width and turns on
   its side. That is not only tidier: this is the community page, and the
   community belongs at the end of the list as the thing that holds the rest
   together, not as an afterthought in a corner. */
.pill-card--wide {
  grid-column: 1 / -1;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-2xl);
  background:
    radial-gradient(90% 140% at 0% 50%, var(--accent-chip), transparent 62%),
    var(--surface);
  border-color: var(--line-strong);
}
.pill-card--wide .pill-card__pill { grid-row: 1 / 3; align-self: center; }
.pill-card--wide h3 { align-self: end; }
.pill-card--wide p { align-self: start; max-width: 70ch; }

/* --------------------------------------------------------------------------
   6. SEE WHAT YOU CAN'T FEEL — INFOGRAPHIC 3
   -------------------------------------------------------------------------- */
/* The freeze frame. Corner brackets, a timing marker and a scan line, so the
   still reads as a moment pulled out of footage rather than a photograph. */
.frame {
  /* also a <figure>: same UA margin to clear */
  margin: 0;
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-strong);
  background: #000;
}
.frame img {
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  object-position: 50% 38%;
  opacity: .78;
}
/* The analysis marks. Drawn as positioned boxes rather than one SVG, so a
   circle stays a circle whatever shape the frame is cropped to — an SVG
   stretched to a 21:9 band and then to 16:10 on a phone would not. The marks
   are deliberately generic: brackets, a guide line and a region ring. They
   say "this frame is being read", without claiming to point at a body part
   in a photograph whose composition may change. */
.frame__marks { position: absolute; inset: 0; pointer-events: none; }
.frame__bracket {
  position: absolute;
  width: 46px;
  height: 46px;
  border: 2px solid var(--brand-300);
  opacity: .7;
}
.frame__bracket--tl { top: 16px; left: 16px; border-right: 0; border-bottom: 0; }
.frame__bracket--tr { top: 16px; right: 16px; border-left: 0; border-bottom: 0; }
.frame__bracket--bl { bottom: 16px; left: 16px; border-right: 0; border-top: 0; }
.frame__bracket--br { bottom: 16px; right: 16px; border-left: 0; border-top: 0; }
/* The horizontal guide a coach draws across a still to check the level */
.frame__guide {
  position: absolute;
  left: 8%;
  right: 8%;
  top: 50%;
  border-top: 1px dashed rgba(128, 192, 224, .5);
}
.frame__guide::after {
  content: "";
  position: absolute;
  left: 0;
  top: -3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand-300);
}
/* The region under review */
.frame__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(90px, 13vw, 150px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 2px dashed rgba(128, 192, 224, .65);
  border-radius: 50%;
  box-shadow: 0 0 0 9999px rgba(7, 11, 16, .28);
}
/* Inset past the corner bracket, not level with it. At the bracket's own
   --space-md the opaque pill painted straight over the top-left mark, hiding
   its horizontal leg and leaving the vertical one as a stub hanging off the
   pill, while the other three corners drew in full. One --space-sm further in
   clears both 2px legs and lets the mark frame the label instead. */
.frame__time {
  position: absolute;
  top: calc(var(--space-md) + var(--space-sm));
  left: calc(var(--space-md) + var(--space-sm));
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 6px 12px;
  border-radius: var(--radius-xs);
  background: rgba(7, 11, 16, .72);
  border: 1px solid var(--line-strong);
  font: 600 .74rem/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  letter-spacing: .12em;
  color: var(--brand-300);
}
.frame__time::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand-300);
}
/* Same clearance as .frame__time: level with the bracket, the closing rule of
   the bottom-right mark was drawn through the caption's last words. */
.frame__caption {
  position: absolute;
  right: calc(var(--space-md) + var(--space-sm));
  bottom: calc(var(--space-md) + var(--space-sm));
  margin: 0;
  max-width: 24rem;
  text-align: right;
  font-size: .86rem;
  line-height: 1.4;
  color: rgba(255, 255, 255, .8);
  text-shadow: 0 1px 12px rgba(0, 0, 0, .9);
}

.feel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-xl);
  margin-top: var(--space-xl);
}
.fcol {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--card-pad);
  background: var(--surface);
  display: grid;
  align-content: start;
  gap: var(--space-lg);
}
.fcol--truth { border-color: var(--line-strong); box-shadow: var(--glow); }
.fcol__title {
  margin: 0;
  display: flex;
  align-items: center;
  /* wrap, or the tag pill squeezes the title into three lines on a phone
     while the neighbouring column keeps its title on one */
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  font-family: var(--font-display);
  font-weight: 800;
  /* the positive tracking went with the capitals; mixed case closes up */
  letter-spacing: -.01em;
  /* floor raised from .98rem: 1.6vw does not clear it until a 980px viewport,
     so below that the title computed 15.68px against 15.2px items and read as
     bold body copy rather than a heading. Desktop value unchanged. */
  font-size: clamp(1.12rem, 1.6vw, 1.18rem);
  color: var(--heading);
}
.fcol__tag {
  /* .72rem/.18em. It was .68rem/.14em, which read as the same small uppercase
     tag rendered inconsistently against the one the pyramid used to carry. */
  font: 600 .72rem/1 var(--font-body);
  letter-spacing: .18em;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--muted);
  white-space: nowrap;
}
.fcol--truth .fcol__tag {
  border-color: var(--line-strong);
  background: var(--accent-chip);
  color: var(--accent-ink);
}
.fcol ul { display: grid; gap: 12px; }
.fcol li {
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-2);
  font-size: .95rem;
  /* 1.55, matching .pill-card p and .rung__text. These three are all body
     copy inside a card and were set at 1.5, 1.55 and 1.45, which is close
     enough to read as an accident rather than three decisions. */
  line-height: 1.55;
}
/* The felt side is quoted speech and reads as the boxer's own voice */
.fcol--felt li {
  font-style: italic;
  color: var(--muted);
  border-style: dashed;
}
.fcol--truth li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  color: var(--heading);
  border-color: var(--line-strong);
}
.fcol--truth li span:first-child {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--brand-600);
  color: #fff;
}
.fcol--truth li svg { width: 13px; height: 13px; }

.reveal-line {
  margin-top: var(--block-gap);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: center;
}
.reveal-line h3 {
/* Shares the step with .verdict h3, the page's other big statement h3.
     At clamp(1.7rem, 3.6vw, 2.9rem) it carried h2's own vw term and maximum,
     so it rendered identically to the h2 above it from 1024 up and actually
     larger than it at 320 and 390. */
  font-size: clamp(1.4rem, 3.2vw, 2.3rem);
  letter-spacing: -.015em;
  margin-bottom: var(--space-md);
}
.reveal-line h3 em { color: var(--accent); }
.reveal-line p { color: var(--text); font-size: 1.02rem; }

/* --------------------------------------------------------------------------
   8. TESTIMONIALS
   -------------------------------------------------------------------------- */
/* Three across rather than four: a review is a paragraph of someone's own
   words, and four columns at this shell width gives each one a measure too
   narrow to read as speech. Six slots then fill two clean rows. */
/* Thirteen reviews stacked down the page ran longer than the rest of the
   section put together, so they travel sideways instead. A scroll snap rail,
   not a scripted slider: it swipes on touch, scrolls with the keyboard, and
   still works with the script blocked. The arrows academy.js adds are an
   affordance on top of a thing that already works. */
.qrail { margin-top: var(--block-gap); }
.quotes {
  display: grid;
  grid-auto-flow: column;
  /* three across, less the two gaps between them */
  /* Three across, less the two gaps AND a 44px slice of the fourth. Without
     that subtraction the three cards plus their gaps filled the content box
     to the pixel, so from 768 up not one pixel of the next card showed and
     nothing on screen said the rail scrolls. The phone rule below has always
     reserved that slice; these two did not. It matters most with the script
     blocked, where there are no arrows and no scrollbar either. */
  grid-auto-columns: calc((100% - 2 * var(--grid-gap) - 44px - var(--grid-gap)) / 3);
  gap: var(--grid-gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* the cards carry a shadow and a focus ring; without room they would be
     shaved off by the scroll container's own edge */
  padding: 4px 4px var(--space-md);
  margin-inline: -4px;
  /* matched to that padding, or every snap position sits 4px further along
     than the scrollport and the rail comes to rest at scrollLeft 4, which
     reads as "already scrolled" and leaves the back arrow live at the start */
  /* start, not stretch. All thirteen cards sit in ONE grid row, and a grid
     row is as tall as its tallest item, so stretch made every card match the
     longest review in the set: a two line quote in a box built for a six line
     one. With start each card is its own height, and academy.js then sets the
     rail's height to the tallest of the cards currently in view, so the row on
     screen looks even while the rail still shrinks for the short ones.
     overflow-y is hidden because a taller card waiting off screen would
     otherwise poke out of that height and raise a vertical scrollbar.
     With the script blocked no height is ever set, the rail stays auto, and
     every card simply takes its own. */
  align-items: start;
  overflow-y: hidden;
  transition: height .28s var(--ease);
  scroll-padding-inline: 4px;
  /* No scrollbar: the arrows and the snap carry the affordance, and a bar
     under a row of cards read as a stray rule. The rail is still scrollable
     by wheel, swipe and keyboard. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.quotes::-webkit-scrollbar { display: none; }
/* The state academy.js measures in: the rail lets go of its height and the
   cards fall back to their own, so what it reads is each card's natural
   height rather than the one it was last given. Held for a single synchronous
   read, so it is never painted. */
.quotes.is-measuring {
  height: auto;
  align-items: start;
  transition: none;
}
/* Offset only, no outline shorthand. The shorthand restated the base ring's
   --brand-300, which is right on the dark bands and wrong here: the reviews
   sit on .band-light, and style.css:229 exists to swap that ring to
   --brand-600 for exactly this ground. Both selectors weigh 0,2,0 and this
   file loads second, so the shorthand was winning and putting the ring back
   to 1.85:1 against the band, under the 3:1 WCAG 1.4.11 asks of a focus
   indicator, while every other control in the same band sat at 5.07:1.
   Leaving the colour to the cascade fixes it on both grounds at once. */
.quotes:focus-visible { outline-offset: 3px; }
.quote {
  position: relative;
  scroll-snap-align: start;
  display: grid;
  align-content: start;
  gap: var(--space-sm);
  padding: var(--card-pad);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  /* a direction of light, the same one the step chips use, so the card reads
     as a surface rather than a flat fill */
  background: linear-gradient(160deg, var(--surface-2) 0%, var(--surface) 120%);
  transition: border-color .22s var(--ease), transform .22s var(--ease),
              min-height .28s var(--ease);
}
.quote:hover { border-color: var(--line-strong); transform: translateY(-2px); }
/* The opening quote mark, drawn rather than typed: it sits behind the text as
   a watermark, so it reads as punctuation on the card and never as content. */
.quote::before {
  /* The empty alt text after the slash keeps this out of the accessibility
     tree. Without it a screen reader announced a stray quotation mark at the
     top of all thirteen cards; it is decoration, and the blockquote already
     carries the quoting semantics. */
  content: "\201C" / "";
  position: absolute;
  top: 8px;
  right: var(--card-pad);
  font-family: var(--font-display);
  font-size: 5rem;
  line-height: 1;
  color: var(--accent);
  opacity: .14;
  pointer-events: none;
}
/* The reviewer's own review title. Set as text, not a pill: at thirteen cards
   a row of chips fought the quotes underneath them for attention. */
.quote__theme {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.02rem;
  line-height: 1.35;
  color: var(--heading);
  /* room for the quote mark, so a long title does not run under it */
  padding-right: 34px;
}
.quote blockquote {
  margin: 0;
  font-size: .97rem;
  line-height: 1.6;
  color: var(--text);
}
/* Six lines, then a read more. Every word of every review is in the markup;
   this only decides how much shows at rest, which is what stops one four
   paragraph review setting the height of all thirteen cards.

   Deliberately NOT on the base rule. The reveal button starts hidden and is
   shown by academy.js, so a clamp that applied unconditionally would, with
   the script blocked, cut five reviews off mid sentence with no control to
   open them. The clamp and its button now arrive together, from the script,
   so the no script rendering is simply every review in full.

   max-height rather than -webkit-line-clamp: the longer reviews are several
   <p> elements, and clamping a box with block children is unreliable.
   9.6em is six lines of the 1.6 line height this block sets.

   The fade starts at 88%, not 76%: at six lines a 76% stop reaches up into
   the fifth line and washes out text meant to be read. */
.quote.is-clamped blockquote {
  max-height: 9.6em;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, #000 88%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 88%, transparent 100%);
}
.quote.is-open blockquote {
  max-height: none;
  -webkit-mask-image: none;
  mask-image: none;
}
.quote blockquote p + p { margin-top: var(--space-sm); }
.quote__more {
  justify-self: start;
  /* padding, not a bare text link: at .86rem the hit area was 71x14, and WCAG
     2.2 SC 2.5.8 asks for 24 by 24. This is a button in a card, not a link
     inside a sentence, so the inline exception does not cover it. */
  padding-block: var(--space-xs);
  padding-inline: 0;
  border: 0;
  background: none;
  font: 600 .86rem/1 var(--font-body);
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.quote__more:hover { color: var(--brand-300); }
.quote__by {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin: var(--space-xs) 0 0;
  font-size: .82rem;
  color: var(--muted);
}
/* A short accent rule instead of a bullet: it ties the name to the card's
   accent without adding another dot to a page that has plenty. */
.quote__by::before {
  content: "";
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  opacity: .7;
  flex: none;
}
.quote__by strong { color: var(--heading); font-weight: 600; }

/* A slot waiting on the client's own words. Visibly a placeholder on the
   draft, and one class removal away from being a finished card. */
.quote--slot blockquote { color: var(--muted); font-style: italic; }
.quote--slot { border-style: dashed; }

/* Controls centred under the rail, now that the source line beside them has
   been removed. */
.qrail__foot {
  display: flex;
  justify-content: center;
  margin-top: var(--space-lg);
}
.qrail__ctrls { display: flex; gap: var(--space-sm); }
.qrail__btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--heading);
  cursor: pointer;
  transition: background .22s var(--ease), border-color .22s var(--ease), opacity .22s var(--ease);
}
.qrail__btn svg { width: 18px; height: 18px; }
.qrail__btn[data-rail="prev"] svg { transform: rotate(180deg); }
.qrail__btn:hover { background: var(--surface-2); border-color: var(--brand-300); }
/* Disabled rather than removed at the ends, so the control does not move
   under the pointer halfway through a run of clicks. */
.qrail__btn[disabled] { opacity: .38; cursor: default; }
.qrail__btn[disabled]:hover { background: var(--surface); border-color: var(--line-strong); }

/* --------------------------------------------------------------------------
   9. MEMBERSHIP
   -------------------------------------------------------------------------- */
.price {
  position: relative;      /* lifts the panel above the band's .edge-label */
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, .74fr) minmax(0, 1.26fr);
  gap: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
/* One accent along the top edge, the same device the question cards use on
   hover. Here it is always on: this is the card the page is asking for. */
.price::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  z-index: 2;
  background: linear-gradient(90deg, var(--brand-700), var(--brand-400), var(--brand-700));
}
.price__figure {
  padding: var(--space-2xl) var(--card-pad);
  background:
    radial-gradient(120% 100% at 0% 0%, rgba(128, 192, 224, .16), transparent 60%),
    var(--page-bg-2);
  display: grid;
  align-content: start;
  gap: var(--space-lg);
  /* A gradient rule rather than a border: a flat line cut straight across the
     card from edge to edge, which made the two halves read as two boxes
     rather than one offer. */
  border-right: 0;
  position: relative;
}
.price__figure::after {
  content: "";
  position: absolute;
  top: var(--space-2xl);
  bottom: var(--space-2xl);
  right: 0;
  width: 1px;
  background: linear-gradient(to bottom, transparent, var(--line-strong) 22%, var(--line-strong) 78%, transparent);
}
.price__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  width: fit-content;
  padding: 7px 15px;
  border-radius: 999px;
  background: var(--brand-600);
  color: #fff;
  font: 600 .74rem/1 var(--font-body);
  letter-spacing: .12em;
  text-transform: uppercase;
}
.price__badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .28);
}
.price__amount {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  font-family: var(--font-display);
  font-weight: 800;
  line-height: .9;
  letter-spacing: -.04em;
  color: var(--heading);
  font-size: clamp(3.6rem, 8vw, 7rem);
  font-variant-numeric: tabular-nums;
}
.price__amount sup {
  font-size: .38em;
  line-height: 1;
  margin-top: .22em;
  color: var(--accent);
}
/* On the number's baseline rather than stacked beneath it in capitals: a
   price is one idea and should read as one. */
.price__per {
  align-self: flex-end;
  margin: 0 0 .22em .1em;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -.01em;
  font-size: 1.02rem;
  color: var(--muted);
}
.price__cap { margin: 0; font-size: .95rem; color: var(--text); }
.price__cap strong { color: var(--accent); }

.price__body {
  padding: var(--space-2xl) var(--card-pad);
  display: grid;
  align-content: start;
  gap: var(--space-lg);
}
.price__body h3 { margin: 0; letter-spacing: -.005em; font-size: clamp(1.3rem, 2.4vw, 1.75rem); }
.price__body h3 em { color: var(--accent); }
.price__body > p { margin: 0; color: var(--text); }
.price__ticks { display: grid; gap: var(--space-sm); }
.price__ticks li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  font-size: .95rem;
  color: var(--text);
}
/* A tinted chip with a ring, matching the pyramid rungs and the inside
   cards. Four solid blue discs were the heaviest thing on the page. */
.price__ticks li > span:first-child {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  border-radius: 50%;
  background: rgba(128, 192, 224, .14);
  border: 1px solid var(--line-strong);
  color: var(--accent);
}
.price__ticks svg { width: 13px; height: 13px; }
.price__cta { display: flex; flex-wrap: wrap; gap: var(--space-md); align-items: center; }
/* Under the button, not beside it. Beside it, a commercial promise read as a
   caption that had nowhere else to go. */
.price__small {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: .86rem;
  color: var(--muted);
}
.price__small svg { width: 15px; height: 15px; color: var(--accent); flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   11. FINAL CTA
   -------------------------------------------------------------------------- */
.closer {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  text-align: center;
}
.closer__bg { position: absolute; inset: 0; z-index: -2; }
.closer__bg img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; }
.closer::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, var(--page-bg) 0%, rgba(7, 11, 16, .88) 40%, rgba(7, 11, 16, .95) 100%);
}
.closer__inner { max-width: 56rem; margin-inline: auto; }
/* No size override. This was clamp(2rem, 5vw, 3.6rem), which made the closing
   headline 57.6px against every other h2 at 46.4px: the only h2 on the page
   off the shared scale, and the longest line on the page as well. */
.closer h2 em { color: var(--accent); }
.closer p { color: var(--text); font-size: 1.02rem; max-width: 44rem; margin-inline: auto; }
.closer__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}

/* --------------------------------------------------------------------------
   MOBILE STICKY JOIN BAR
   Traffic arrives from Instagram and TikTok, which means one thumb and a lot
   of scrolling. The bar appears once the hero is behind you and never on
   desktop, where the header CTA is always in view anyway.
   -------------------------------------------------------------------------- */
.joinbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: none;
  align-items: center;
  gap: var(--space-md);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: rgba(10, 16, 23, .93);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--line-strong);
  transform: translateY(110%);
  transition: transform .3s var(--ease);
}
.joinbar.is-up { transform: none; }
.joinbar__price {
  margin: 0;
  font: 600 .82rem/1.3 var(--font-body);
  color: var(--muted);
  flex: 1 1 auto;
}
.joinbar__price strong {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.12rem;
  color: var(--heading);
}
.joinbar .btn { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   RESPONSIVE
   The three breakpoints are style.css's own, so this page changes shape at
   exactly the same widths as the main site.
   -------------------------------------------------------------------------- */
@media (max-width: 1200px) {
  /* two columns, not three: see the note on .asks above */
  .asks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  /* two columns at this width would put each question in about 160px */
  .asks { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1024px) {
  /* Once the two columns stack, the closing lines stop working left aligned.
     They carry the spine's 34px inset, which is right beside a spine but
     leaves them sitting 59px from the card's left edge and 25px from its
     right: the card reads as tipped over. Side by side that inset is what
     ties the verdict to the track above it; stacked there is no comparison to
     hold, so both lines centre on the card instead.
     The loop line goes back to inline flow to do it: as a flex row its text
     took the full width and centred inside itself, which left the glyph
     stranded at the far left with a gap between it and its own words. */
  .vcol__out { padding-left: 0; }
  /* Inline flow, not flex, for the same reason as the loop line. As a flex row
     the label took every pixel going, so justify-content had nothing to centre
     and the glyph stayed pinned to the pill's left edge while the words
     centred in the remainder: at 1024 that left the ink 46px from one edge of
     the card and 618px from the other. Flowing the glyph as part of the first
     line makes the two one object, and one object can be centred. */
  .vcol__verdict { display: block; text-align: center; }
  .vcol__verdict-icon {
    display: inline-block;
    vertical-align: -.22em;
    margin-inline-end: var(--space-sm);
  }
  .vcol__loop {
    display: block;
    padding-left: 0;
    text-align: center;
  }
  .vcol__loop svg {
    display: inline-block;
    vertical-align: -.18em;
    margin-block-start: 0;
    margin-inline-end: var(--space-xs);
  }

  .versus { grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
  .versus__split::before, .versus__split::after { display: none; }
  .versus__split { justify-self: center; }
  .versus__split span { width: 46px; height: 46px; font-size: .72rem; }
  .reveal-line { grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
  .pyramid__grid { gap: 0 var(--space-xl); }
  .pills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Nine cards divide into three clean rows at three columns but not at two,
     where the ninth is left alone with an empty cell beside it, directly above
     the full width community card so the hole is impossible to miss. Spanning
     it closes the gap. Written as "the second to last card, when it starts a
     row" rather than nth-child(9), so it stays correct if a card is ever added
     or removed. The measure is capped so the copy does not run the full width
     of the band. */
  .pills > .pill-card:nth-last-child(2):nth-child(odd) { grid-column: 1 / -1; }
  .pills > .pill-card:nth-last-child(2):nth-child(odd) p { max-width: 62ch; }
  .quotes { grid-auto-columns: calc((100% - var(--grid-gap) - 44px - var(--grid-gap)) / 2); }
}

@media (max-width: 860px) {
  /* Stacked at 1024 this left the badge, the figure and the founding rate line
     alone in a 958px strip with about 550px of empty card beside them. The two
     column state only asks for a 412px figure track, which 1024 has room for,
     so the stack waits until the width where it is actually needed. */
  .price { grid-template-columns: minmax(0, 1fr); }
  /* the gradient rule divides two columns; once they stack, the border
     below does the dividing and the vertical rule is a stray line down the
     right edge of the card */
  .price__figure { border-right: 0; border-bottom: 1px solid var(--line); }
  .price__figure::after { display: none; }
  /* The pyramid stops being a two-track diagram and becomes one: the shape
     first, then the four rungs beneath it. Row alignment no longer matters,
     so the rungs lose their fixed height and close up. */
  .pyramid__grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-xl); }
  .pyramid__stack { max-width: 460px; margin-inline: auto; width: 100%; }
  .rung { min-height: 0; padding-block: 14px; }

  /* A shallower pyramid. The desktop apex is 30% of the stack, which is ample
     at 600px wide and far too narrow at 300 — "Understand" was being clipped
     by its own trapezoid. Opening every tier out keeps the silhouette and the
     progression while giving the longest word room to sit inside the shape.
     Each tier's top edge still matches the one below it, so the four clips
     continue to draw one unbroken pyramid. */
  .tier--1 { clip-path: polygon(22% 0, 78% 0, 84% 100%, 16% 100%); }
  .tier--2 { clip-path: polygon(16% 0, 84% 0, 90% 100%, 10% 100%); }
  .tier--3 { clip-path: polygon(10% 0, 90% 0, 95% 100%, 5% 100%); }
  .tier--4 { clip-path: polygon(5% 0, 95% 0, 100% 100%, 0 100%); }
  .feel { grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
  .frame img { aspect-ratio: 16 / 10; }
  /* The caption drops out of the picture and sits under it, so the marks must
     stop at the picture's bottom edge too — left at inset:0 they would stretch
     over the caption and pull the region ring off centre. */
  .frame__marks { bottom: auto; aspect-ratio: 16 / 10; }
  .frame__caption { position: static; max-width: none; text-align: left; padding: var(--space-md); background: var(--surface); color: var(--text); }
}

@media (max-width: 767px) {
  /* --space-xl, not a hand tuned 36px: the scale runs 8/12/16/24/32/48 and 36
     is not a step on it. Pairs with the --space-xl below it. */
  .ahero { padding-block: calc(var(--header-stuck) + var(--space-xl)) var(--section-y); }
  /* The hero keeps the centred treatment it has from 768 up, at the client's
     request. Four rules used to pull it left on a phone and all four are gone
     rather than restated: `text-align: left` on .ahero__head and on
     .ahero__under, `margin-inline: 0` on .ahero__lede, and
     `justify-content: flex-start` on .vsl__meta. Each now falls through to its
     centred base rule, so there is one definition of the hero's alignment
     instead of two that have to be kept in step. Only the spacing overrides
     belong at this width, and they stay. */
  .ahero__head { margin-bottom: var(--space-xl); }
  /* balance, or the eyebrow drops "Academy" onto a line of its own below 414 */
  .ahero__head .eyebrow { text-wrap: balance; }
  .ahero__under { margin-top: var(--space-lg); }
  .ahero__ctas { justify-content: stretch; }
  .ahero__ctas .btn { flex: 1 1 100%; }
  .vplay__btn { width: 64px; height: 64px; }
  .vplay__btn svg { width: 24px; height: 24px; }
  /* The cropped analysis strip is only about 97px tall on a phone, so the
     standard 64px button and the corner label crowd each other inside it.
     The button steps down and the label goes: the section heading directly
     above already says what the clip is, and the button keeps its aria-label. */
  .vsl--wide .vplay__btn { width: 44px; height: 44px; }
  .vsl--wide .vplay__btn svg { width: 18px; height: 18px; }
  .vsl--wide .vplay__label { display: none; }
  .tier { min-height: 66px; }
  .creed__row { gap: var(--space-sm); }
  .price__figure, .price__body { padding: var(--space-xl) var(--space-lg); }
  .price__cta .btn { flex: 1 1 100%; }
  /* The closing pair sizes to its labels instead of spanning the column. Two
     full width bars stacked under a centred headline read as a form, not as a
     choice between two actions. Both the flex basis here and style.css's
     `.btn { width: 100% }` below 480 have to be answered, hence the explicit
     width. .closer__ctas already centres and wraps, so the buttons pair onto
     one row from about 480 up and stack centred below it. */
  .closer__ctas .btn { flex: 0 1 auto; width: auto; }
  .joinbar { display: flex; }
  /* style.css sends every .btn to width:100% below this breakpoint, which is
     right for the stacked CTAs it was written for and wrong here: the join
     bar's button sits beside the price, so at 100% it pushed itself 60px off
     the right of a 390px screen. Two classes outrank the one it sets. */
  .joinbar .btn { width: auto; }
  .joinbar__price { min-width: 0; }
  /* The bar sits over the last section, so the page needs room underneath it
     or the footer credit ends up behind the CTA. Derived from the bar rather
     than guessed: it is --btn-h tall plus its own 12px above and below, which
     is --space-lg, plus the 1px border-top. A flat 96px reserved 23px more
     than the bar occupies, so the legal line sat 51px clear of it against the
     24px of air above it, and the footer looked bottom heavy. At the bar's
     true height the line gets the footer's own --space-lg on both sides. */
  .site-footer {
    padding-bottom: calc(var(--btn-h) + var(--space-lg) + 1px + env(safe-area-inset-bottom, 0px));
  }
  .asks { gap: var(--space-md); }
  .ask { padding: var(--space-lg); }
  .pills { grid-template-columns: minmax(0, 1fr); }
  /* 86%: the card plus the 28px gap has to leave room for a slice of the next
     one, or nothing on screen says the rail scrolls. At 92% that slice fell
     past the shell edge and the peek disappeared. The six line clamp, not a
     wider card, is what keeps the height down. */
  .quotes { grid-auto-columns: 86%; }
  .pill-card { padding: var(--space-lg); }
  /* On one column the wide card has no row to span and no room to sit
     sideways, so it goes back to being an ordinary card. */
  .pill-card--wide { grid-template-columns: minmax(0, 1fr); }
  .pill-card--wide .pill-card__pill { grid-row: auto; }
  .fcol, .vcol { padding: var(--space-lg); }
  .pyramid { padding: var(--space-lg); }
  /* style.css drops these to .66rem (10.56px) below 768, which made the four
     progression dates the smallest type anywhere on the page, and smaller on
     the screens where they are hardest to read than on the desktop that shows
     them at 11.84px. Held at the base size; the four 55px tracks carry
     "12 May" at .74rem without wrapping. */
  .prog__date { font-size: .74rem; }
}

/* --------------------------------------------------------------------------
   FOOTER LINK TARGETS
   The shared footer sets these links in .93rem with no vertical padding, so
   they measure 18px tall at every width. WCAG 2.2 SC 2.5.8 Target Size
   (Minimum) is Level AA and asks for 24 by 24 CSS pixels; the inline-in-a-
   sentence exception does not apply, because these sit in a nav list rather
   than in running prose. Padding rather than a bigger font, so the footer's
   type scale is untouched. Scoped here, not in style.css, which is the main
   site's file.
   -------------------------------------------------------------------------- */
.site-footer__nav a {
  display: inline-block;
  padding-block: var(--space-xs);
}
.site-footer__nav ul { gap: var(--space-xs) var(--space-lg); }

/* The row is a space-between flex of three items, and it only stacks below
   768. Between 768 and about 1076 the three no longer fit on one line: the nav
   takes the middle and right of the row, the logo the left, and the socials
   wrap alone onto a second line with most of the footer empty beside them.
   Stacked centred is the treatment style.css already applies on phones, so it
   is carried up to the width where the row genuinely stops fitting. */
@media (max-width: 1076px) {
  .site-footer__top { flex-direction: column; align-items: center; text-align: center; }
  .site-footer__nav ul { justify-content: center; }
}

/* .site-footer a sets .93rem, which wins inside .site-footer__base at .84rem,
   so the credit link rendered 1.44px larger than the sentence containing it
   and read as a size change mid sentence. The colour shift is the link cue. */
.site-footer__credit a { font-size: inherit; }

/* balance, or at 320 "reserved." drops onto a line of its own, and at 360 so
   does "Media" */
.site-footer__base p,
.site-footer__credit { text-wrap: balance; }

/* The question is an h3 and the lede beside it is body copy, but .accordion__btn
   is clamp(.97rem, 1.18vw, 1.05rem), whose vw term does not clear the floor
   until a 1315px viewport, so from 320 to 1280 the question computed 15.52px
   against the lede's 16.32px and the heading was the smaller of the two. Only
   the size changes; the weight, line height and toggle grid are style.css's. */
.accordion__btn { font-size: clamp(1.08rem, 1.3vw, 1.25rem); }

/* style.css stacks the FAQ at 1024, which is the very failure its own comment
   says the two column layout exists to prevent: run full width, each question
   sat about 715px from the plus toggle that opens it. The two tracks still fit
   comfortably at 1024, so the stack waits for the 860 breakpoint. */
@media (min-width: 861px) and (max-width: 1024px) {
  .faq__grid { grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr); }
  .faq__lede { max-width: 42ch; }
}

/* --------------------------------------------------------------------------
   TESTIMONIALS SECTION HEAD
   The shared .section-head sets the note beside the heading from 1025 up. Here
   the note reads as a caption on the case study below it rather than a
   counterweight to the headline, so it stacks under the heading at every
   width. Same single column treatment style.css already applies below 1024,
   with the row gap it uses on a phone so the line stays tied to the heading
   rather than drifting toward the card beneath it.
   -------------------------------------------------------------------------- */
#reviews .section-head {
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  row-gap: var(--space-md);
}

/* Side by side, the figure column is the shorter of the two and the grid
   stretches it to match, so start alignment left the badge, the price and the
   founding rate line stacked at the top with the whole remainder trailing
   below them and the gradient divider running on past anything it divided.
   Centred, the slack splits evenly and the rule reads as dividing two filled
   halves. Stacked, start is still right, so this is scoped to the two column
   state only. */
@media (min-width: 861px) {
  .price__figure { align-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .joinbar { transition: none; }
  .vplay__poster, .vplay__btn { transition: none; }
}

/* Print: the diagrams are the point of the page, so they keep their shape */
@media print {
  .joinbar, .site-header { display: none !important; }
}

/* --------------------------------------------------------------------------
   NARROWEST PHONES
   At 86% a 320px screen left the review a 191px measure, about 22 characters
   a line, which reads as a column of fragments rather than a quote. 92% buys
   back roughly three characters and still shows a slice of the next card.
   -------------------------------------------------------------------------- */
@media (max-width: 360px) {
  /* Widening the card to 92% bought measure but spent the peek, which fell to
     8px. The padding is the cheaper place to find it: the card stays at 86%,
     so the slice of the next one survives, and the quote gains 16px of
     measure. */
  .quote { padding: var(--space-md); }
}
