/* ==========================================================================
   client-sections.css — the three client-landing modules added between
   How-it-works and the FAQ on /landing:

     gf-*   Find the right provider for your goals   (GoalFinder.tsx)
     tf-*   Choose how you train                     (TrainFormats.tsx)
     ip-*   Why independent providers?               (IndiePros.tsx)

   One sheet for the three because they are one commission and they are read
   as one run of the page; splitting them would hide the fact that their
   surfaces are chosen against each other rather than in isolation.

   Surface rhythm, top to bottom (band / composition / item treatment):

     How-it-works   white   2-col, left-hung        (existing)
     GoalFinder     tint    centred head, ribbon    white cards, hairline
     TrainFormats   white   centred                 open row, no surface
     IndiePros      tint    2-col split             bare list, no surface
     FAQ            white   2-col                   (existing)

   Bands alternate, and no two consecutive modules share a composition or an
   item treatment — that is what stops three new sections in a row from
   reading as three takes on the same card grid.

   Nothing here uses data-reveal: /landing runs initLandingFx(), which has no
   reveal observer (that is useReveal + trainer-modular.css, /trainer-modular
   only). Adding the attribute would be inert markup at best.

   All values read tokens.css. No hex/rgb literals.
   ========================================================================== */

/* ══ GoalFinder — centred head, then five goal cards on a drifting rail ══
   Five cards three-up leaves a hole in row two, and every way of dressing
   that hole is an apology for it. So the five run as one full-bleed ribbon
   instead: a single line reads as a list you scan sideways, which is what
   these five are, and it costs the section a third of its vertical footprint
   between two taller modules.

   Mechanism: the wrapper is the only element that clips, so nothing here can
   widen the page; the track inside it is a flex row carrying the list three
   times (GoalFinder.tsx) and its x is written per-frame by that component's
   rAF loop, wrapped modulo one copy's width. Layout here therefore stays out
   of the loop's way: no transform of our own, no scroll-snap (snapping and a
   continuous drift are contradictory instructions), and no scrollbar to hide
   — at rest the wrapper does not scroll at all, it is translated.

   Card width is fixed rather than fluid: a drifting card that also resized
   with the viewport would have nothing stable for the eye to lock onto.
   ------------------------------------------------------------------------ */
.gf {
  position: relative;
  overflow: hidden;
  /* The page keeps the vertical axis; only horizontal gestures reach the
     component, which is what lets it capture a swipe without ever costing
     the reader a scroll. */
  touch-action: pan-y;
  /* Room for the cards' hover shadow (0 8px 24px), which the clip would
     otherwise shave. */
  padding-block: var(--space-8) var(--space-20);
  /* Both edges dissolve instead of cutting a card in half, so the ribbon
     reads as continuous rather than as a grid someone cropped. A mask samples
     only the alpha channel, so `black` here means "opaque" and no colour
     token applies — the same sanctioned exception trainer-modular.css takes
     for its marquee. The fade width is a variable so the breakpoints can
     retune it without re-stating the gradient — and, more to the point, so
     that the reduced-motion rule below can switch the mask off for good
     rather than being re-armed by a later breakpoint. */
  --gf-fade: var(--space-64);
  mask-image: linear-gradient(
    90deg,
    transparent 0,
    black var(--gf-fade),
    black calc(100% - var(--gf-fade)),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0,
    black var(--gf-fade),
    black calc(100% - var(--gf-fade)),
    transparent 100%
  );
}

/* While a pointer is holding the ribbon (GoalFinder.tsx adds the class past
   an 8px threshold): no text selection dragging along with it, and a cursor
   that says the row is being held rather than offered. `grab` at rest would
   be a lie on cards that are also links, so only the held state is styled. */
.gf.is-dragging { cursor: grabbing; }
.gf.is-dragging .gf__track { user-select: none; }
.gf.is-dragging .gf__card { cursor: grabbing; }
/* The two things a mouse can pick up on its own inside a card. The component
   also cancels dragstart; this stops the ghost image being drawn at all. */
.gf__card,
.gf__icon { -webkit-user-drag: none; }

.gf__track {
  display: flex;
  width: max-content;
  gap: var(--space-24);
  /* transform is owned by the rAF loop: drift, drag and momentum are one
     number there, and the hover pause is a velocity target rather than a
     paused animation. */
  will-change: transform;
}
.gf__row {
  display: flex;
  gap: var(--space-24);
}
.gf__card { flex: 0 0 360px; }

@media (prefers-reduced-motion: reduce) {
  /* No drift and no momentum (the rAF loop bails out on the same query), so
     the loop has nothing to hide and the duplicate copies are only clutter:
     what is left is a plain five-card horizontal scroller that a finger moves
     natively — hence touch-action back to auto — and that a mouse still drags
     through scrollLeft. The start padding puts the first card on the head's
     content line, the way the container above it would.

     --gf-pad is the container's own side gutter, restated rather than read
     from --container-pad because layout.css narrows the gutter on phones by
     overriding .container's padding, not the token. */
  .gf {
    --gf-pad: var(--container-pad);
    overflow-x: auto;
    touch-action: auto;
    scrollbar-width: none;
    padding-inline: max(
        var(--gf-pad),
        calc((100% - var(--container-max)) / 2 + var(--gf-pad))
      )
      var(--gf-pad);
    /* Tabbing scrolls the wrapper itself; without this the card the browser
       brings into view would land under that start gutter. */
    scroll-padding-inline-start: max(
      var(--gf-pad),
      calc((100% - var(--container-max)) / 2 + var(--gf-pad))
    );
    mask-image: none;
    -webkit-mask-image: none;
  }
  .gf::-webkit-scrollbar { display: none; }
  .gf__track { will-change: auto; }
  .gf__row--dup { display: none; }
}

/* Head: the plain centred .block-head, keeping its own 36px bottom margin —
   it is a banner above the grid now, not a cell inside it. Nothing about the
   h2/lead type or the 60ch centred measure is overridden. */

/* Kicker: pill off, flanking hairlines on — the same eyebrow categories.css
   and why-book.css use, and the same 96px line measure, so the three marks
   read as one across the page. Centred here, so BOTH sides draw (why-book
   draws only the right because its head is left-hung). The pair share one
   declaration so a side can never drift from its opposite; `flex: 0 1 96px`
   lets both give ground symmetrically on a narrow phone, and the nowrap label
   guarantees the shortfall comes out of the lines rather than breaking the
   eyebrow onto two rows. Scoped to .gf__head: the treatments are not meant to
   coexist in one eyeline, so each section opts in for itself. */
.gf__head .kicker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-12);
  padding: 0;
  background: none;
  border-radius: 0;
  line-height: 1;
  white-space: nowrap;
}
.gf__head .kicker::before,
.gf__head .kicker::after {
  content: '';
  flex: 0 1 96px;
  height: 1px;
  align-self: center;
  background: rgba(var(--brand-rgb), .18);
}

/* Card: hairline on white over the tinted band, --radius-lg. No resting
   shadow — the band already separates the cards from the page, and a shadow
   under all five would be chrome on every item rather than emphasis on any
   — chrome only goes in where it earns its keep. Elevation is reserved for
   hover, where it means something. */
.gf__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-14);
  padding: var(--space-28);
  background: var(--bg);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  transition:
    border-color var(--dur-base) var(--ease),
    box-shadow var(--dur-base) var(--ease);
}
.gf__card:hover {
  border-color: rgba(var(--brand-rgb), .35);
  box-shadow: var(--shadow-card-hover);
}
.gf__card:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
}

/* Icon inline with the title rather than stacked above it: the stacked tile
   is the template feature card, and the inline row also buys
   the body copy the card's full width instead of starting it under a 64px
   gap. align-items: center hangs the title on the icon's optical middle,
   which holds whether the title runs to one line or two. */
.gf__top {
  display: flex;
  align-items: center;
  gap: var(--space-16);
}
.gf__icon {
  width: 64px;
  height: 64px;
  flex: 0 0 64px;
  object-fit: contain;
}
.gf__card h3 {
  margin: 0;
  font-size: var(--fs-h3-lg);
  font-weight: var(--fw-bold);
  line-height: var(--lh-card);
  letter-spacing: var(--ls-tight);
  color: var(--ink);
  text-wrap: balance;
}
.gf__card p {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--ink-3);
}

/* margin-top: auto is the point of the flex column — it pins the CTA to the
   card's floor, so all three link lines in a row sit on one baseline however
   unevenly the bodies wrap. Rendered as a <span>: the card itself is the
   anchor, so nesting a real link here would be an interactive element inside
   an interactive element. */
.gf__cta {
  margin-top: auto;
  padding-top: var(--space-6);
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--brand-ink);
}
/* Solar bold arrow, not a text glyph — same mark as the hero's provider link.
   The loader replaces the <i> placeholder with an <svg> after mount and copies
   the class across, so one selector covers both states and the box is the same
   14px before and after: the arrow arrives into a slot the CTA row already
   held, and nothing reflows. flex: none keeps it at that size whatever the
   label measures. */
.gf__cta-arrow {
  flex: none;
  width: 14px;
  height: 14px;
  transition: transform var(--dur-base) var(--ease-out);
}
.gf__card:hover .gf__cta-arrow { transform: translateX(3px); }

@media (prefers-reduced-motion: reduce) {
  .gf__cta-arrow { transition: none; }
  .gf__card:hover .gf__cta-arrow { transform: none; }
}

/* ══ TrainFormats — six formats as one open row ═══════════════════════════
   No plate, no hairlines, no cells: six icon-over-label stacks sitting
   directly on the section background. Six equal minmax(0, 1fr) columns keep
   the stacks evenly spread across the full container width whatever the
   labels measure, and align-items: start lets a two-line label grow downward
   without pushing its neighbours' icons off the shared baseline.
   ------------------------------------------------------------------------ */

/* ── Kicker: pill off, flanking hairlines on ───────────────────────────────
   kicker.css ships .kicker as a tinted pill; this section takes the
   flanking-line eyebrow the goal grid above it and the category sections
   already use, so the two heads in one eyeline carry the same mark. Scoped
   to .tf-section rather than the page, on the same principle as .cat-section:
   a .kicker keeps the pill until its own section opts out.

   The head is centred, so the lines run both sides. 96px is the measure used
   at every other opt-in, and the pair share one declaration so a side can
   never drift from its opposite; `flex: 0 1 96px` lets both give ground
   symmetrically on a narrow phone instead of breaking the label in two. */
.tf-section .kicker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-12);
  padding: 0;
  background: none;
  border-radius: 0;
  /* line-height 1 keeps the eyebrow's box the height of its own text, which
     is what `align-items: center` measures the hairlines against. */
  line-height: 1;
  /* One line only: with min-content equal to max-content, a row too narrow
     for 96+label+96 takes the shortfall out of the lines, not the label. */
  white-space: nowrap;
}
.tf-section .kicker::before,
.tf-section .kicker::after {
  content: '';
  flex: 0 1 96px;
  height: 1px;
  align-self: center;
  background: rgba(var(--brand-rgb), .18);
}

.tf-row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-32) var(--space-24);
  align-items: start;
}

.tf-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-12);
  text-align: center;
}
.tf-item__icon {
  width: 72px;
  height: 72px;
  object-fit: contain;
  transform-origin: 50% 50%;
  transition: transform var(--dur-base) var(--ease-out);
}
/* Card-title step, not body: the label is the item's whole content, so it
   carries the weight a heading would. */
.tf-item__label {
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: var(--lh-card);
  letter-spacing: var(--ls-tight);
  color: var(--ink);
  transition: color var(--dur-base) var(--ease-out);
}

/* Hover: the icon lifts a step, the label warms to brand ink. The item is a
   legend entry, not a control — nothing here navigates, so the cursor stays
   the default arrow and the feedback stays a nudge rather than an
   affordance. Only transform and color move: no width, height or font-size
   changes, so the grid track never re-measures and the five neighbours hold
   still. Scaling the label as well was tried and dropped — 1.02 on bold text
   reads as a reflow blur next to the crisp icon, while the colour step
   registers at a glance and survives at any zoom. */
.tf-item:hover .tf-item__icon { transform: scale(1.07); }
.tf-item:hover .tf-item__label { color: var(--brand-ink); }

@media (prefers-reduced-motion: reduce) {
  .tf-item__icon,
  .tf-item__label { transition: none; }
  .tf-item:hover .tf-item__icon { transform: none; }
}

/* Plain centred line under the row — no tint, no frame. Body-size and
   --ink-3: it is a note about the six formats, not a seventh format. The
   measure is capped so it stays a single balanced sentence under a wide row
   instead of one long ruler-width line. */
.tf-note {
  max-width: 56ch;
  margin: var(--space-32) auto 0;
  text-align: center;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--ink-3);
}

/* ══ IndiePros — full-bleed dark photo band ═══════════════════════════════
   Markup: <section class="block ip-band on-dark">
             <img class="ip-band__bg">
             <div class="container"><div class="ip">…

   Layers (back → front), the same recipe the provider CTA used to carry:
     1. <img.ip-band__bg>   the photograph
     2. ::before            dark, brand-tinted gradient — carries the contrast
     3. ::after             noise.svg — kills gradient banding on a wide band
     4. .container          content

   Padding stays on `section.block` (88/56) rather than being restated here:
   the band is a normal section that happens to be dark, not a hero, and the
   page's vertical rhythm should not break just because the surface did.
   `.alt` is gone — the tint it painted is what the photograph replaced.
   ------------------------------------------------------------------------ */
.ip-band {
  position: relative;
  isolation: isolate;   /* keeps the z-index:-3…-1 stack inside the section */
  overflow: hidden;
  background: var(--bg-dark);  /* fallback if the photo fails to load */
  color: white;

  /* Lightened azure for the checklist glyphs. --brand (#00a7e5) lands around
     3.8:1 on this gradient — legal for a non-text glyph, but visibly muddy,
     and it is the one accent on the band. --brand-soft (#d6f1fb) goes the
     other way and reads as white. This sits between them: still recognisably
     the brand azure, ~11:1 against the darkest stop.

     Retuned one step lighter (was #6cccf0) now that the glyphs render
     bold-duotone. Duotone puts the silhouette in a 50%-opacity layer, and on
     a dark ground a 50% wash loses far more than it does on white: at #6cccf0
     that layer fell to ~2.6:1 in the light corner of the gradient and the
     shape started dropping out, leaving only the full-strength detail. At
     #8ad8f4 the wash holds ~2.8–3.7:1 across the ramp — readable as a form,
     still clearly below the detail layer, and not the white --brand-soft
     turns into here. */
  --ip-accent: #8ad8f4;
}

/* object-position biases left-of-centre: the coach is the subject and the
   right third of the frame is background the checklist column covers anyway. */
.ip-band__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 35% 45%;
  z-index: -3;
  pointer-events: none;
}

/* Two stacked gradients, not one, and the split is load-bearing.

   The obvious version — a single ramp from bg-dark into rgba(brand-ink, .74)
   — does not survive the contrast check. --brand-ink (#0086b8) is a mid-tone
   azure: white on it is 4.13:1 even at full opacity, so once the ramp reaches
   the brand end the checklist is under 4.5:1 no matter how bright or dark the
   photograph behind it happens to be. Alpha cannot fix that, because the
   photo showing through only lightens the result.

   So the two jobs are separated. The lower layer is the brand tint, doing
   hue and nothing else, fading in from 35% so the left third stays neutral.
   The upper layer is a pure bg-dark scrim that never drops below .78, which
   is what actually guarantees the text: the worst case on this band — the
   sunlit gap between the trees, under the lightest stop, through the tint —
   still lands near 11:1 for white.

   Diagonal at 105deg so the ramp runs against the split composition: densest
   at the top left under the h2, lightest under the far end of the checklist,
   which is the corner of the frame with the least going on in it. */
.ip-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    linear-gradient(
      105deg,
      rgba(var(--bg-dark-rgb), .93) 0%,
      rgba(var(--bg-dark-rgb), .84) 52%,
      rgba(var(--bg-dark-rgb), .78) 100%
    ),
    linear-gradient(
      105deg,
      rgba(var(--brand-ink-rgb), 0) 35%,
      rgba(var(--brand-ink-rgb), .34) 100%
    );
  pointer-events: none;
}

/* Film grain — a two-stop gradient across a 1440px band bands visibly on
   8-bit displays; the noise breaks it up and matches hero/trainer-cta. */
.ip-band::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: url('../../noise.svg');
  background-repeat: repeat;
  background-size: 200px 200px;
  opacity: .32;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* ── Head on dark ─────────────────────────────────────────────────────────
   kicker.css draws a brand-tint pill for light surfaces; at 6% alpha it is
   invisible here, so the band takes the eyebrow-plus-hairline mark the other
   dark surfaces use (closing-cta, stop-band). One trailing rule only — the
   head is left-aligned, and a leading rule would hang off the container edge
   with nothing to balance it. */
.ip-band .kicker {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-12);
  background: none;
  border-radius: 0;
  padding: 0;
  color: rgba(var(--white-rgb), .82);
  line-height: 1;
  white-space: nowrap;
}
.ip-band .kicker::after {
  content: '';
  /* 96px is the site-wide line measure — geometry, not spacing. Shrinkable so
     a narrow column takes the shortfall out of the rule, not the label. */
  flex: 0 1 96px;
  height: 1px;
  align-self: center;
  background: rgba(var(--white-rgb), .28);
}

.ip-band .block-head h2 { color: white; }
/* The lead is a colon-terminated hand-off into the list, not body copy it
   needs to compete with — .82 keeps it subordinate and still clears 4.5:1. */
.ip-band .block-head p { color: rgba(var(--white-rgb), .82); }

/* ── Split: head left, checklist right, closer under the list ─────────────
   Roughly 45/55: the list column takes the larger share because it holds six
   items in two sub-columns, while the head is three short blocks. Column gap
   is deliberately large (72px) — it is the only thing separating a heading
   from a list on the same band, with no rule or surface between them.
   ------------------------------------------------------------------------ */
.ip {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: var(--space-48) var(--space-72);
  align-items: start;
}
.ip__head { margin-bottom: 0; }

/* List + closer as one column so the closer's rule is always 32px under the
   last claim. As two separate grid cells the closer would be pushed into row
   two, i.e. under the taller of the two columns — normally the head, leaving
   the rule floating in space below the list it belongs to. */
.ip__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-32);
  align-items: stretch;
}

.ip__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-28) var(--space-32);
}

/* Glyph and text on one row, top-aligned rather than centred: the longest
   claim wraps to two lines in a half column, and centring would then drop its
   glyph to the middle of the block instead of holding the first line. */
.ip__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-14);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-card);
  letter-spacing: var(--ls-tight);
  /* Full white, not a .9 wash: these six lines are the section's argument and
     they sit over a photograph, where any alpha reads as soft focus. */
  color: white;
}

/* Written against `svg.` and `[data-lucide].` rather than the bare class:
   base.css sizes every icon with `svg.lucide` (0,1,1), which outranks a lone
   class and would clamp this back to 20px the moment the loader swaps the
   <i> for an inline SVG. Same idiom as trainer-modular.css.

   Fixed box so the post-mount swap cannot reflow the list, and 2px of top
   margin to sit the 24px glyph on the cap height of the 16px line beside it
   rather than on its ascent. */
svg.ip__icon,
[data-lucide].ip__icon {
  display: block;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  margin-top: var(--space-2);
  color: var(--ip-accent);
}

/* Measured to the list, not to the section: the closer is the list's last
   line, so it sits in the list column over a hairline the width of the list.
   Bold at the h4 step — one notch above the claims it closes, well below the
   h2 it must not compete with. */
.ip__closer {
  margin: 0;
  padding-top: var(--space-28);
  /* --line-strong is a light-surface hairline and vanishes on the band; .28
     white is the same weight the kicker rule and closing-cta use on dark. */
  border-top: 1px solid rgba(var(--white-rgb), .28);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  line-height: var(--lh-card);
  letter-spacing: var(--ls-tight);
  color: white;
}

/* ── Responsive ───────────────────────────────────────────────────────────
   Two breakpoints, matching the landing's existing 980 / 720 pair.
   ------------------------------------------------------------------------ */
@media (max-width: 980px) {
  /* Same ribbon, one step narrower: 360px cards on a tablet leave barely two
     on screen, which reads as a slideshow rather than as a list. */
  .gf__card { flex-basis: 320px; }

  /* One row of six → 3 + 3. Six stacks across a tablet column would squeeze
     each label under 110px and wrap every one of them; three-up keeps the
     labels on one line and the two rows still read as one block. */
  .tf-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tf-item__icon { width: 64px; height: 64px; }

  /* The split collapses: head above, body below. The list keeps its two
     sub-columns here — at ~700px of content width a single column of six
     short claims would run half-empty. */
  .ip {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-36);
  }
}

@media (max-width: 720px) {
  /* The ribbon is the mobile layout too — a swipe is the native gesture here,
     and stacking five full-width cards would cost a screen and a half of
     scrolling. 300px shows one card and a clear slice of the next at 360px.
     The gaps and the mask's edge fade both tighten with the container's own
     24 → 16 step; a 64px fade a side would eat a third of a phone screen. */
  .gf {
    /* Matches .container's phone gutter (layout.css) — only consulted in the
       reduced-motion branch, where the ribbon becomes a real scroller. */
    --gf-pad: var(--space-16);
    --gf-fade: var(--space-20);
  }
  .gf__track,
  .gf__row { gap: var(--space-16); }
  .gf__card { flex-basis: 300px; padding: var(--space-24); }
  /* The eyebrow's flanking lines give way before the label does, but on a
     328px column 96px a side still crowds it — halve them. */
  .gf__head .kicker::before,
  .gf__head .kicker::after { flex-basis: 48px; }
  /* The icon steps down with the card's padding so the title keeps a usable
     measure on a 328px content column. */
  .gf__icon { width: 56px; height: 56px; flex-basis: 56px; }

  /* 2 x 3. Two columns is the floor — six formats stacked one-up is a scroll,
     not a menu, and the labels are short enough to hold a half column at
     360px. A horizontal scroller was the alternative and is worse here: it
     hides two of the six behind a swipe nobody is prompted to make. */
  .tf-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-28) var(--space-16);
  }
  .tf-item__icon { width: 56px; height: 56px; }
  .tf-note { margin-top: var(--space-28); }
  /* Same halving the goal-grid eyebrow takes: 96px a side crowds the label on
     a 328px content column. */
  .tf-section .kicker::before,
  .tf-section .kicker::after { flex-basis: 48px; }

  /* One claim per row: at a half column of 156px the longest item
     ("Greater variety than a single gym or studio") wraps to four lines. */
  .ip__list {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-20);
  }
  .ip { gap: var(--space-28); }
  .ip__closer {
    padding-top: var(--space-20);
    font-size: var(--fs-h3-lg);
  }
}
