/* ============================================================
   AMALAB — benefits-journey.css (V9)
   Value-proposition benefits journey (index.html, inside the
   hero cover-reveal scene). One full-viewport stage pinned for
   the whole sequence:
   - The title stays at the top the whole time. It opens on the
     positioning line ("Nous faisons évoluer…") and, the instant
     the frame starts moving, crossfades to the promise ("Nous
     utilisons l'IA…"), which then holds.
   - The frame's travel to its resting slot is locked to the first
     morph: as it glides down it scrubs clip 0, landing already at
     the end of the first video (state 1). Two further morphs then
     scrub between the remaining states while the benefit copy
     reels vertically on the right, SCALED-style — no dwell freeze,
     so the scrub tracks scroll naturally.
   Motion communicates one causality throughout: dispersed usage
   becomes a structured system.
   Degrades: reduced motion → static two-column layout, posters
   swapped per benefit; no JS → stacked layout, state-0 poster.
   ============================================================ */

/* Paper remains the full-band surface. The near-white media is blended into
   it so the footage reads as part of the page without turning white into a
   section background (white is reserved for cards). */
.benefits-scene { background: var(--bg); }
.reveal-scene.is-active .reveal-cover.benefits-scene { background: var(--bg); }

/* ---------- Pin: scroll room for the travel + three morphs ----------
   Deep enough that each system morph unfolds over a full stretch of scroll
   — the change must be watchable, never a snap between two states.
   Pace metric = pin RANGE (height − viewport) per timeline unit: 220svh
   gives 120svh range / 3.64 units ≈ 33svh per unit (was 55). Guardian
   floor for video morphs — tighten further only by trading away the
   dwells (panel precedent), not by compressing the morphs. */
.benefits-pin { height: calc(220svh - var(--nav-h)); }
.benefits-viewport {
  position: sticky;
  top: var(--nav-h);
  height: calc(100vh - var(--nav-h));
  height: calc(100svh - var(--nav-h));
  overflow: hidden; /* the travelling frame must never spill on neighbours */
  display: flex;
  flex-direction: column; /* persistent title on top, media row fills below */
}
.benefits-layout {
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* ---------- Head — two stacked titles, a hand-off as the video moves --------
   The title block stays at the top of the stage for the whole journey. At rest
   BOTH lines show: line A (the positioning line) full size on top, line B (the
   promise) smaller below. The instant the frame starts moving, line A fades
   away while line B grows to line A's size and holds — a hand-off, not a swap.
   Both reserve their full-size box (line B scales via transform), so the header
   height never shifts. */
.benefits-head {
  flex: none;
  padding-top: clamp(14px, 3svh, 32px);
  padding-bottom: clamp(8px, 1.6svh, 18px);
  padding-inline: var(--gutter);
  text-align: center;
}
.benefits-title {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: clamp(1.625rem, 1.25rem + 1.55vw, 2.5rem);
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin-inline: auto;
  max-width: 40ch;
}
.benefits-line {
  margin: 0;
  text-wrap: balance;
  will-change: opacity, transform;
}
/* Line A opens as the main title, then disappears the instant the video
   starts moving (gone by --tp 0.5, with a slight lift). */
.benefits-line--a {
  opacity: clamp(0, calc(1 - var(--tp, 0) * 2), 1);
  transform: translateY(calc(var(--tp, 0) * -10px));
}
/* Line B sits below, smaller, and grows to line A's size as the transition
   runs — the two share one max size, so B lands exactly where A was in scale. */
.benefits-line--b {
  margin-top: clamp(14px, 3svh, 34px);
  transform: scale(calc(0.6 + var(--tp, 0) * 0.4));
  transform-origin: center top;
}

/* ---------- Scroll-linked arrival ----------
   js/benefits-journey.js writes --arr (0→1) while the stage enters the
   viewport, spilling ~15% into the pin: the title settles first, the frame
   follows, and the first morph is already running as the entrance finishes —
   motion is underway before the pin engages and the seam into the journey is
   continuous. Scrubbed from scroll (reversible), so there is no transition
   here. var(--arr, 1) defaults to the settled state: no-JS, reduced motion
   (engine adds .is-static and never writes the var) and deep links all
   render the stage in place. */
html.js .benefits-pin:not(.is-static) .benefits-head {
  --arr-head: clamp(0, calc(var(--arr, 1) / 0.55), 1);
  opacity: var(--arr-head);
  transform: translateY(calc((1 - var(--arr-head)) * 24px));
}
html.js .benefits-pin:not(.is-static) .benefits-stage {
  --arr-stage: clamp(0, calc((var(--arr, 1) - 0.18) / 0.72), 1);
  opacity: var(--arr-stage);
  transform: translateY(calc((1 - var(--arr-stage)) * 34px));
}

/* ---------- Layout — frame left, benefit reel right ----------
   The grid fills the space under the persistent header and centres the media
   row in it (align-content), so the parked frame sits at the middle of that
   space — the "middle" the video travels to — with the reel on its axis. */
.benefits-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(32px, 5vw, 80px);
  align-content: center;
  align-items: center;
  width: 100%;
  /* lift the media row a touch above dead-centre so it reads more central
     against the persistent header on top */
  transform: translateY(calc(-1 * clamp(8px, 3svh, 36px)));
}

/* ---------- Stage : the travelling frame ----------
   The stage box is exactly the frame, so it centres the frame on the media
   row = reel middle. Capped so a full-size (parked) frame always clears the
   persistent header. */
.benefits-stage {
  /* Floored — see .sh-stage-inner: a short landscape window drives the
     subtraction below zero and collapses the stage to a sliver. */
  width: min(100%, max(160px, calc(100svh - var(--nav-h) - 340px)));
  margin-inline: auto;
}
.benefits-frame {
  position: relative;
  aspect-ratio: 1;
  background: #fff;
  border-radius: var(--radius-card);
  /* true white ground so the near-white footage reads on white, not on the
     off-white Paper band (Paper behind multiply looked like a grey mask) */
  overflow: hidden;
  transform-origin: center;
  will-change: transform;
}
.benefits-frame video,
.benefits-frame .benefits-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* HARD CUT — same rationale as the SCALED journey: the engine parks
     every inactive clip on its shared boundary frame, so at the swap
     moment only one clip is on-screen and there is nothing to crossfade
     (a blend would expose re-encode drift as flicker). */
  transition: none;
}
.benefits-frame video.is-active,
.benefits-frame .benefits-poster.is-active { opacity: 1; }

/* Source footage is baked on #FBFBFB, not true white. Brightness lifts the
   near-white ground to true #FFF so the video merges with the white frame,
   while leaving the dark network graphics (orange / teal / navy lines)
   untouched (0 * f = 0). Previously this was paired with mix-blend-mode:
   multiply to mask the grey, but multiply forced a per-pixel-per-frame
   composite over the white frame — dropped to remove the hot-spot during
   scroll-scrub. Brightness alone resolves the near-white ground. */
.benefits-frame video { filter: brightness(1.02); }

/* Without JS the frame still shows the starting state */
html:not(.js) .benefits-frame .benefits-poster--0 { opacity: 1; }

/* ---------- Docking: the benefit reel appears once the frame has parked ----
   The reel fades in as the frame lands (state 1 reached) so the three results
   reveal themselves once the first one has formed. */
.benefits-copy {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 360ms var(--ease-out), transform 360ms var(--ease-out);
}
.benefits-pin.is-docked .benefits-copy {
  opacity: 1;
  transform: none;
}

/* ---------- Benefit reel — SCALED-style vertical word carousel ----------
   One masked window on the right; the three benefit texts sit in a single
   column and slide like a reel, driven from JS with the exact same eased
   progress that scrubs the video, so the text change and the system morph
   read as one motion. Typography only — no cards, no eyebrows. */
.benefits-copy { min-width: 0; }
.benefits-reel {
  --reel-h: clamp(250px, 38svh, 350px);
  height: var(--reel-h);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 4%, #000 20%, #000 80%, transparent 96%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, transparent 4%, #000 20%, #000 80%, transparent 96%, transparent 100%);
}
.benefits-reel-track {
  display: flex;
  flex-direction: column;
  will-change: transform;
}
.benefits-item {
  height: var(--reel-h);
  flex: none;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.benefits-item h3 {
  font-size: clamp(2rem, 1.55rem + 1.9vw, 3rem);
  line-height: 1.1;
  margin-bottom: var(--s-5);
  max-width: 18ch;
}
.benefits-item p {
  font-size: var(--step-lead);
  line-height: 1.55;
  max-width: 44ch;
}

/* Short windows (reduced laptop window): the pinned composition shares
   100svh − nav with the two-line head above it, and the 250px reel floor
   is what pushed the active benefit past the viewport at ≤~620px heights.
   The floor follows the viewport and the type steps down so a full
   benefit (h3 + p) still fits inside the smaller reel window. */
@media (max-height: 620px) {
  .benefits-reel { --reel-h: clamp(150px, 34svh, 250px); }
  .benefits-item h3 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); }
  .benefits-item p { font-size: var(--step-body); }
}

/* V24.2: .benefits-closing / .benefits-closing-hero rules removed with their
   markup — the closing statement « L'IA doit… » was cut from the home; its
   dot field moved to the CTA hero band (closing-field.css/js). */

/* ---------- Static layout : no JS, or reduced motion (JS adds .is-static) ----------
   The pin collapses to normal flow: head on top, then the two columns —
   sticky poster frame left, the three benefits stacked as a plain list
   right. Under reduced motion JS swaps the posters per benefit. */
html:not(.js) .benefits-pin,
.benefits-pin.is-static { height: auto; }
html:not(.js) .benefits-viewport,
.benefits-pin.is-static .benefits-viewport {
  position: static;
  height: auto;
  overflow: visible;
  display: block;
}
html:not(.js) .benefits-head,
.benefits-pin.is-static .benefits-head {
  padding-inline: 0;
  padding-block: 0;
  margin-bottom: var(--s-8);
}
/* static/no-JS shows only the promise line, held visible */
html:not(.js) .benefits-line--a,
.benefits-pin.is-static .benefits-line--a { display: none; }
html:not(.js) .benefits-line--b,
.benefits-pin.is-static .benefits-line--b { opacity: 1; transform: none; }
html:not(.js) .benefits-grid,
.benefits-pin.is-static .benefits-grid { display: block; }
html:not(.js) .benefits-stage,
.benefits-pin.is-static .benefits-stage {
  position: sticky;
  top: calc(var(--nav-h) + 24px);
}
html:not(.js) .benefits-frame,
.benefits-pin.is-static .benefits-frame { transform: none !important; }
html:not(.js) .benefits-copy,
.benefits-pin.is-static .benefits-copy {
  margin-top: var(--s-6);
  opacity: 1;
  transform: none;
  transition: none;
}
html:not(.js) .benefits-reel,
.benefits-pin.is-static .benefits-reel {
  height: auto;
  -webkit-mask-image: none;
  mask-image: none;
}
html:not(.js) .benefits-reel-track,
.benefits-pin.is-static .benefits-reel-track {
  transform: none !important;
  gap: clamp(64px, 12vh, 140px);
  padding-block: var(--s-6);
}
html:not(.js) .benefits-item,
.benefits-pin.is-static .benefits-item {
  height: auto;
  display: block;
  border-top: 1px solid var(--border);
  padding-top: var(--s-5);
  transition: opacity 360ms var(--ease-out);
}
html.js .benefits-pin.is-static .benefits-item.is-muted { opacity: 0.35; }

/* ---------- Tablet / mobile : single column inside the pinned stage ---------- */
@media (max-width: 900px) {
  .benefits-pin { height: calc(220svh - var(--nav-h)); }
  .benefits-head { padding-top: clamp(6px, 1.5svh, 14px); padding-bottom: clamp(4px, 1svh, 10px); }
  .benefits-title { font-size: clamp(1.125rem, 4.5vw, 1.375rem); }
  .benefits-grid {
    grid-template-columns: 1fr;
    gap: clamp(10px, 2svh, 18px);
  }
  .benefits-stage { width: min(60vw, 260px); }
  .benefits-reel { --reel-h: clamp(150px, 24svh, 200px); }
  .benefits-item { text-align: center; align-items: center; }
  .benefits-item h3 { font-size: clamp(1.5rem, 5.5vw, 2rem); margin-bottom: var(--s-4); }
  .benefits-item p { font-size: var(--step-body); }

  /* static layout keeps the compact stage on top, list below */
  html:not(.js) .benefits-stage,
  .benefits-pin.is-static .benefits-stage {
    position: static;
    width: min(100%, 300px);
  }
  html:not(.js) .benefits-item,
  .benefits-pin.is-static .benefits-item {
    text-align: left;
    align-items: initial;
  }
}

/* ---------- Reduced motion : no travel, no scrubbing, no fades ---------- */
@media (prefers-reduced-motion: reduce) {
  .benefits-frame video { display: none; }
  .benefits-head,
  .benefits-copy,
  .benefits-reel-track,
  .benefits-frame,
  .benefits-frame .benefits-poster,
  .benefits-item { transition: none; }
}
