:root {
  /* Fluid ratio: 0 at mobile frame (393px), 1 at desktop frame (1512px).
     Drives font-sizes and all values that don't change structurally. */
  --t: clamp(0, calc((100vw - 393px) / 1119px), 1);

  /* The headline has a real structural breakpoint at 760px (see .hero__title
     rules below): below it, Figma's mobile 3-line headline scales fluidly
     up from the mobile frame (393px); above it, Figma's desktop 2-line
     headline scales fluidly down from the desktop frame (1512px). These two
     ratios drive each side of that breakpoint independently. */
  --t-mobile-side: clamp(0, calc((100vw - 393px) / 367px), 1); /* 0 at 393px, 1 at 760px */
  --t-desktop-side: clamp(0, calc((100vw - 760px) / 752px), 1); /* 0 at 760px, 1 at 1512px */

  --color-green: #16403F;
  --color-button-green: #1F4435;
  --color-green-panel-start: #16403F;
  --color-green-panel-end: #0F2E2D;
  --color-gold: #EAB95C;
  --color-cream: #F8F3E9;
  --color-cream-mid: #F2EADE;
  --color-card-cream: #FFFDF7;
  --color-white: #FFFFFF;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  color: var(--color-green);
  font-family: "DM Sans", sans-serif;
  background: var(--color-cream);
}

img {
  display: block;
  max-width: 100%;
}

a {
  text-decoration: none;
  color: inherit;
}

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

.hero {
  position: relative;
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  /* Mobile end lowered from 852px to 740px: on a real iPhone the visible
     viewport (address bar + tab bar included) is only ~680px, so an 852px
     hero left the content block looking pushed toward the bottom half (huge
     gap above it, section continuing for 172px below the fold). 740px
     leaves a small ~60px bleed below the fold for the gradient fade into
     the Challenges section, while keeping the content itself within the
     visible area — see .hero__content's `top` below, recentered for this
     new height. Desktop end (982px at t=1) is unchanged, desktop was
     already confirmed good. */
  height: calc(740px + 242px * var(--t));
  overflow: hidden;
  background: linear-gradient(180deg,
    #EAB95C 0%,
    #EAB95C 12.433%,
    rgba(248, 243, 233, 0.6) 80.37%,
    rgba(248, 243, 233, 0.8) 90.385%,
    #F8F3E9 100%);
}

/* Only cap the section to the viewport on real desktop-sized screens (so a
   MacBook in fullscreen sees the whole hero without scrolling). Phones and
   tablets keep the plain fluid height above and just scroll normally like
   the rest of the page — capping them too was shrinking the mobile layout
   below its designed proportions. */
@media (min-width: 1024px) {
  .hero {
    height: min(calc(852px + 130px * var(--t)), 100vh);
  }
}

/* The <video> itself is never shown — it just plays off-screen as a frame
   source for js/hero-video-key.js, which draws it to the canvas below with
   the cream background keyed out to real per-pixel alpha (see that file for
   why: mix-blend-mode:multiply was only an approximation of transparency and
   left a visible seam where the video's background didn't exactly match the
   CSS gradient). display:none would risk the browser pausing/throttling
   decode, so it's kept a real (tiny, invisible) element instead. */
.hero__video-source {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Canvas: same box the <video> used to occupy. Height is a % of the
   section's own height, which keeps the video/content vertical relationship
   like Figma even when the section is capped shorter by the 100vh limit
   above — EXCEPT the min-height floor below: the video's native footage is
   2400x1018 (ratio ~2.358). If the %-based height ever gets shorter than
   width/2.358, the "cover" crop (done manually in the shader, since canvas
   has no object-fit) would have to crop vertically (cropping the top)
   instead of horizontally like it does at the real Figma proportions — the
   floor guarantees the box never gets relatively "wider than the video," so
   cropping always stays on the sides, never the top. */
.hero__video {
  position: absolute;
  left: 0;
  /* Mobile only: shifted up 40px so more of the video is visible on first
     load, without touching its height/crop math (that's all handled by the
     shader in js/hero-video-key.js off this box's width/height, not its
     bottom offset). This DOES need .hero__fade's gradient adjusted to match
     (see below) — the fade previously only reached full opacity exactly at
     the hero's bottom edge (100%), which lined up with the video also
     ending there; with the video now ending 40px earlier, that same 100%
     timing left a ~40px stretch where the fade was still slightly
     translucent, letting the canvas's hard bottom edge show through as a
     faint seam. Desktop keeps the original bottom: 0, unchanged, and so
     does its fade timing. */
  bottom: 40px;
  width: 100%;
  height: calc(54.8005% + 11.7983% * var(--t));
  min-height: calc(min(100vw, 1512px) / 2.35756);
  pointer-events: none;
  z-index: 1;
}

@media (min-width: 761px) {
  .hero__video {
    bottom: 0;
  }
}

.hero__fade {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(180deg,
    #EAB95C 0%,
    #EAB95C 12.433%,
    rgba(248, 243, 233, 0.6) 80.37%,
    rgba(248, 243, 233, 0.8) 90.385%,
    #F8F3E9 100%);
}

/* Mobile only: the video's box now ends 40px above the hero's bottom edge
   (see .hero__video's bottom: 40px), so the fade must reach full opacity by
   that same point (700px / 740px = 94.59%) rather than at 100% — otherwise
   the last stretch is still slightly translucent while the video content
   behind it has already hard-stopped, letting the canvas's flat bottom edge
   show through as a faint seam.

   Two earlier attempts before this one: (1) just moving the last stop to
   94.59% fixed the bottom seam but compressed the whole fade-to-solid
   segment, making it noticeably more abrupt than desktop's. (2) Keeping
   that segment's exact desktop pixel-length (by pushing the compression
   entirely into the gold→cream segment instead) fixed that, but over­
   compressed THAT segment enough to create a new, different seam right at
   the video's TOP edge (which sits inside it) — desktop never has this
   problem because its fade is never compressed at all.

   This version instead scales EVERY stop from 12.433% onward by the same
   factor (so 12.433%→100% becomes 12.433%→94.59%, uniformly), spreading
   the necessary compression evenly across the whole gradient rather than
   concentrating it in any one segment or edge. Desktop's own stops
   (80.37%/90.385%/100%, uncompressed) are untouched. */
@media (max-width: 760px) {
  .hero__fade {
    background: linear-gradient(180deg,
      #EAB95C 0%,
      #EAB95C 12.433%,
      rgba(248, 243, 233, 0.6) 76.17%,
      rgba(248, 243, 233, 0.8) 85.56%,
      #F8F3E9 94.59%);
  }
}

.logo {
  position: absolute;
  z-index: 3;
  left: calc(20px + 114px * var(--t));
  top: calc(2.3474% - 1.3291% * var(--t));
  width: calc(77px + 28px * var(--t));
  height: auto;
}

.logo__image {
  width: 100%;
  height: auto;
}

/* Headline + Sub + Button + Eyebrow flow together as one column (like a
   Figma Auto Layout stack) so the button/eyebrow always sit AFTER the text,
   however many lines it wraps to at in-between viewport widths. Fixed
   top-offsets for each element independently caused the Sub text to slide
   under the Button at intermediate sizes.

   Below 761px this now follows the reworked mobile design (design-refs/
   hero-mobile.png): text stays centered like desktop, but the button becomes
   full-width instead of a pill — a real structural difference, not just
   smaller sizes, so (per CLAUDE.md's own rule) it's a hard breakpoint rather
   than fluid-scaled from the desktop composition. Desktop (>=761px) keeps
   its exact previous values, untouched. */
.hero__content {
  position: absolute;
  z-index: 3;
  left: 50%;
  transform: translateX(-50%);
  /* Deliberately deviates from Figma's mobile node (which had this at
     338px / 852px = 39.6714%). Recentered to 197px / 740px within a real
     iPhone's ~680px visible viewport, nudged down so the CTA button's top
     lined up with where the eyebrow text used to end (279.5px / 740px),
     then nudged back up 30px total per real-device feedback (249.5px /
     740px) — a deliberate real-device-tested placement, not a Figma value. */
  top: 33.71%;
  width: calc(353px + 347px * var(--t-mobile-side));
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

@media (min-width: 761px) {
  .hero__content {
    top: calc(33.0986% + 5.1906% * var(--t) - 25px);
    width: calc(700px + 360px * var(--t-desktop-side));
  }
}

/* Two explicit headlines instead of one that relies on natural reflow: the
   desktop copy hard-breaks into Figma's 2-line composition, the mobile copy
   hard-breaks into Figma's 3-line composition. Only one is shown at a time,
   switching at the same 760px breakpoint as the width above. Letting a
   single headline's line-wrapping "discover" 2 vs. 3 lines on its own turned
   out to flip unpredictably (and briefly overflow past the viewport) at
   in-between widths — an explicit breakpoint matches CLAUDE.md's own rule
   for structural changes. */
.hero__title {
  width: 100%;
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(30px + 30px * var(--t));
  line-height: 1.2;
  color: var(--color-green);
}

.hero__title--desktop {
  display: none;
}

.hero__title--mobile {
  display: block;
}

@media (min-width: 761px) {
  .hero__title--desktop {
    display: block;
  }

  .hero__title--mobile {
    display: none;
  }
}

.hero__title strong {
  font-weight: 700;
}

.hero__sub {
  width: 100%;
  margin: 17px 0 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: 17px;
  line-height: 1.35;
  color: var(--color-green);
}

@media (min-width: 761px) {
  .hero__sub {
    margin: calc(22px + 16px * var(--t) - 10px) 0 0;
    font-size: calc(15px + 15px * var(--t));
  }
}

.hero__sub em {
  font-style: italic;
}

/* Mobile: full-width button (design-refs/hero-mobile.png). Desktop keeps the
   original pill-shaped button, untouched. */
.cta-button {
  margin: 32px 0 0;
  width: 100%;
  height: 55px;
  border-radius: 15px;
  font-size: 15px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-button-green);
  color: var(--color-white);
  font-family: "Unbounded", sans-serif;
  font-weight: 400;
  text-align: center;
  cursor: pointer;
}

@media (min-width: 761px) {
  .cta-button {
    margin: calc(32px + 16px * var(--t)) 0 0;
    width: calc(241.588px + 202.412px * var(--t));
    height: calc(37px + 31px * var(--t));
    border-radius: calc(15px + 5px * var(--t));
    font-size: calc(10.882px + 9.118px * var(--t));
  }
}

/* Eyebrow stays centered on both mobile and desktop; only its container
   width changes (full column on mobile vs. a narrow pill-width on desktop). */
.hero__eyebrow {
  margin: 12px 0 0;
  width: 100%;
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-style: italic;
  font-size: 12px;
  color: var(--color-button-green);
  text-align: center;
}

@media (min-width: 761px) {
  .hero__eyebrow {
    margin: calc(12px + 4px * var(--t)) 0 0;
    width: calc(247px + 197px * var(--t));
    font-size: calc(9px + 5px * var(--t));
  }
}

/* ---------- Challenges ---------- */

/* Shared utility for small mobile/desktop copy divergences across sections
   (a word or two that only appears on one breakpoint) — toggled at the same
   761px breakpoint the hero uses for its structural mobile/desktop split. */
.mobile-only {
  display: inline;
}

.desktop-only {
  display: none;
}

@media (min-width: 761px) {
  .mobile-only {
    display: none;
  }

  .desktop-only {
    display: inline;
  }
}

.challenges {
  /* Static fluid fallback for the instant before js/challenges-scroll.js
     runs, or if JS is unavailable. js/challenges-scroll.js overrides this
     via --dynamic-top-gap at every width, mobile included: it measures the
     actual pinned content height and the real viewport height and computes
     a gap that puts EQUAL space above and below the stack (so the resting
     frame is exactly centered — on a fullscreen vs. a windowed browser at
     the same width, the two have different effective heights, so a single
     static value could never center both). */
  --top-gap: calc(55px + 41px * var(--t));

  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  padding: var(--dynamic-top-gap, var(--top-gap)) 0 calc(206px + 143px * var(--t));
}

/* The tall scroll "runway" for the pin/stack effect. Its height is set
   imperatively by js/challenges-scroll.js (sticky height + extra scroll
   distance) so the JS progress=1 point and the CSS sticky-release point
   never drift apart — deliberately left unset here. Must never get
   `overflow: hidden` (or any ancestor of it) — that silently breaks
   `position: sticky` on the child below. */
.challenges__pin-wrap {
  position: relative;
}

/* Pins the headline + card stack while the runway above scrolls underneath.
   `top` matches .challenges' own top padding exactly (--dynamic-top-gap or
   its --top-gap fallback) so the pinned content is already sitting at its
   locked gap the instant it scrolls into view — no separate glide-then-snap
   transition. */
.challenges__sticky {
  position: sticky;
  top: var(--dynamic-top-gap, var(--top-gap));
}

.challenges__headline {
  width: calc(353px + 503px * var(--t));
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(16px + 12px * var(--t)); /* 16px mobile → 28px desktop */
  text-align: center;
}

.challenges__title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(21px + 23px * var(--t));
  line-height: 1.2;
  color: var(--color-green);
}

.challenges__title strong {
  font-weight: 700;
}

.challenges__sub {
  margin: 0;
  width: calc(271px + 172px * var(--t));
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: calc(14px + 8px * var(--t));
  line-height: 1.4;
  color: var(--color-green);
}

.challenges__sub strong {
  font-weight: 600;
}

.challenges__cards {
  width: calc(353px + 566px * var(--t));
  /* Constant 32px on every screen size — not fluid-scaled — per the
     original brief; kept as-is regardless of what the Figma redesign's
     headline-to-card spacing measured out to. */
  margin: 32px auto 0;
  display: grid;
}

/* All 3 cards share the same grid cell so they stack directly on top of one
   another. `align-self: start` keeps each card at its own (content-driven)
   height instead of being stretched to the tallest card's row height.
   No z-index needed: default grid stacking paints later DOM siblings on
   top, so card 03 already covers 02, which covers 01 — matching the
   fly-in-then-cover order in js/challenges-scroll.js. */
.challenge-card {
  grid-area: 1 / 1 / 2 / 2;
  align-self: start;
  box-sizing: border-box;
  width: 100%;
  /* Padding is intentionally NOT equal on every side at the desktop end —
     these are the exact figures from the Figma redesign (89 top / 99 bottom
     / 94 sides). */
  padding: calc(36px + 53px * var(--t)) calc(36px + 58px * var(--t)) calc(36px + 63px * var(--t));
  border: calc(0.664px + 1.065px * var(--t)) solid var(--color-gold);
  border-radius: calc(8.302px + 13.311px * var(--t));
  background: linear-gradient(180deg, var(--color-green-panel-start) 0%, var(--color-green-panel-end) 100%);
  box-shadow:
    calc(2.657px + 4.259px * var(--t)) calc(2.657px + 4.259px * var(--t)) calc(2.989px + 4.792px * var(--t)) rgba(11, 30, 22, 0.3);
  will-change: transform;
  /* js/challenges-scroll.js forces all 3 cards to a shared min-height (the
     tallest card's natural height) so each fully covers the one before it
     when stacked. Cards' natural heights can differ slightly (the
     mobile-only/desktop-only copy variants aren't the same length), so
     centering the content group vertically keeps the padding visually equal
     on every side regardless of how much shorter a card's own text is than
     the tallest one. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.challenge-card__heading {
  display: flex;
  flex-direction: column;
  gap: calc(6px + 16px * var(--t));
}

.challenge-card__number {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 900;
  font-size: calc(26px + 29.33px * var(--t));
  line-height: 1;
  color: var(--color-gold);
}

.challenge-card__title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 700;
  font-size: calc(30px + 28.788px * var(--t));
  line-height: 1;
  color: var(--color-card-cream);
}

.challenge-card__body {
  margin: calc(16px + 29px * var(--t)) 0 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(15px + 16.123px * var(--t));
  line-height: calc(17px + 15.852px * var(--t));
  color: rgba(248, 243, 233, 0.9);
}

.challenge-card__highlight {
  color: rgba(234, 185, 92, 0.9);
}

/* ---------- Solution ---------- */

.solution {
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  background: var(--color-cream);
}

/* The tall scroll "runway" for the pin/reveal effect: its height is set
   imperatively by js/solution-scroll.js (sticky height + extra scroll
   distance) so the JS
   progress=1 point and the CSS sticky-release point never drift apart. Must
   never get `overflow: hidden` (or any ancestor of it) — that silently
   breaks `position: sticky` on the child below.

   The background gradient lives here instead of on .solution__sticky so it
   spans the section's FULL scroll runway, not just the pinned content's own
   box — otherwise the gradient's end color (#F2EADE) hits a hard seam against
   the plain cream around it as soon as the runway extends past the sticky
   content's height. Percentages are relative to this element's own height,
   which js/solution-scroll.js sets dynamically, so the gradient always
   spans exactly this section regardless of viewport size. */
.solution__pin-wrap {
  position: relative;
  background: linear-gradient(180deg,
    #F8F3E9 0%,
    rgba(241, 233, 220, 0.6) 80.37%,
    rgba(241, 233, 220, 0.8) 90.385%,
    #F2EADE 100%);
}

/* Pins the headline/journey/cards/CTA while the runway above scrolls
   underneath. `top` is computed by js/solution-scroll.js as the gap that
   centers this whole block vertically in the current viewport, at every
   width. */
.solution__sticky {
  position: sticky;
  top: var(--dynamic-top-gap, 40px);
  padding: calc(40px - 28px * var(--t)) 0 calc(48px - 13px * var(--t));
}

.solution__headline {
  width: calc(353px + 710px * var(--t));
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(14px + 14px * var(--t));
  text-align: center;
}

.solution__title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  line-height: 1.2;
  color: var(--color-green);
}

.solution__title {
  font-size: calc(22px + 22px * var(--t));
}

.solution__title strong {
  display: inline;
  font-weight: 600;
  font-size: calc(19px + 25px * var(--t));
}

.solution__underscore-wrap {
  position: relative;
  display: inline-block;
}

.solution__underscore {
  position: absolute;
  left: 50%;
  /* em, not px: .solution__underscore-wrap's box is a full line-height box
     (taller than the glyphs themselves), so anchoring purely off "100%"
     left the stroke sitting well below the actual descenders of "through,"
     — noticeably lower than Figma's tight, almost-touching placement. An
     em-based pull-up scales with the headline's own font-size (very
     different between mobile and desktop), unlike a fixed px offset. */
  top: calc(100% - 0.16em);
  transform: translateX(-50%);
  /* A % width (of .solution__underscore-wrap, its containing block as an
     absolutely-positioned element) instead of a fixed px formula: sizing it
     as a fraction of the "through," word's OWN rendered width is what
     actually reproduces the Figma frame at every size — a fixed calc(94px
     -> 272px) value was derived from Figma's own two frames but overshot
     the word significantly at desktop, since our webfont doesn't render
     "through," at exactly Figma's assumed width. */
  width: 100%;
  height: auto;
  pointer-events: none;
}

.solution__sub {
  margin: 0;
  width: 100%;
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: calc(14px + 8px * var(--t));
  line-height: 1.4;
  color: var(--color-green);
}

.solution__sub em {
  font-style: italic;
}

/* Journey: desktop-only decorative step row — no distinct mobile design exists
   in Figma for it (the mobile frame skips straight from headline to cards),
   so it's hidden below the same 761px hard breakpoint used elsewhere for
   structural mobile/desktop splits. Every size below fluid-scales off
   --t-desktop-side (0 at 761px, 1 at 1512px, matching the exact Figma value)
   instead of a fixed 1512px-only size — a fixed size looked fine at exactly
   1512px but didn't scale at all in between, making the row (and its reveal
   animation) look broken/static at any in-between desktop window width. */
.solution__journey {
  display: none;
}

@media (min-width: 761px) {
  .solution__journey {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: calc(523px + 376px * var(--t-desktop-side));
    margin: calc(52px - 12px * var(--t)) auto 0;
  }
}

.solution__journey-line {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(19px + 18.5px * var(--t-desktop-side));
  height: 2px;
  background: rgba(234, 185, 92, 0.9);
  transform-origin: left;
  transform: scaleX(0);
  will-change: transform;
}

.solution__journey-step {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
  will-change: opacity, transform;
}

.solution__journey-circle {
  width: calc(38px + 37px * var(--t-desktop-side));
  height: calc(38px + 37px * var(--t-desktop-side));
  border-radius: 50%;
}

.solution__journey-label {
  margin: 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: calc(10px + 2px * var(--t-desktop-side));
  color: var(--color-green);
  white-space: nowrap;
}

/* Mobile: cards 2/3 overlay card 1 (same box) instead of stacking in normal
   flow, sliding in from the right edge of the screen — see
   js/solution-scroll.js. That needs this container to span the true
   viewport width (not just its own padded column) so a card can start
   fully off past the right edge without being clipped early; the
   left:50%/translateX(-50%) pair is a standard "full-bleed" break-out that
   doesn't touch any ancestor's overflow (which would risk breaking the
   position:sticky ancestor above, see .solution__pin-wrap). Height is set
   imperatively by the script once card heights are known (cards 1-3 differ
   slightly; they're equalized to the tallest so the overlay has a clean,
   consistent edge). Desktop's row layout (media query below) resets all of
   this back to a normal, non-breakout flex row. */
.solution__cards {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  margin: calc(35px - 3px * var(--t)) 0 0;
  overflow: hidden;
}

/* Desktop: card width + gap are both solved (see the accompanying
   .solution-card width override below) so 3*cardWidth + 2*gap always equals
   this row's own natural width at every point between 761px and 1512px —
   never wrapping to 2+1, and never needing a max-width guess that only
   happened to fit at exactly 1512px. width:fit-content + no wrap is what
   guarantees that (a max-width here would just reintroduce the same risk of
   the row not matching the cards' actual combined width at some in-between
   size). */
@media (min-width: 761px) {
  .solution__cards {
    position: static;
    left: auto;
    transform: none;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: center;
    gap: calc(40px + 62px * var(--t-desktop-side));
    width: fit-content;
    margin: calc(52px - 12px * var(--t)) auto 0;
    overflow: visible;
    height: auto;
  }
}

/* `left` is set imperatively by js/solution-scroll.js (centered within the
   full-viewport-width .solution__cards break-out above — a fixed px inset
   here would only be correct at exactly the 393px mobile frame and drift
   off-center at any other mobile viewport width, since the card's own width
   is fluid). Desktop doesn't use `left` at all (position:relative), so it's
   harmless there. */
.solution-card {
  box-sizing: border-box;
  position: absolute;
  top: 0;
  width: calc(353px - 8px * var(--t));
  padding: calc(27px - 1px * var(--t));
  border: 1px solid rgba(244, 182, 71, 0.3);
  border-radius: 15px;
  background: var(--color-card-cream);
  will-change: transform;
}

@media (min-width: 761px) {
  .solution-card {
    position: relative;
    top: auto;
    left: auto;
    /* Replaces the mobile width formula above (which tracks the full
       393px->1512px --t and would give ~350px here, not the ~214px->345px
       range that actually keeps 3 cards + 2 gaps matching .solution__cards'
       own width at every point from 761px to 1512px). */
    width: calc(213.667px + 131.333px * var(--t-desktop-side));
    height: auto !important;
    will-change: transform;
  }
}

.solution-card__heading {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(12px - 4px * var(--t));
}

.solution-card__checkmark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(54.397px - 21.397px * var(--t));
  height: calc(54.397px - 21.397px * var(--t));
  border-radius: 50%;
  background: var(--color-green);
  color: rgba(234, 185, 92, 0.9);
  font-family: "Unbounded", sans-serif;
  font-weight: 400;
  font-size: calc(36.265px - 14.265px * var(--t));
}

.solution-card__title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(22px - 6px * var(--t));
  line-height: 1.3;
  color: var(--color-green);
}

.solution-card__body {
  margin: calc(16px - 4px * var(--t)) 0 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(16px - 3.5px * var(--t));
  line-height: 1.3;
  color: var(--color-green);
}

.solution-card__body em {
  font-style: italic;
}

.solution__cta {
  margin: calc(35.2px + 4.8px * var(--t)) auto 0;
  /* Matches .hero__content's own mobile width formula exactly (not the
     card-width-derived calc used elsewhere in this section) so this button
     is 1:1 identical to the Hero's — including how much it grows toward
     the 761px breakpoint, since .cta-button itself is width:100% of this
     container. */
  width: calc(353px + 347px * var(--t-mobile-side));
  will-change: opacity;
}

@media (min-width: 761px) {
  .solution__cta {
    width: fit-content;
  }
}

.solution__cta .cta-button {
  margin: 0;
}

/* ---------- About Suda ---------- */

/* Structural mobile/mobile-desktop split, same reasoning as the Hero and
   Solution sections above: mobile stacks image → text → both buttons in one
   column (image full-bleed, eyebrow becomes a centered bordered badge);
   desktop switches to a 2-column row (image | text) with the two buttons
   below, each aligned under its own column — a real layout change, not just
   smaller sizes, so it's a hard breakpoint at 761px rather than fluid-scaled
   from one composition. Figma's own two frames (mobile node 284:595 /
   desktop node 167:117) diverge further: mobile's eyebrow gets an explicit
   border "badge" and a different color/weight that desktop's plain-text
   eyebrow doesn't have. */
.about-suda {
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  background: var(--color-cream-mid);
  padding: 0 0 calc(56px + 8px * var(--t));
}

.about-suda__inner {
  display: flex;
  flex-direction: column;
}

/* Mobile: media/content are flattened (display:contents) so their children
   — photo, outline button, eyebrow, title, body, filled button — all become
   direct flex items of .about-suda__inner and can be freely reordered with
   `order` below, regardless of their DOM/nesting position. This is what
   lets the desktop layout nest each button inside its own column (see the
   761px override) for a hard structural guarantee that photo+outline stay
   adjacent, while mobile still shows both buttons grouped at the very end
   like Figma's mobile frame — one isn't possible without the other unless
   the DOM is duplicated, which we don't want. */
.about-suda__media,
.about-suda__content {
  display: contents;
}

/* Mobile: deliberately smaller and centered rather than the full-bleed size
   Figma's mobile frame uses — at the literal Figma size, image + eyebrow +
   heading + body don't fit a real phone viewport without scrolling past the
   buttons. Shrinking the photo lets the whole text block land on screen
   together with it. Desktop (see override below) restores Figma's own
   full-bleed-of-its-column sizing via .about-suda__media's own width. */
.about-suda__photo {
  order: 1;
  display: block;
  width: 300px;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  aspect-ratio: 485 / 608;
  object-fit: cover;
}

/* Mobile: a centered bordered "badge" (Figma mobile node 283:365) that
   desktop drops entirely in favor of plain left-aligned text — see the
   desktop override below. Flush against the image (no gap), matching
   Figma (image bottom and badge top sit within a fraction of a px). */
.about-suda__eyebrow {
  order: 2;
  margin: 0 auto;
  width: fit-content;
  padding: 6px 16px;
  border: 1.1px solid #2E7355;
  border-radius: 16px;
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: 13px;
  color: #2E7355;
  text-align: center;
}

.about-suda__title {
  order: 3;
  margin: 20px 20px 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(22.722px + 5.278px * var(--t));
  line-height: 1.15;
  color: var(--color-green);
}

.about-suda__body {
  order: 4;
  margin: 18px 20px 0;
}

.about-suda__body p {
  /* Figma spaces paragraphs with a full blank line (its own text block at
     the same line-height), not a tight margin — so the gap scales with
     line-height (~21px mobile → ~33px desktop) rather than a fixed value. */
  margin: 0 0 calc(21px + 12px * var(--t));
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(16.23px + 3.77px * var(--t));
  line-height: 1.35;
  color: var(--color-green);
}

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

.about-suda__body strong {
  font-weight: 500;
}

.about-suda__body em {
  font-style: italic;
}

.about-suda__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  border-radius: 15px;
  font-family: "Unbounded", sans-serif;
  font-weight: 400;
  font-size: 15px;
  text-align: center;
}

.about-suda__btn--outline {
  order: 5;
  width: calc(100% - 40px);
  margin: 13px auto 0;
  background: transparent;
  border: 1px solid #2E7355;
  color: #2E7355;
}

.about-suda__btn--filled {
  order: 6;
  width: calc(100% - 40px);
  margin: 13px auto 0;
  background: #2E7355;
  color: var(--color-white);
}

@media (min-width: 761px) {
  .about-suda {
    /* min-height:100vh + flex centering below reacts to whatever the
       CURRENT viewport height actually is (fullscreen or a small windowed
       browser) instead of a fixed value that only centers correctly at one
       specific window height — same intent as Challenges/Solution's
       JS-measured --dynamic-top-gap, done with plain CSS since this
       section doesn't pin/scroll-jack. The padding below is now just a
       floor for short windows, not the primary spacing mechanism, which is
       why it's much smaller than Figma's own 139px gap (used pre-fix) —
       that floor alone was tall enough to always exceed real laptop
       viewport heights, so centering never had room to actually apply and
       the section was permanently stuck at its own worst-case height. */
    min-height: 100vh;
    padding-top: calc(40px + 30px * var(--t));
    padding-bottom: calc(40px + 30px * var(--t));
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
  }

  .about-suda__inner {
    flex-direction: row;
    justify-content: center;
    /* stretch (the default, so no align-items override here) makes both
       columns match the taller one's height (media, thanks to its locked
       photo+button). .about-suda__btn--filled's margin-top:auto below then
       pushes it to the bottom of that shared height, lining the two
       buttons up on the same row without undoing the media lock. */
    gap: 24px;
  }

  /* Un-flatten: media/content become real flex columns again on desktop, so
     each one's own children (photo+outline / eyebrow+title+body+filled)
     stack independently — `order` from the mobile rules above no longer
     matters once these are laid out inside their own separate containers. */
  .about-suda__media,
  .about-suda__content {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
  }

  .about-suda__media {
    width: calc(300px + 185px * var(--t));
    /* Photo and its button are ALWAYS adjacent — zero gap, no margin — so
       nothing about the text column's height can ever push them apart. */
    gap: 0;
  }

  .about-suda__photo {
    /* Reset mobile's fixed 300px shrink — desktop fills whatever width
       .about-suda__media's own fluid formula above gives it. */
    width: 100%;
    max-width: none;
    margin: 0;
  }

  .about-suda__content {
    width: calc(320px + 115px * var(--t));
    /* Figma stagger: the text column starts noticeably lower than the image
       (node 167:70 top 4316 vs the image's own top 4241, a 75px offset) so
       the block reads as vertically centered against the photo rather than
       top-aligned with it. */
    margin-top: calc(40px + 35px * var(--t));
  }

  .about-suda__eyebrow {
    margin: 0;
    width: auto;
    padding: 0;
    border: none;
    border-radius: 0;
    font-weight: 300;
    font-size: calc(13px + 1px * var(--t));
    color: var(--color-green);
    text-align: left;
  }

  .about-suda__title {
    margin: 16px 0 0;
    line-height: 1.1;
  }

  .about-suda__body {
    margin: 33px 0 0;
  }

  .about-suda__body p {
    line-height: 1.3;
    /* Old formula (16.23px→20px across the FULL 393-1512 range) only moved
       ~2.5px within the 761-1512 desktop range itself — resizing the
       browser within that range barely changed the font size at all, so
       narrowing just wrapped more lines instead of visibly scaling down.
       --t-desktop-side (0 at 761px, 1 at 1512px) gives the same 20px
       endpoint at full width but a real, visible 6px range across the
       whole desktop-only span. */
    font-size: calc(14px + 6px * var(--t-desktop-side));
  }

  .about-suda__btn {
    width: auto;
    height: calc(28px + 26px * var(--t));
    border-radius: calc(15px + 5px * var(--t));
    font-size: calc(10.882px + 9.118px * var(--t));
  }

  /* Outline button sits centered under the (wider) image column, inset
     25px each side (Figma: button left 312 / right 747 vs. image left 287 /
     right 772); the filled button matches the text column's box exactly
     (both left 762, width 435). Both are 0-margin now — spacing comes from
     their parent column's own flow (media's gap:0 lock; content's own
     rhythm), not from the button rules themselves. */
  .about-suda__btn--outline {
    width: calc(100% - 50px);
    margin: 0 auto;
  }

  .about-suda__btn--filled {
    width: 100%;
    /* Pushes to the bottom of .about-suda__content's stretched height (see
       .about-suda__inner above), lining this button up with the outline
       button's row instead of sitting wherever the text happens to end. */
    margin: auto 0 0;
  }
}

/* ---------- TheGreenChambers ---------- */

/* Figma's own frame has the wrong flat background for this section; per
   direct instruction this is a gradient back from the previous section's
   #F2EADE down to the site's base #F8F3E9 (mirrors the shape of every other
   gradient on the page, just inverted start/end). Lives on the outer section
   (not just the pinned content) so it spans the carousel/CTA below the pin
   too, same reasoning as .solution__pin-wrap's own background comment. */
.green-chambers {
  /* Single shared curve for the reveal image's own width, reused by both the
     image boxes and (via the *1.345524 factor = 706/525, the Figma image's
     own aspect ratio) the full-bleed desktop viewport's explicit height,
     see below. Continuous across the whole 393-1512 range like the rest of
     the site's fluid values; only its desktop endpoint changed (525px ->
     430px) to shrink the reveal per direct feedback, mobile's own endpoint
     (353px at t=0) is untouched. */
  --gc-image-w: calc(353px + 77px * var(--t));

  position: relative;
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  /* Direct feedback: more breathing room above the section than the previous
     section's own bottom padding already gave. */
  padding-top: calc(60px + 60px * var(--t));
  background: linear-gradient(180deg,
    #F2EADE 0%,
    rgba(248, 243, 233, 0.6) 40%,
    rgba(248, 243, 233, 0.85) 70%,
    #F8F3E9 100%);
}

/* Tall scroll "runway" for the pin/reveal effect — same technique as
   .solution__pin-wrap: height set imperatively by
   js/green-chambers-scroll.js (sticky height + extra scroll distance) so the
   JS progress=1 point and the CSS sticky-release point never drift apart.
   Must never get `overflow: hidden` (or any ancestor of it) — that silently
   breaks `position: sticky` on the child below. */
.green-chambers__pin-wrap {
  position: relative;
}

/* Pins the image reveal + captions + welcome text while the runway above
   scrolls underneath. `top` is computed by js/green-chambers-scroll.js as
   the gap that centers this whole block vertically in the current viewport
   (mirrors .solution__sticky). The desktop reveal images + caption + welcome
   body were sized specifically so this whole stack fits one ordinary laptop
   viewport with room to spare for that centering gap (direct feedback) —
   see --gc-image-w above and the caption/welcome font-size overrides below. */
.green-chambers__sticky {
  position: sticky;
  top: var(--dynamic-top-gap, 40px);
  padding: calc(40px - 20px * var(--t)) 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Mobile: sized to hold exactly one image box (the two images overlay each
   other there, unchanged per direct feedback — "mobile reveal so lassen").
   Desktop: a full-bleed 100vw stage (same break-out technique as
   .solution__cards' mobile overlay cards) so the "breathing" image can
   visibly travel in all the way from the real screen edge, not just from
   just past its own resting column — see the 761px override and
   js/green-chambers-scroll.js's measureDesktopOffsets(). Explicit height
   there (aspect-ratio can't apply to the container itself once its own
   width stops matching the image's aspect ratio). */
.green-chambers__reveal-viewport {
  position: relative;
  overflow: hidden;
  width: var(--gc-image-w);
  aspect-ratio: 525 / 706;
  margin: 0 auto;
}

@media (min-width: 761px) {
  .green-chambers__reveal-viewport {
    left: 50%;
    transform: translateX(-50%);
    width: 100vw;
    aspect-ratio: auto;
    height: calc(var(--gc-image-w) * 1.345524);
  }
}

/* Both images sit at the same base position (top:0/left:0) on both
   breakpoints now — ALL real positioning (centered-alone start state vs.
   side-by-side end state on desktop; overlay slide on mobile) is driven
   purely by js/green-chambers-scroll.js's own transform math each frame, not
   by flex/flow layout. This is what lets the desktop images travel across
   the full-bleed viewport above instead of being constrained to a flex
   row's own box. */
.green-chambers__image {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--gc-image-w);
  aspect-ratio: 525 / 706;
  overflow: hidden;
  box-sizing: border-box;
  border: calc(1.345px + 0.655px * var(--t)) solid var(--color-green);
  will-change: transform;
}

.green-chambers__image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Replicates Figma's own tight, shifted crop on the architect's render (its
   source photo is a wide 2.09:1 landscape shot being forced into a 0.74:1
   portrait box) — object-fit:cover alone would center-crop and lose the
   building on the right; this shifts the visible window rightward to match. */
.green-chambers__image--vision img {
  object-position: 64% 50%;
}

/* Both caption states occupy the same grid cell (like .challenge-card's own
   stacking trick) so the container never jumps height as the text
   transitions between the short start copy and the longer end copy — no JS
   measurement needed, CSS grid sizes to whichever cell content is tallest.

   Deliberately NOT overflow:hidden (it was here at one point, and that was
   itself a real bug): this box is only ~1065px wide, narrower than the
   viewport, while .green-chambers__caption-line below is a full-bleed
   100vw box with its OWN correctly-scoped overflow:hidden. An overflow:hidden
   HERE would clip that already-correct 100vw box down to this narrower
   column — invisible in a getBoundingClientRect()/transform check (an
   ancestor's clip doesn't change an element's own reported geometry) but
   very visible on screen: the incoming text would visually appear/get
   clipped against this narrow column's edge instead of the real screen
   edge, arriving noticeably later than — and out of sync with — the
   breathing image (whose own container has no such narrower ancestor
   clip). This was the actual cause of the "text doesn't start at the same
   place as the image" report; matching transform VALUES alone didn't catch
   it because both were correct — only the paint-time clip boundary wasn't.
   The mobile crossfade pair below never moves via transform (opacity only),
   so it never depended on this clip either. */
.green-chambers__caption {
  position: relative;
  display: grid;
  width: calc(353px + 712px * var(--t));
  margin: calc(17px + 5px * var(--t)) 0 0;
}

/* Mobile: unchanged opacity-crossfade pair, each holding the FULL caption
   text — kept exactly as before per direct feedback ("mobile so lassen").
   Hidden outright on desktop, replaced there by the split-line pair below. */
.green-chambers__caption-text {
  grid-area: 1 / 1 / 2 / 2;
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  /* Desktop endpoint shrunk from 39.8px to 28px alongside --gc-image-w, per
     direct feedback ("den Text darunter etwas kleiner skalieren"). Mobile's
     own endpoint (20px at t=0) is untouched. */
  font-size: calc(20px + 8px * var(--t));
  line-height: 1.2;
  text-align: center;
  color: var(--color-green);
}

.green-chambers__caption-text--end {
  opacity: 0;
}

@media (min-width: 761px) {
  .green-chambers__caption-text {
    display: none;
  }
}

/* Desktop only: the "Once a vision …" prefix NEVER disappears — it starts
   centered alone, then slides left into its final resting spot as the first
   half of the combined line, while just the SUFFIX (" today it's a
   breathing place.") flies in from the right to complete the line — direct
   feedback specifically asked for this instead of the old crossfade (which
   made the start text vanish entirely). Hidden on mobile, where the
   original .green-chambers__caption-text pair above still crossfades
   whole-line to whole-line, unchanged.

   Full-bleed 100vw + overflow:hidden, same INTENT as
   .green-chambers__reveal-viewport's own desktop override (the incoming
   suffix needs to travel through, and be measured/positioned against, the
   REAL screen edge) — but position:absolute here, not :relative. Tried
   :relative + grid-area first (matching the viewport's own markup more
   closely) and it measurably did NOT work: a percentage `left` on a
   *grid item* resolves against its own grid CELL, and — verified via
   getBoundingClientRect(), not just assumption — the combination with an
   explicit 100vw width and a translateX(-50%) counter-offset did not net out
   to a real viewport-edge-to-edge box the way the same technique does for
   an ordinary (non-grid-item) flex child. Taking it out of grid flow
   entirely with position:absolute sidesteps that: its containing block
   becomes .green-chambers__caption's own padding box (position:relative,
   set above) via ordinary abspos percentage rules, which is exactly the
   already-proven technique. Since an absolutely positioned element no
   longer contributes to the grid's own auto-height, .green-chambers__caption
   gets an explicit height here instead (same formula, just moved up). */
.green-chambers__caption-line {
  display: none;
}

@media (min-width: 761px) {
  .green-chambers__caption {
    height: calc((20px + 8px * var(--t)) * 1.2);
  }

  .green-chambers__caption-line {
    display: block;
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100vw;
    height: 100%;
    overflow: hidden;
    margin: 0;
  }
}

.green-chambers__caption-part {
  position: absolute;
  top: 0;
  left: 0;
  white-space: nowrap;
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  font-size: calc(20px + 8px * var(--t));
  line-height: 1.2;
  color: var(--color-green);
  will-change: transform;
}

/* Fades in only once the reveal animation's end state is fully locked in
   (see js/green-chambers-scroll.js's welcome stage) — same treatment as
   .solution__cta. Figma's own two frames measure to the same 22px gap below
   the caption on both mobile and desktop, so (per CLAUDE.md's own rule for
   constants that don't differ between the two endpoints) this is a flat
   value, not fluid-scaled. */
.green-chambers__welcome {
  width: calc(353px + 712px * var(--t));
  margin: 22px 0 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(15px + 17px * var(--t));
  line-height: 1.3;
  color: var(--color-green);
  text-align: left;
  opacity: 0;
  will-change: opacity;
}

/* Desktop: pinned to a flat 20px (down from the fluid curve's 32px end) and
   the paragraph's <br><br> break removed (mobile-only, see index.html) —
   Figma's own desktop frame was updated to match (single flowing sentence,
   font-size 20). Mobile keeps the original fluid 15px endpoint and its line
   break untouched. */
@media (min-width: 761px) {
  .green-chambers__welcome {
    font-size: 20px;
  }
}

/* Full-bleed break-out (same technique as .solution__cards on mobile): the
   carousel is deliberately wider than its column and bleeds off both edges
   of the viewport, so it needs the true viewport width to scroll/clip
   against rather than the centered content column. Auto-rotates via
   js/green-chambers-carousel.js; overflow-x:auto (not hidden) is what lets a
   real horizontal scroll/drag gesture take over, while overflow-y stays
   clipped so a vertical wheel scroll is never captured and just passes
   through to the page underneath. */
.green-chambers__carousel {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  /* Mobile endpoint (40px) brought down to match .green-chambers__cta's own
     mobile top-margin, per direct feedback ("gleicher Abstand wie von
     carousel zu CTA"). Desktop endpoint (76px) is unchanged. */
  margin: calc(40px + 36px * var(--t)) 0 0;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
}

.green-chambers__carousel.is-dragging {
  cursor: grabbing;
}

.green-chambers__carousel::-webkit-scrollbar {
  display: none;
}

.green-chambers__carousel-track {
  display: flex;
  width: max-content;
  gap: calc(10.7px + 9.3px * var(--t));
  padding: 0 calc(10.7px + 9.3px * var(--t));
}

.green-chambers__carousel-track img {
  flex-shrink: 0;
  width: calc(151.948px + 128.052px * var(--t));
  aspect-ratio: 280 / 425;
  object-fit: cover;
  border: calc(0.543px + 0.457px * var(--t)) solid var(--color-green);
  box-sizing: border-box;
  -webkit-user-drag: none;
  user-select: none;
}

.green-chambers__cta {
  display: flex;
  justify-content: center;
  margin: calc(40px + 22px * var(--t)) 0 calc(56px + 30px * var(--t));
}

.green-chambers__airbnb-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(354px + 174px * var(--t));
  height: calc(45.591px + 22.409px * var(--t));
  border: calc(1.341px + 0.659px * var(--t)) solid var(--color-green);
  border-radius: calc(13.409px + 6.591px * var(--t));
  background: var(--color-cream);
  font-family: "Unbounded", sans-serif;
  font-weight: 400;
  font-size: calc(15px + 7px * var(--t));
  color: var(--color-green);
  text-align: center;
}

/* ---------- Values ---------- */

.values {
  --heading-gap: calc(10.6px + 5.4px * var(--t)); /* pillar title -> category label */
  --row-gap: calc(14px + 10px * var(--t)); /* heading block <-> body text (vertical gap on mobile, column-gap on desktop) */
  --pillars-gap: calc(14px + 12px * var(--t)); /* gap between each pillar/divider in the stack */

  /* Static fluid fallback until js/values-scroll.js's first recalc()
     overrides it with a real centered gap (see --dynamic-top-gap) — same
     fallback pattern as .solution. */
  --top-gap: calc(56px + 24px * var(--t));

  position: relative;
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  padding: var(--dynamic-top-gap, var(--top-gap)) 0 calc(64px + 32px * var(--t));
}

/* Tall scroll runway for the pin/reveal effect, sized imperatively by
   js/values-scroll.js — same technique as .solution__pin-wrap. Must never
   get `overflow: hidden` (or any ancestor of it) — that silently breaks
   `position: sticky` on the child below. */
.values__pin-wrap {
  position: relative;
}

/* Pins the headline + pillars while the runway above scrolls underneath.
   Centers on both mobile and desktop — the brief asks for the same
   "headline locks, pillars fade in one by one, final frame is the whole
   thing centered" behavior at every width (mirrors .solution__sticky's own
   reasoning). */
.values__sticky {
  position: sticky;
  top: var(--dynamic-top-gap, var(--top-gap));
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Figma's mobile frame has this headline left-aligned (items-start); per
   direct instruction this section is centered on mobile too, matching every
   other section's own mobile treatment — so both breakpoints share the same
   centered markup/CSS here instead of forking at the 761px breakpoint. */
.values__headline {
  width: calc(353px + 714px * var(--t));
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(16px + 12px * var(--t));
  text-align: center;
}

.values__title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  font-size: calc(22px + 22px * var(--t));
  line-height: 1.15;
  color: var(--color-green);
}

.values__sub {
  margin: 0;
  width: calc(353px + 368px * var(--t));
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(15px + 7px * var(--t));
  line-height: 1.4;
  color: var(--color-green);
}

/* Figma's mobile/desktop gap here (46px / 48px) is close enough to count as
   a single constant, not two authored endpoints worth interpolating between
   (same reasoning CLAUDE.md documents for the Challenges headline-to-card
   gap) — kept as a barely-fluid value only to avoid a hard jump at 761px. */
.values__pillars {
  width: calc(353px + 714px * var(--t));
  margin: calc(44px + 4px * var(--t)) auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--pillars-gap);
}

/* Column (stacked) on mobile, matching Figma's own mobile layout — switches
   to `display: contents` at 761px below (see the media query), letting its
   heading/body children become direct items of .values__pillars' own grid
   instead. Fade/rise animation (js/values-scroll.js) is applied to the
   heading and body elements directly, not to this wrapper — a
   `display: contents` element has no box of its own, so opacity/transform
   set here would be silently ignored on desktop once the media query below
   takes over. */
.values__pillar {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--row-gap);
}

.values__pillar-heading {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--heading-gap);
  flex-shrink: 0;
  opacity: 0;
  will-change: opacity, transform;
}

.values__pillar-title {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  font-size: calc(26.51px + 13.49px * var(--t));
  line-height: 1;
  color: var(--color-green);
  white-space: nowrap;
}

.values__pillar-category {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 400;
  font-size: calc(14.58px + 7.42px * var(--t));
  line-height: 1;
  color: var(--color-gold);
  white-space: nowrap;
}

.values__pillar-body {
  margin: 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 400;
  font-size: calc(13px + 7px * var(--t));
  line-height: 1.4;
  color: var(--color-green);
  opacity: 0;
  will-change: opacity, transform;
}

/* Fades in together with the pillar directly below it (js/values-scroll.js)
   so a line never sits above an still-invisible row. */
.values__divider {
  width: 100%;
  height: 1px;
  background: rgba(22, 64, 63, 0.35);
  opacity: 0;
  will-change: opacity;
}

/* Structural switch to Figma's desktop side-by-side row. This is a CSS Grid,
   not flexbox, specifically so the label column gets ONE shared width across
   all 3 rows: a `max-content` grid track is sized to the single widest cell
   in that column (whichever of "Protect"/"Preserve"/"Conserve" or their
   category labels is widest — direct feedback: "Nutze den dritten Punkt als
   Start, die beiden darüber müssen sich dem anpassen"). A `flex: 0 0 %`
   column (the earlier approach) can't do this: flexbox's default
   min-width:auto forces each row's OWN label column to grow past its % basis
   whenever ITS OWN content needs more room, independently per row — so
   different rows silently ended up with different column widths, and the
   body text's left edge drifted row to row. A shared grid column can't drift
   like that by construction.
   `display: contents` on .values__pillar unwraps it so its heading/body
   children land directly in this grid (a `display: contents` parent doesn't
   break the grid's column-sharing the way an intermediate flex/block box
   would). align-items: center restores Figma's own vertical centering
   (items-center) of the label block against the body text — direct
   feedback asked for this back after the earlier flex-start-only fix, now
   that the actual horizontal drift is fixed at its root cause instead. */
@media (min-width: 761px) {
  .values__pillars {
    display: grid;
    grid-template-columns: max-content 1fr;
    align-items: center;
    row-gap: var(--pillars-gap);
    column-gap: var(--row-gap);
  }

  .values__pillar {
    display: contents;
  }

  .values__divider {
    grid-column: 1 / -1;
  }
}

/* ---------- Testimonials ---------- */

/* No pin/scroll animation (unlike .challenges/.solution/.values) — this is
   a single static block. Per direct feedback, Figma's absolute gap to the
   Values section above is ignored; instead `min-height: 100vh` + flex
   centering guarantees the title+card always sit centered within a full
   viewport of their own, so scrolling to a resting stop anywhere past this
   section's top edge shows ONLY testimonial content — Values above (and,
   once built, Final CTA below) is already fully off-screen. A fixed
   fluid padding alone (the previous approach) couldn't guarantee that: on
   short-content viewports the padding could be smaller than the viewport
   itself, leaving the previous section's tail visible above the fold
   alongside this one's "centered" card. */
.testimonials {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(32px + 20px * var(--t));
  box-sizing: border-box;
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  padding: calc(64px + 32px * var(--t)) 0;
}

/* Width is deliberately narrower than the section on mobile (283px, per
   Figma's mobile frame) so the title wraps to 2 lines there; fluidly widens
   past the text's natural desktop width so it stays a single line at 1512px
   without needing a hard breakpoint switch. */
.testimonials__title {
  margin: 0 auto;
  width: calc(283px + 617px * var(--t));
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  font-size: calc(22px + 18px * var(--t));
  line-height: 1.2;
  color: var(--color-green);
  text-align: center;
}

/* Wraps the card(s) so a future 2nd/3rd testimonial (see CLAUDE.md) can
   become a row/carousel here without touching .testimonials itself. */
.testimonials__track {
  display: flex;
  justify-content: center;
}

.testimonial-card {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: calc(10.758px + 5.242px * var(--t));
  width: calc(353px + 172px * var(--t));
  padding: calc(28.24px + 13.76px * var(--t)) calc(39.67px + 19.33px * var(--t)) calc(34.291px + 16.709px * var(--t));
  border: calc(0.672px + 0.328px * var(--t)) solid var(--color-gold);
  border-radius: calc(16.81px + 8.19px * var(--t));
  background: linear-gradient(180deg, var(--color-green-panel-start) 0%, var(--color-green-panel-end) 100%);
  box-shadow: calc(2.017px + 0.983px * var(--t)) calc(2.017px + 0.983px * var(--t)) calc(4.74px + 2.31px * var(--t)) rgba(11, 30, 22, 0.3);
}

.testimonial-card__quote-mark {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(29.585px + 14.415px * var(--t));
  line-height: 1;
  color: rgba(234, 185, 92, 0.9);
}

.testimonial-card__quote {
  margin: 0;
  font-family: "Unbounded", sans-serif;
  font-weight: 500;
  font-size: calc(13.448px + 6.552px * var(--t));
  line-height: 1.3;
  color: var(--color-white);
}

.testimonial-card__body {
  margin: 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 300;
  font-size: calc(9.413px + 4.587px * var(--t));
  line-height: 1.4;
  color: var(--color-white);
}

.testimonial-card__divider {
  width: 100%;
  height: 1px;
  background: rgba(255, 255, 255, 0.25);
}

.testimonial-card__author {
  margin: 0;
  font-family: "DM Sans", sans-serif;
  font-weight: 300;
  font-size: calc(9.413px + 4.587px * var(--t));
  line-height: 1.5;
  color: var(--color-white);
}

.testimonial-card__author strong {
  font-weight: 500;
}

/* ---------- Final CTA ---------- */

.final-cta {
  position: relative;
  overflow: hidden;
  width: 100%;
  max-width: 1512px;
  margin: 0 auto;
  /* Extra top gap is desktop-only (0 at mobile's t=0, unchanged there per
     direct feedback — only desktop needed more breathing room against the
     previous (Testimonials) section). Bottom gap matches Figma's own frame
     exactly (measured off the section's real rendered height: footer bottom
     sits only ~10px above the desktop frame's edge, ~20px on mobile) — an
     earlier guess (56px/96px) left a noticeably oversized plain-gold gap
     below the footer once the fade below was fixed to reach this section's
     true bottom edge (see .final-cta__fade), since the fade fix made that
     excess padding visible as solid color instead of hiding it under a
     premature fade cutoff. */
  padding: calc(80px * var(--t)) 0 calc(20px - 10px * var(--t));
}

/* Same gradient stops as .hero__fade, reversed (cream at the top instead of
   gold), so the gold only appears near the very bottom of the page — behind
   the footer — instead of at the section's own top edge. Figma achieves this
   with a rotate(180deg) on an identical gradient div; writing the stops
   reversed directly is equivalent and avoids the rotate+flex-centering
   wrapper it uses. Sits behind the normal-flow content below purely via DOM
   order (no z-index needed): a position:absolute box with z-index:auto
   paints before any later, non-positioned sibling in the same stacking
   context.

   `bottom: 0` instead of Figma's fixed 755px/982px height: Figma's own fade
   box ends a bit short of this section's true rendered bottom (its padding-
   bottom included), which left a plain cream strip between the gold fade and
   the page's actual last pixel — the page never actually finished "on gold"
   the way direct feedback asked for. Stretching the fade all the way to this
   section's own bottom edge (its nearest positioned ancestor) guarantees the
   last color painted on the page is always solid gold, on both breakpoints,
   regardless of exactly how tall the content above ends up being. */
.final-cta__fade {
  position: absolute;
  left: 0;
  width: 100%;
  top: calc(138px + 253px * var(--t));
  bottom: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    #F8F3E9 0%,
    rgba(248, 243, 233, 0.8) 9.615%,
    rgba(248, 243, 233, 0.6) 19.63%,
    #EAB95C 87.567%,
    #EAB95C 100%);
}

.final-cta__title {
  position: relative;
  margin: 0 auto;
  width: calc(353px + 712px * var(--t));
  font-family: "Unbounded", sans-serif;
  font-weight: 600;
  /* Restored to the original Figma-matched size — js/final-cta-fit.js now
     handles the cross-browser wrap risk at runtime (measuring the ACTUAL
     rendered width on whatever browser is currently loaded and shrinking
     only if it truly doesn't fit), instead of a blanket static reduction
     here that shrank it even in browsers where it was never necessary. */
  font-size: calc(21.5px + 38.5px * var(--t));
  line-height: 1.2;
  color: var(--color-green);
  text-align: center;
}

.final-cta__intro,
.final-cta__body {
  position: relative;
  margin: 0 auto;
  width: calc(353px + 712px * var(--t));
  font-family: "DM Sans", sans-serif;
  font-weight: 500;
  font-size: calc(15px + 19px * var(--t));
  line-height: 1.3;
  color: var(--color-green);
  text-align: left;
}

.final-cta__intro {
  margin-top: calc(26px + 26px * var(--t));
}

.final-cta__intro p {
  margin: 0 0 1em;
}

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

.final-cta__highlight {
  color: var(--color-gold);
}

.final-cta__divider {
  position: relative;
  margin: calc(28px + 24px * var(--t)) auto 0;
  width: calc(353px + 712px * var(--t));
  height: 1px;
  background: rgba(22, 64, 63, 0.35);
}

.final-cta__body {
  margin-top: calc(28px + 24px * var(--t));
}

.final-cta__body p {
  margin: 0 0 calc(21px + 12px * var(--t));
}

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

.final-cta__body em {
  font-style: italic;
}

/* Matches .solution__cta's own mobile/desktop button-width technique
   exactly (full-width pill below 761px, fit-content pill above it). */
.final-cta__cta {
  position: relative;
  margin: calc(48px + 30px * var(--t)) auto 0;
  width: calc(353px + 347px * var(--t-mobile-side));
}

@media (min-width: 761px) {
  .final-cta__cta {
    width: fit-content;
  }
}

.final-cta__cta .cta-button {
  margin: 0;
}

.final-cta__footer {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: calc(133px - 5px * var(--t)) auto 0;
  width: calc(353px + 881px * var(--t));
}

.final-cta__footer-text {
  font-family: "DM Sans", sans-serif;
  font-weight: 600;
  font-size: calc(9px + 7px * var(--t));
  line-height: 1.3;
  color: #000000;
}

.final-cta__footer-text p {
  margin: 0;
}

.final-cta__footer-text strong {
  font-weight: 500;
}

.final-cta__logo {
  flex-shrink: 0;
  width: calc(77px + 28px * var(--t));
  height: auto;
}
