/* cases — Кейсы · desktop 3001:145 (1440 x 863.7) / mobile 3001:3856 (335 x 697.53)
   Written by SECTION_BUILD. Scoped rules ONLY: .cases__* plus scoped overrides of the
   already-linked template classes (css/templates/slider.css). No global selector, no :root,
   no .container, no template stylesheet copy or link.

   Mobile-first: base = 375, then @media 768 (PSEUDO-tablet, interpolated — has-tablet-layer
   is false) then @media 1024 (drawn desktop layer, --vw-base 1520).

   Section-scoped tokens live on .cases, NOT on :root (the page owns :root), and every one is
   declared in ALL THREE bands — brief-page.md 4.1a rule 2, the ragazzi-pdp2 defect where 2 of
   51 scoped tokens were missing from exactly the 768-1023 block.

   Deliberately static, so nobody hunts for a missing band (slice 2.4): the 20px card gap
   (= page --grid-gap, fixed on both layers), 10px media->body, 10px did-label->body, 14px did
   stack, 8px chip padding, 7/5px CTA padding, 2px CTA border, 28px strip height, 10px nav gap,
   6px nav-glyph inset, 5/6px metric-tile gap, 20px mobile head gap, 25px mobile lead-wrap gap,
   27px desktop head min-gap. Each is identical on both layers or differs by <= 1px. */

/* ============================================================================
   MOBILE base (0-767) — no @media
============================================================================ */

.cases {
  /* scoped fluid ramps · M-min = round(M x 320/375), M-max = round(M x 767/375) */
  --cases-stack-gap: clamp(17px, calc(20px + (var(--max-vw) - 375px) * 0.015), 41px);
  --cases-chip-gap: clamp(9px, calc(10px + (var(--max-vw) - 375px) * 0.015), 20px);
  --cases-body-gap: clamp(13px, calc(15px + (var(--max-vw) - 375px) * 0.015), 31px);

  /* the section frame itself: vertical stack, gap 40 desktop / 20 mobile, padding 0 both
     layers, no fill. head -> nav strip -> card row. .container (base.css) owns the gutter;
     nothing here may add a padding shorthand. */
  display: flex;
  flex-direction: column;
  gap: var(--cases-stack-gap);
}

/* ---- head — 3001:146 / 3001:3857 --------------------------------------- */

.cases__head {
  display: flex;
  flex-direction: column;
  gap: 20px;                          /* 3001:3857 gap 20 */
}

/* Frame 20 (3001:3858, 335 x 25) and Frame 1321320706 (3001:3860, 335 x 57) are REAL
   single-child Figma frames — emitted as real elements (slice 5.1). Their gaps are inert with
   one child but are shipped as drawn. ragazzi-community cost 20 of 39 gate P0/P1 by omitting
   exactly this class of wrapper; the fix there was a MORE faithful DOM, not a leaner one. */
.cases__head-row {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 20px;
}

.cases__lead-wrap {
  display: flex;
  flex-direction: column;
  gap: 25px;
}

/* .cases__title uses .t-h2 verbatim: it already carries the per-band weight (600 -> 700),
   line-height (0.88 -> 1), tracking (-4% -> -6.25%), UPPER and --c-heading (#232323 -> #252627).
   No override is needed on either layer, and the token is NOT forked. */

/* .t-lead carries family/weight/size/colour. This lead is drawn at 120%/-3%, not the token
   row's 115%/-5%, so those two properties are set ON THE ELEMENT (slice header rule). */
.cases__lead {
  line-height: 1.2;
  letter-spacing: -0.03em;
}

/* ---- nav strip — 3001:149 / 3001:3862 ---------------------------------------
   In FLOW, ABOVE the card row on both layers (D-8) — not the template's absolute overlay
   centred on the slides. Geometry closes exactly: 28 + 10 + rule + 10 + 28 = 1440 / 335,
   so the rule is flex:1 and 76px is the only constant. */

.cases__nav {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  height: 28px;
}

/* Every value here replaces a template demo default: .carousel-btn ships a 44x44 pink
   border-radius:50% pill absolutely positioned at top:50%. Figma draws a square 28x28 box,
   no fill, radius 0 (brief 4.4a: everything 0). position:static also makes the template's
   `.prev { left: 30px }` / `.next { right: 30px }` inert. */
.cases__nav .carousel-btn {
  position: static;
  transform: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding-inline: 6px;                /* 6 + 16 + 6 = 28 — the outer box is the hit target */
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--c-ink);                /* #232323 — the glyph paints currentColor */
  cursor: pointer;
}

/* No hover fill is drawn anywhere on this page; components.css owns the opacity norm. */
.cases__nav .carousel-btn:hover {
  background: none;
}

/* BA-2 · a disabled arrow stays VISIBLE and mutes to the page's own state colour.
   This section deliberately does NOT carry the template's unguarded
   `.carousel-controls .carousel-btn:disabled { display: none }` (FD-86): at 1024+ BOTH arrows
   are disabled because slidesPerView(3) === totalCards(3), so display:none would delete the
   entire desktop strip Figma draws at full ink — and it would hide `prev` at scrollLeft 0 on
   mobile, where the strip is half of the section's only affordance. The class is not used in
   the markup at all, so the rule cannot match; this declaration is the belt to that braces. */
.cases__nav .carousel-btn:disabled {
  display: flex;
  cursor: default;
}

/* Ink on a disabled arrow: FULL `#232323`, as drawn — corrected by the driver at integration.
   BA-2's muted default was a reasonable guess (Figma draws no disabled variant to copy), but two
   facts settled it against the guess. Fidelity: at 1024+ BOTH arrows are disabled here, because
   slidesPerView(3) === totalCards(3), so the muted colour covered 100% of a strip Figma draws at
   full ink. Consistency: `results` is the same shape — three cards, no desktop scroll — and it
   ships the arrows at full ink, so the two sliders on one page disagreed with each other.
   The remaining question is a DESIGN one, not a build one, and it is flagged in the run report:
   on desktop these arrows look enabled and have nothing to scroll. */
.cases__nav .carousel-btn:disabled { color: var(--c-ink); }

.cases__nav-glyph {
  display: block;
  flex: 0 0 auto;
  width: 16px;
  height: 28px;
}

/* 1px #232323 CENTER stroke on a zero-height line: height 0 + border-top, vertically centred
   by the row. #232323 here and #000000 on the did rules / the CTA are genuinely different
   inks in one section (D-25) — the gate compares exact hex, so neither is unified. */
.cases__nav-rule {
  flex: 1 1 auto;
  align-self: center;
  height: 0;
  border-top: 1px solid var(--c-ink);
}

/* ---- track — 3001:155 / 3001:3868 -------------------------------------------
   Overrides the template's demo `.slides-blocks { margin-top: 20px; padding: 0 10px;
   gap: 16px }`. NO gutter bleed and NO peek: Figma clips the mobile row at exactly 335 (card 2
   starts at x=355, 20px past the viewport), so the track is padding 0 / width = the content
   box. No negative margins, no scroll-padding, no width: calc(100% + 40px) (D-11).
   The container's width must NOT be capped by its content: at 375 this gives
   scrollWidth 1045 > clientWidth 335, maxScroll 710, step 355. */

.cases__track {
  display: flex;
  flex-direction: row;
  align-items: stretch;               /* Figma alignItems: MAX, and flex-end reproduced it EXACTLY
                                         as long as D-13/D-15 made all three cards equal height —
                                         which they do only at 1024+. Below it the shipped desktop
                                         copy runs 5 lines in the 4-line did-body reserve, in card
                                         2 (768) and cards 2+3 (375): measured 544.17 against
                                         527.38, and with flex-end the SHORT card also dropped its
                                         photo 16.79px, so the media jumped between slides.
                                         `stretch` makes the row's height common to all three; the
                                         surplus is routed into .cases__did-body (the one box Figma
                                         draws at a FIXED height with the text vertically CENTRED),
                                         so the photos line up at the top and the CTAs at the
                                         bottom exactly as drawn.
                                         D-19's warning was that stretch would stretch
                                         .cases__media — MEASURED false here: the media keeps
                                         `flex: 0 0 auto` + aspect-ratio, so it cannot grow on the
                                         card's main axis (208.02px at 375, identical before and
                                         after). At 1024+ every card is already equal, so stretch
                                         is a no-op there — verified at 1024 and 1520. */
  gap: var(--grid-gap);               /* 20 on BOTH layers, page token, deliberately unclamped */
  width: 100%;
  padding: 0;
  margin-top: 0;
  scroll-snap-type: x mandatory;      /* KEEP mandatory snap (brief 8.2a) */
  scroll-behavior: auto;              /* auto, not smooth: the JS passes behavior:'smooth' per
                                         call; CSS smooth is what makes buttons read dead */
}

/* D-12 / FD-72 · slider.js toggles .is-dragging to switch snap OFF for the duration of a drag.
   A same-specificity section override that loads after the template can silently ORPHAN that
   state variant, and the drag then re-snaps on every pointermove (jerky, no error anywhere).
   Re-stated here at (0,2,0) so it outranks anything above. */
.cases__track.is-dragging {
  scroll-snap-type: none;
  scroll-behavior: auto;
}

/* ---- card — 3001:156 / 3001:181 / 3001:206 (mobile 3869 / 3894 / 3919) ------
   Replaces the template's demo `width: 281px; height: 400px; border-radius: 20px/12px;
   overflow: hidden`, including its @media (min-width: 1440px) 425x637 block — 1440 is not one
   of this page's breakpoints, and a (0,2,0)/(0,1,0)-later selector is the only way to neutralise
   a rule inside a stylesheet this section must not edit.
   min-width: 0 is load-bearing, not defensive: a flex item's automatic minimum size would clamp
   the card up to its min-content width, and card 3's metric row is WIDER than the card. */
.cases__card {
  flex: 0 0 100%;                     /* V = 1 · Figma mobile: card = container = 335 */
  min-width: 0;
  width: auto;
  height: auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;               /* D-24: the CENTER/MIN drift across the three cards is
                                         invisible (every child is full-width) — normalised */
  gap: 10px;                          /* media -> body, both layers */
  border-radius: 0;
  overflow: visible;                  /* D-16: card 3's desktop metric row overruns 4px into the
                                         20px column gap and Figma does not clip it */
  scroll-snap-align: start;
}

/* MUST-APPLY (D-10). At 768-1023, V = 2 and slide 3's `start` snap point is 748 against a
   maxScroll of 374 — mandatory snap then drags every attempt back to slide 2 and card 3 can
   NEVER be seen. `end` makes the track's own end a snap point (1102 - 728 = 374 = maxScroll).
   Free at 375, where the slide's start and end points are both 710. */
.cases__card:last-child {
  scroll-snap-align: end;
}

/* ---- media — one aspect-ratio for all three (D-22) --------------------------
   Figma draws 290.00 / 290.08 / 290.70 at 467 wide — a <= 0.24% spread from the row's
   bottom-alignment drift, not intent. One value is what makes the cards exactly equal height. */
.cases__media {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  aspect-ratio: 467 / 290;
  overflow: hidden;
}

/* D-23 · all three fills are scaleMode: CROP with an explicit imageTransform. `object-fit:
   cover` ALONE cannot reproduce that. widthPct/heightPct/offsetXPct/offsetYPct come straight
   from the export and were confirmed digit-for-digit by Figma's own codegen; `cover` (not the
   default `fill`) is the one place its output is improved on — it absorbs card 1's 0.25%
   aspect mismatch by cropping instead of skewing.
   max-width: none is REQUIRED: base.css caps every img at max-width: 100%, which would clip
   card 3's 142% to 100% and silently destroy the crop. */
.cases__media img {
  position: absolute;
  max-width: none;
  object-fit: cover;
}

.cases__card:nth-child(1) .cases__media img {   /* crop 103.37 / 109.00 / 1.68 / 4.50 */
  width: 103.37%;
  height: 109%;
  left: -1.68%;
  top: -4.5%;
}

.cases__card:nth-child(2) .cases__media img {   /* crop 100.00 / 106.94 / 0.00 / 3.47 */
  width: 100%;
  height: 106.94%;
  left: 0;
  top: -3.47%;
}

.cases__card:nth-child(3) .cases__media img {   /* crop 142.00 / 114.06 / 42.00 / 9.37 */
  width: 142%;
  height: 114.06%;
  left: -42%;
  top: -9.37%;
}

/* ---- card body — 3001:158 / 3001:3871 ---------------------------------- */

/* flex: 1 1 auto here, on .cases__content, .cases__did, .cases__did-inner and
   .cases__did-body is ONE mechanism, not five rules: it is the chain that carries the row's
   surplus height from the stretched card down to the single box that is allowed to absorb it.
   Every link is `auto` basis, so nothing changes when there is no surplus (1024+, and card 2/3
   below it, which SET the height) — the boxes still hug their content exactly as measured. */
.cases__body {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--cases-chip-gap);         /* 20 desktop / 10 mobile */
  flex: 1 1 auto;
}

/* brand chip — 3001:159 / 3001:3872. DOM relationship: CHILD, in normal flow, full card width
   (D-18). Not a sibling, not an abs-overlay, and NOT a pill: radius 0. */
.cases__brand {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: flex-start;
  gap: 10px;
  padding: 8px;                       /* allowed: a chip, not a .container section */
  background: var(--c-ink);
}

/* --text-label carries the SIZE only (16 / 20). All six chip nodes export letterSpacing 0%
   while the 4.3 --text-label row documents -8% / -6.4%, so tracking is set to 0 ON THIS
   ELEMENT — the page token is not forked. */
.cases__brand-name {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-label);
  line-height: var(--lh-label);       /* 1 · 8 + 20 + 8 = 36 desktop, 8 + 16 + 8 = 32 mobile */
  letter-spacing: 0;
  text-transform: uppercase;
  color: #ffffff;
}

/* content — 3001:161 / 3001:3874. Figma alignItems MAX on this vertical frame right-aligns its
   children; metrics and did are full width, so only the CTA is visibly affected — expressed as
   align-self on the CTA rather than as a cross-axis flip that would shrink the metric row and
   break its percentage gap. */
.cases__content {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--cases-body-gap);          /* 20 desktop / 15 mobile */
  flex: 1 1 auto;
}

/* ---- metric row — 3001:162 / 3001:3875 --------------------------------------
   D-17. 11.94% of 335 is 40.00px EXACTLY at the drawn width, and flex-shrink only engages
   below it. A fixed 40px would break under 375: card 3's row has ZERO horizontal slack at 375
   (85 + 40 + 85 + 40 + 85 = 335), and at 320 three hugged tiles + 80px of gap overflow the
   slide by ~40px — which, because the slide is flex: 0 0 100%, lands ON TOP of the next slide.
   Nothing else in the pipeline measures 320. */
.cases__metrics {
  display: flex;
  flex-direction: row;
  align-items: flex-start;             /* D-24 · CENTER/MIN drift normalised */
  gap: 11.94%;

  /* D-16, made compatible with the desktop arrow contract — MEASURED, both halves.
     Card 3's metric row genuinely overruns its card (Figma 471 in 467; here 1.28px at 1520,
     9.6px at 1024 where --text-small sits on its 11px floor) and Figma does not clip it. But
     an overrun inside a scroll container is also SCROLLABLE OVERFLOW: with plain `visible` the
     desktop track measured scrollWidth 1441 vs clientWidth 1440 (and 1006 vs 970 at 1024), so
     `next` reported "there is more" on a row that must not scroll at all.
     `overflow-x: clip` + a 20px clip margin (= the column gap) resolves both: the bleed is
     still PAINTED — tile 3's right edge measured identical at 1006.50 / 1481.25 px before and
     after — while the track measured exactly scrollWidth === clientWidth and BOTH arrows
     disabled. Nothing is ever clipped: the largest overrun in any band is 9.6px < 20px.
     The card itself stays `overflow: visible`. */
  overflow-x: clip;
  overflow-clip-margin: 20px;
}

.cases__metric {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 0 1 auto;                      /* may shrink; the label re-wraps */
  min-width: 0;
}

/* Exact match to the 4.3 --text-stat row — size, weight, line-height and BOTH letter-spacings
   (-5.56% mobile / -4.17% desktop). No override anywhere. `nowrap` is the drawn behaviour
   (slice 5.3b), not an invented fix: the label wraps, the number never does. */
.cases__metric-val {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-stat);
  line-height: var(--lh-stat);
  letter-spacing: var(--ls-stat);
  color: var(--c-ink);
  white-space: nowrap;
}

/* --text-small carries the SIZE only (14 / 16). The 4.3 row documents Onest 400 / 120% / -3% /
   UPPER; all 18 label nodes draw 110% / 0% / as-typed, so those are element-level.
   min-height is D-15, and it is structural on purpose: four mobile labels carry a trailing or
   mid `\n` that HTML collapses, and every mobile metric row is 78 tall because of it. Shipping
   the newline as <br> would false-delta the gate (<br> contributes nothing to textContent,
   FD-79 class); reserving 2 lines restores the 78px row and the 527.53 card instead.
   2.2 x 14 = 30.8 ~ Figma 30. The reserve stays through 768-1023, where labels still wrap. */
.cases__metric-label {
  font-weight: 400;
  font-size: var(--text-small);
  line-height: 1.1;
  letter-spacing: 0;
  color: var(--c-ink);
  min-height: 2.2em;
}

/* ---- "Что сделали" block — 3001:172 / 3001:3885 -----------------------------
   D-14 MUST-APPLY: the gap is 14px, NOT Figma's 15. Line 36/37 are zero-height CENTER-aligned
   1px strokes, so in Figma they contribute no height (0 + 15 + 111 + 15 + 0 = 141). A naive
   `height: 0 + border-top: 1px` with gap 15 gives 1 + 15 + 111 + 15 + 1 = 143 — a silent +2px
   on EVERY card that no single measurement flags but the section total (863.7 / 697.53) does.
   With 14: 1 + 14 + 111 + 14 + 1 = 141 and the inner lands at 15...126, exactly Figma's. */
.cases__did {
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 1 1 auto;
}

.cases__rule {
  flex: 0 0 auto;
  width: 100%;
  height: 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--c-hairline);   /* #000000, NOT --c-ink (D-25) */
}

.cases__did-inner {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
}

/* --text-h5 size; all six nodes export letterSpacing 0% against the row's -3% -> element-level. */
.cases__did-label {
  font-family: var(--font-body);
  font-weight: 800;
  font-size: var(--text-h5);
  line-height: var(--lh-h5);          /* 1.2 · 20 x 1.2 = 24 desktop, 16 x 1.2 = 19 mobile */
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--c-ink);
}

/* D-13 MUST-APPLY · the box is FOUR lines on both layers with the text vertically CENTRED
   (every body node is textAlignVertical: CENTER in a fixed-height box: 77 = 4 x 16 x 1.2
   desktop, 68 = 4 x 14 x 1.2 mobile) while the content renders 2, 3 or 4 lines. A <p> that
   grows to its own content makes card 1 shorter than card 3, moves the bottom rule and the CTA,
   and breaks the bottom-aligned row. 4.8em is 4 lines at line-height 1.2. */
.cases__did-body {
  font-weight: 400;
  font-size: var(--text-small);
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--c-ink);
  min-height: 4.8em;
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1 1 auto;                     /* the end of the chain — the surplus lands HERE, and
                                         justify-content: center is already Figma's own
                                         textAlignVertical: CENTER, so a taller box re-centres the
                                         copy instead of pushing the rule and the CTA down. */
}

/* ---- CTA — 3001:178 / 3001:3891 --------------------------------------------
   A text link with a 2px bottom rule. Not a pill, not a button, no fill, radius 0.
   The stroke is strokeWidths { top 0, right 0, bottom 2, left 0 } with strokeAlignment INSIDE,
   and an INSIDE stroke does NOT add to padding (brief 4.4a trap 2): padding-bottom is 7 - 2 = 5.
   7 + 26 + 5 + 2 = 40 desktop · 7 + 18 + 5 + 2 = 32 mobile. The box HUGS — 126/91 are not
   hard-coded. Right-aligned via align-self (the parent's Figma alignItems: MAX).
   .t-btn brings --lh-btn, which is `normal`: this CTA is the section's ONE Figma AUTO leading
   (26/20 = 1.30 desktop, 18/14 = 1.286 mobile, FD-175). `line-height: 1` would shorten it by
   6px/4px and break the box arithmetic above. Colour is #000000 = --c-hairline, not --c-ink. */
.cases__more {
  align-self: flex-end;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 7px 0 5px;
  border-bottom: 2px solid var(--c-hairline);
  color: var(--c-hairline);
}

/* ============================================================================
   PSEUDO-TABLET 768-1023 — INTERPOLATED, nothing is drawn here.
   MANDATORY: every scoped token above is re-declared, and this is the band where every real
   defect of ragazzi-mission and ragazzi-pdp2 lived because no stage measures it.
============================================================================ */

@media (min-width: 768px) {
  .cases {
    --cases-stack-gap: clamp(20px, calc(20px + (var(--max-vw) - 768px) * 0.025), 40px);
    --cases-chip-gap: clamp(10px, calc(10px + (var(--max-vw) - 768px) * 0.025), 20px);
    --cases-body-gap: clamp(15px, calc(15px + (var(--max-vw) - 768px) * 0.025), 20px);
  }

  /* D-21 · the head goes 2-up at 768, not 1024: it is a text-beside-text pair with no image and
     no aspect-ratio to explode, which brief-page.md 7.4 lets drop to one column at 767.
     The 27px gap is a non-binding min-gap guard (actual slack at 1520 is 463); the lead is a
     PERCENTAGE of the content box (712/1440), so it survives the 1024 seam and the --max-vw cap
     with no second set of numbers. No min-height: the 96px row height IS the h2's line box. */
  .cases__head {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    gap: 27px;
  }

  .cases__head-row {
    flex: 0 1 auto;
    align-items: center;
  }

  .cases__lead-wrap {
    flex: 0 0 49.444%;
  }

  /* V = 2 · interpolation between the two DRAWN layers using only drawn numbers: the card is
     capped by the desktop width rather than stretched to the mobile 100%. A 1-up carried to
     1023 would render a 983px card with a 610px photo; a 3-up carried down to 768 would render
     a 229px card that cannot hold three 40px-gapped metric tiles. */
  .cases__card {
    flex: 0 0 calc((100% - 20px) / 2);
  }

  /* Back to the drawn fixed gap once there is room for it. The tiles stay SHRINKABLE through
     this whole band — measured, not assumed: with `flex: 0 0 auto` at 768 the labels stop
     wrapping, card 3's row hugs to 415 inside a 354 card and overruns it by 61px, which lands
     in the scroll container as 61px of phantom extent (scrollWidth 1163 instead of 1102) and
     leaves `next` live after the last card. Slice 5.4 states the label 2-line reserve holds
     "through 768-1023, where the labels still wrap at that card width" — wrapping is exactly
     what shrinkable tiles buy. Hugging starts at 1024, with the desktop 1-line labels. */
  .cases__metrics {
    gap: 40px;
  }
}

/* ============================================================================
   DESKTOP 1024+ — the drawn layer, proportional to --vw-base: 1520px
============================================================================ */

@media (min-width: 1024px) {
  .cases {
    --cases-stack-gap: clamp(27px, calc(40px * (var(--max-vw) / var(--vw-base))), 51px);
    --cases-chip-gap: clamp(13px, calc(20px * (var(--max-vw) / var(--vw-base))), 25px);
    --cases-body-gap: clamp(13px, calc(20px * (var(--max-vw) / var(--vw-base))), 25px);

    /* pricing -> cases is a DRAWN 76, one of the five non-120 desktop boundaries (brief 4.5).
       base.css gives every boundary the 120 default via `section + section`; this replaces that
       ONE property at class specificity so an override cannot add to it. Mobile is untouched —
       all 10 mobile boundaries are the page's single 50px auto-layout gap.
       D-min = round(76 x 1024/1520) = 51 · D-base = 76 · D-max = round(76 x 1920/1520) = 96 */
    margin-block-start: clamp(51px, calc(76px * (var(--max-vw) / var(--vw-base))), 96px);
  }

  /* V = 3 · 3 x 466.67 + 2 x 20 = 1440 exactly = the content box, so scrollWidth === clientWidth
     and slider.js disables BOTH arrows at load (slidesPerView === totalCards). Not a bug, and
     the strip must stay visible — see the :disabled rule above. */
  .cases__card {
    flex: 0 0 calc((100% - 40px) / 3);
  }

  /* The 40px tile gap is the value drawn at 1520 — inside the desktop band it must scale with
     the band like every other distance, or it becomes a flat px in a band that shrinks around
     it (the `--lh-btn-shop` class of defect: +35% at 1024). Measured with a flat 40: card 3's
     row overran its 310px card by 35.6px at 1024 — 12% of the card, against 0.3% at 1520.
     With the ramp: 27px at 1024 (overrun 9.6px), exactly 40px at 1520 (Figma's drawn value),
     50.5px at 1920. D-min = round(40 x 1024/1520) = 27 · D-max = round(40 x 1920/1520) = 51. */
  .cases__metrics {
    gap: clamp(27px, calc(40px * (var(--max-vw) / var(--vw-base))), 51px);
  }

  /* Tiles hug their content from 1024 up — this is what reproduces card 3's genuine Figma
     overrun (471 in a 467 card at 1520) instead of shrinking it away. Below 1024 they stay
     shrinkable so the metric labels wrap to the 2 lines the 2.2em reserve is sized for. */
  .cases__metric {
    /* RESPONSIVE (STEP 5b): the tile's inner gap is drawn 6 on desktop (3001:163/166/169) and
       5 on mobile (3001:3876/3879/3882) — per-layer, so it ramps with the band like the tile
       gap above it, instead of holding 100% of its 1520 value at 1024 and never growing above
       --vw-base. D-min = round(6 x 1024/1520) = 4 · D-max = round(6 x 1920/1520) = 8 */
    gap: clamp(4px, calc(6px * (var(--max-vw) / var(--vw-base))), 8px);
    flex: 0 0 auto;
  }

  /* Every desktop label is ONE line: 1.1 x 16 = 17.6 ~ Figma 18, tile 58 + 6 + 18 = 82. */
  .cases__metric-label {
    min-height: 1.1em;
  }

  .cases__more {
    /* Label→arrow gap: 10 desktop (3001:178/203/228) vs 5 mobile (3001:3891/3916/3941) — the
       same per-layer shape as .program__cta. D-min = 7 · D-max = 13. The box arithmetic in the
       comment above is vertical (7/5 padding + 2px stroke) and is untouched. */
    gap: clamp(7px, calc(10px * (var(--max-vw) / var(--vw-base))), 13px);
  }
}
