/* ============================================================
   G.E.T. Énergie — styles
   Inspired by terminal-industries.com — clean, white, minimal,
   bold typography with subtle brand color accents.
   ============================================================ */

/* ---------- Cinematic intro ---------- */

.intro-active { overflow: hidden; }

.intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}

/* Lightning-shaped seam — same zigzag for both halves so edges meet
   exactly. Each half covers the FULL viewport, then is clipped to its
   side of the bolt; sliding them apart preserves the jagged edge. */
.intro-half {
  position: fixed;
  inset: 0;
  /* Width/height are intentionally omitted — `inset: 0` already
     pins all four edges to the viewport. Adding `width: 100vw`
     would overflow horizontally under the proportional page
     zoom (vw values bypass the zoom adjustment). */
  background: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: transform, clip-path;
}

/* Both halves overlap by 0.3% at the seam so sub-pixel rounding
   under the page zoom never opens a visible gap between them. */
.intro-left {
  clip-path: polygon(
    0 0,
    50.3% 0,
    44.3% 18%,
    54.3% 21%,
    40.3% 43%,
    56.3% 46%,
    42.3% 69%,
    52.3% 72%,
    50.3% 100%,
    0 100%
  );
}

.intro-right {
  clip-path: polygon(
    100% 0,
    100% 100%,
    49.7% 100%,
    51.7% 72%,
    41.7% 69%,
    55.7% 46%,
    39.7% 43%,
    53.7% 21%,
    43.7% 18%,
    49.7% 0
  );
}

.intro-content {
  position: relative;
  z-index: 2;
  width: min(720px, 86vw);
  text-align: center;
}

/* ---------- Decorative shapes inside the intro ---------- */

.intro-shapes {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
}

.intro-shape {
  position: absolute;
  display: block;
  border: 1px solid var(--line);
  border-radius: 160px;
  opacity: 0;
  will-change: transform, opacity;
  transition:
    opacity 1.2s var(--ease),
    transform 1.4s cubic-bezier(.2,.7,.2,1);
}

.intro-shape.s1 {
  top: -220px;  left: -260px;
  width: 680px; height: 400px;
  transform: translate(-90px, -90px);
  transition-delay: 0.10s;
}
.intro-shape.s2 {
  top: -160px;  right: -300px;
  width: 780px; height: 360px;
  border-radius: 200px;
  transform: translate(100px, -90px);
  transition-delay: 0.20s;
}
.intro-shape.s3 {
  top: 28%;     right: -360px;
  width: 540px; height: 420px;
  border-radius: 140px;
  transform: translate(110px, 0);
  transition-delay: 0.30s;
}
.intro-shape.s4 {
  top: 50%;     left: -380px;
  width: 600px; height: 380px;
  border-radius: 180px;
  transform: translate(-110px, 0);
  transition-delay: 0.40s;
}
.intro-shape.s5 {
  bottom: -240px; left: -280px;
  width: 720px;   height: 400px;
  border-radius: 180px;
  transform: translate(-90px, 90px);
  transition-delay: 0.50s;
}
.intro-shape.s6 {
  bottom: -320px; right: -220px;
  width: 620px;   height: 380px;
  border-radius: 160px;
  transform: translate(90px, 90px);
  transition-delay: 0.60s;
}

/* When the intro plays, shapes animate in alongside the logo (logo
   fades 0.05–0.60s; shape staggered delays span 0.10–0.60s after start). */
.intro-play .intro-shape {
  opacity: 1;
  transform: translate(0, 0);
}

.intro-logo {
  width: min(520px, 70vw);
  height: auto;
  display: block;
  /* Nudge right to compensate for the logo's left-leaning visual mass */
  margin: 0 auto 36px;
  opacity: 0;
  transform: translateX(38px) scale(0.94);
}


.intro-tag {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.005em;
  margin: 0;
  font-weight: 400;
  font-size: clamp(20px, 2.6vw, 36px);
  line-height: 1.15;
  color: var(--ink);
  overflow: hidden;
}

.intro-tag-line {
  display: block;
  transform: translateY(110%);
  opacity: 0;
}
.intro-tag-line.muted {
  color: var(--ink-soft);
  margin-top: 10px;
  font-size: 0.86em;
}

@media (max-width: 720px) {
  .intro-half {
    background: #f3f4f4;
  }

  .intro-content {
    width: min(330px, 82vw);
    transform: translateY(4svh);
  }

  .intro-logo {
    width: min(290px, 68vw);
    margin-bottom: 30px;
    transform: translateX(20px) scale(0.94);
  }

  .intro-tag {
    font-size: clamp(18px, 5vw, 24px);
    line-height: 1.2;
  }

  .intro-tag-line.muted {
    margin-top: 12px;
    font-size: 0.76em;
    line-height: 1.28;
  }

  .intro-shapes::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 430 932' preserveAspectRatio='none'%3E%3Cg fill='none' stroke='%23d3d6d5' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round' opacity='.86'%3E%3Cpath d='M-34 116H124C158 116 178 130 202 154L332 284C360 312 403 294 403 254V144C403 116 422 98 450 98H468'/%3E%3Cpath d='M-48 708H126C162 708 184 748 162 778L-34 1034'/%3E%3Cpath d='M466 766H350C318 766 300 784 300 816V978'/%3E%3C/g%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }

  .intro-shape {
    display: none;
  }

  .intro-shapes::after {
    display: none;
  }

  .intro-play .intro-logo {
    animation:
      introLogoInMobile 0.55s var(--ease) 0.05s forwards,
      introLogoLoopMobile 3.2s ease-in-out 0.9s infinite;
  }

  @keyframes introLogoInMobile {
    to { opacity: 1; transform: translateX(20px) scale(1); }
  }

  @keyframes introLogoLoopMobile {
    0%, 100% {
      transform: translateX(20px) scale(1);
      filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.06));
    }
    50% {
      transform: translateX(20px) scale(1.025);
      filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.18));
    }
  }
}

/* Lightning bolt SVG — full viewport, follows the seam exactly */
.intro-bolt {
  position: fixed;
  inset: 0;
  /* See .intro-half: inset alone covers the viewport, vw/vh
     would overflow under page zoom. */
  opacity: 0;
  pointer-events: none;
}

.intro-bolt-path,
.intro-bolt-core {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}

@media (max-width: 720px) {
  .intro-bolt {
    display: none;
  }

  .intro-bolt-path {
    stroke-width: 0.55;
    filter: none;
    opacity: 0.82;
  }

  .intro-bolt-core {
    stroke-width: 0.18;
    opacity: 0.72;
  }
}

/* White flash overlay at the moment of impact */
.intro-flash {
  position: fixed;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  pointer-events: none;
}

/* ---------- Animation sequence (triggered by .intro-play) ----------
   0.05 → 0.60s : logo fades + scales in
   0.55 → 1.25s : tagline lines slide up (staggered)
   1.25 → 3.25s : reading pause (≈ 2s after the phrase is fully visible)
   3.25 → 4.10s : lightning bolt draws (slow, dramatic)
   3.25 → 4.35s : bolt glow lingers
   3.95 → 4.65s : white flash peaks
   4.20 → 6.00s : two halves slide apart along the bolt seam
   ~6.20s       : overlay removed
*/

.intro-play .intro-logo {
  animation:
    introLogoIn 0.55s var(--ease) 0.05s forwards,
    introLogoLoop 3.2s ease-in-out 0.9s infinite;
}

/* Continuous breathing pulse — runs throughout the intro so the
   logo never feels static. Scale + a neutral dark shadow that
   deepens with the zoom, no coloured glow. */
@keyframes introLogoLoop {
  0%, 100% {
    transform: translateX(38px) scale(1);
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.06));
  }
  50% {
    transform: translateX(38px) scale(1.03);
    filter: drop-shadow(0 14px 28px rgba(0, 0, 0, 0.22));
  }
}

.intro-play .intro-tag-line:nth-child(1) {
  animation: introTagIn 0.55s cubic-bezier(.2,.7,.2,1) 0.55s forwards;
}
.intro-play .intro-tag-line:nth-child(2) {
  animation: introTagIn 0.55s cubic-bezier(.2,.7,.2,1) 0.70s forwards;
}

.intro-play .intro-bolt        { animation: introBoltFlash 0.90s var(--ease) 1.85s forwards; }
.intro-play .intro-bolt-path   { animation: introBoltDraw 0.70s cubic-bezier(.4,.1,.3,1) 1.85s forwards; }
.intro-play .intro-bolt-core   { animation: introBoltDraw 0.70s cubic-bezier(.4,.1,.3,1) 1.85s forwards; }

.intro-play .intro-flash       { animation: introFlash 0.60s var(--ease) 2.40s forwards; }

/* Each half shakes during the flash, then swings + slides outward.
   Shake: 2.30 → 2.80 s (overlaps with white flash).
   Slide: 2.80 → 5.00 s — long enough for the 16° rotation to read
   clearly. Easing pulled toward linear so the rotation is visible
   throughout, not bunched at the start. */
.intro-play .intro-left        {
  animation:
    introShakeLeft 0.50s ease-out 2.30s,
    introSlideLeft 2.20s cubic-bezier(.4, 0, .3, 1) 2.80s forwards;
}
.intro-play .intro-right       {
  animation:
    introShakeRight 0.50s ease-out 2.30s,
    introSlideRight 2.20s cubic-bezier(.4, 0, .3, 1) 2.80s forwards;
}

@keyframes introLogoIn {
  0%   { opacity: 0; transform: translateX(38px) scale(0.94); }
  100% { opacity: 1; transform: translateX(38px) scale(1); }
}

@keyframes introTagIn {
  0%   { opacity: 0; transform: translateY(110%); }
  100% { opacity: 1; transform: translateY(0); }
}

@keyframes introBoltDraw {
  0%   { stroke-dashoffset: 100; }
  100% { stroke-dashoffset: 0; }
}

@keyframes introBoltFlash {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes introFlash {
  0%   { opacity: 0; }
  35%  { opacity: 0.9; }
  100% { opacity: 0; }
}

/* Mirror-shake — each half oscillates in opposite directions for
   ~500 ms, simulating an electrical "rattle" right at the moment
   of the bolt strike. Amplitudes bumped up so the tremor is clearly
   visible before the slide takes over. */
@keyframes introShakeLeft {
  0%   { transform: translateX(0)    rotate(0deg); }
  15%  { transform: translateX(-8px) rotate(-0.7deg); }
  30%  { transform: translateX(7px)  rotate(0.6deg); }
  45%  { transform: translateX(-6px) rotate(-0.5deg); }
  60%  { transform: translateX(5px)  rotate(0.4deg); }
  75%  { transform: translateX(-3px) rotate(-0.2deg); }
  90%  { transform: translateX(2px)  rotate(0.1deg); }
  100% { transform: translateX(0)    rotate(0deg); }
}

@keyframes introShakeRight {
  0%   { transform: translateX(0)    rotate(0deg); }
  15%  { transform: translateX(8px)  rotate(0.7deg); }
  30%  { transform: translateX(-7px) rotate(-0.6deg); }
  45%  { transform: translateX(6px)  rotate(0.5deg); }
  60%  { transform: translateX(-5px) rotate(-0.4deg); }
  75%  { transform: translateX(3px)  rotate(0.2deg); }
  90%  { transform: translateX(-2px) rotate(-0.1deg); }
  100% { transform: translateX(0)    rotate(0deg); }
}

/* Slide + outward rotation — the half swings open like a door
   while accelerating off-screen. translateX(±115vw) overshoots the
   viewport edge so the 16°-rotated half is fully clear. */
@keyframes introSlideLeft {
  0%   { transform: translateX(0)      rotate(0deg); }
  100% { transform: translateX(-115vw) rotate(-16deg); }
}

@keyframes introSlideRight {
  0%   { transform: translateX(0)     rotate(0deg); }
  100% { transform: translateX(115vw) rotate(16deg); }
}

.intro.done { display: none; }

/* ---------- Fast intro variant (inter-page nav: bolt + slide only) ----------
   Used when navigating between pages within the same session. Skips the
   logo + tagline reveal and runs only the lightning bolt and the two
   halves sliding apart, in ~2.75s total. */
.intro-fast .intro-content,
.intro-fast .intro-shapes {
  display: none;
}

.intro-fast.intro-play .intro-bolt        { animation: introBoltFlash 0.95s var(--ease) 0s forwards; }
.intro-fast.intro-play .intro-bolt-path   { animation: introBoltDraw 0.65s cubic-bezier(.4,.1,.3,1) 0s forwards; }
.intro-fast.intro-play .intro-bolt-core   { animation: introBoltDraw 0.65s cubic-bezier(.4,.1,.3,1) 0s forwards; }

.intro-fast.intro-play .intro-flash       { animation: introFlash 0.55s var(--ease) 0.55s forwards; }

.intro-fast.intro-play .intro-left        {
  animation:
    introShakeLeft 0.45s ease-out 0.45s,
    introSlideLeft 2.00s cubic-bezier(.4, 0, .3, 1) 0.90s forwards;
}
.intro-fast.intro-play .intro-right       {
  animation:
    introShakeRight 0.45s ease-out 0.45s,
    introSlideRight 2.00s cubic-bezier(.4, 0, .3, 1) 0.90s forwards;
}

/* Intro reduced-motion is handled by JS (gentle fade instead of
   bolt animation). Don't hide it with CSS — let the logo appear. */


:root {
  --bg: #ffffff;
  --bg-alt: #f6f6f4;
  --bg-dark: #0d0d0d;
  --ink: #0a0a0a;
  --ink-soft: #4a4a4a;
  --ink-mute: #8a8a8a;
  --line: #e5e5e2;
  --line-dark: #1f1f1f;

  --accent-blue: #21abe8;
  --accent-green: #7ed957;
  --accent-red: #ff3b30;
  --accent-yellow: #ffd60a;

  --max: 1320px;
  --pad: clamp(20px, 4vw, 56px);

  --font-display: "League Gothic", "Arial Narrow", Impact, sans-serif;
  --font-body: "Arimo", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --radius: 4px;
  --ease: cubic-bezier(.2, .7, .2, 1);

  /* ============================================================
     Proportional page scale on screens larger than the 16"
     baseline (1728 px logical = MacBook Pro 16" default). At
     1728 px viewport, --page-zoom = 1 (no change). Above that,
     the whole document is uniformly scaled so the design always
     reads at the proportions it was tuned for, no matter the
     monitor. The 16" experience itself stays untouched.
     JS that mixes window.innerHeight with getBoundingClientRect
     reads this var to keep both values in the same coordinate
     space (see script.js → getPageZoom).
     ============================================================ */
  --page-zoom: 1;

  /* Zoom-compensated viewport units. Under html { zoom: N }
     `100vw` still resolves to 100 % of the actual viewport in
     CSS pixels — those CSS pixels are then magnified by N, so
     a `width: 100vw` element overflows horizontally by the zoom
     factor (e.g. +120 % on a 4K monitor). These two custom props
     produce a value that, after the zoom magnification, renders
     at exactly one viewport edge-to-edge. Use them anywhere a
     full-bleed reference is required (`width`, the
     `calc(50% - 50vw)` container-escape trick, half-bleed
     `50vw` figures, etc.) — NOT inside clamp() text sizing,
     which is already proportionally scaled by zoom. */
  --vw100: calc(100vw / var(--page-zoom));
  --vh100: calc(100vh / var(--page-zoom));
}

/* The proportional page zoom is driven by JavaScript at runtime
   (see script.js → applyPageZoom). Chromium parses `zoom` strictly
   as <number> | <percentage> | normal, NOT as a CSS calc(), so a pure
   CSS implementation cannot scale fluidly with viewport width. JS
   sets both the inline `zoom` style on <html> and the --page-zoom
   custom property below, so any consumer (CSS calc, JS scroll math)
   can read the same source of truth. */

* { box-sizing: border-box; }

html {
  /* Native smooth-scroll is intentionally OFF — Lenis handles all
     programmatic + wheel smoothing (see script.js). Browsers that
     also apply scroll-behavior: smooth would fight Lenis and cause
     jitter on anchor jumps. The fallback below kicks in only when
     Lenis is unavailable. */
  scroll-padding-top: 96px;
  overflow-x: clip;
}

/* Fallback smooth-scroll for users who reach the site without JS
   (or before Lenis loads). Once Lenis takes over, it adds the
   `.lenis` class to <html>, which disables this rule. */
html:not(.lenis) {
  scroll-behavior: smooth;
}

/* Lenis applies these classes once the smooth-scroll engine is
   running. The transform-friendly stacking context they create can
   subtly affect sticky descendants — kept here as a hook for any
   targeted overrides we need to ship later. */
html.lenis {
  height: auto;
}
html.lenis.lenis-smooth {
  scroll-behavior: auto;
}
html.lenis.lenis-stopped { overflow: hidden; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

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

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

::selection { background: var(--accent-blue); color: #fff; }

/* ---------- Layout ---------- */

.container {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 var(--pad);
}

.section {
  padding: clamp(80px, 11vw, 160px) 0;
  border-top: 1px solid var(--line);
}

.section-dark {
  /* Aligned with the marquee + footer + advantage: every dark
     surface on the page now uses --ink so they read as one unified
     palette instead of two adjacent near-blacks (--bg-dark was
     #0d0d0d, --ink is #0a0a0a). */
  background: var(--ink);
  color: #fff;
  border-color: var(--line-dark);
}

/* ============================================================
   01 — Notre mission : full-bleed dark section with a looping
   skull-wireframe video background. The source mp4 is encoded
   as forward+reverse concatenated, so a plain `<video loop>`
   plays it as a seamless ping-pong indefinitely.
   ============================================================ */
.section-mission {
  /* Sticky-pinned so the next section (Services) scrolls UP over it as
     the user scrolls — the mission stays in the background while the
     services panel reveals over it. */
  position: sticky;
  top: 0;
  z-index: 0;
  background: var(--bg-dark);
  color: #fff;
  overflow: hidden;
  border-top: 0;
  /* 16:9 aspect, but capped at viewport height so the pinned section
     never overflows the screen — keeps the reveal clean on any viewport. */
  /* Under page zoom, `vw`/`vh` resolve in unzoomed CSS pixels and
     then get magnified — overshooting by the zoom factor. Use
     --vw100 / --vh100 so the 9 :16 mission stage stays viewport-
     bound on every monitor. */
  height: min(calc(0.5625 * var(--vw100)), var(--vh100));
  padding: 0;
  display: flex;
  align-items: center;
  /* Wavy top edge — two bays cut into the section. No fill element. */
  -webkit-clip-path: url(#sectionTopNotch);
          clip-path: url(#sectionTopNotch);
}

/* All sections that follow the pinned mission must render ABOVE it so
   they actually cover it as they scroll up. */
.section-mission ~ section {
  position: relative;
  z-index: 1;
}

/* Plain light sections (Projets) don't declare a background, so the
   sticky mission shows through. Give those a solid bg as a fallback.
   Sections that already define their own bg (section-dark, marquee,
   section-contact with --bg-alt) are excluded. */
.section-mission ~ section:not(.section-dark):not(.section-contact):not(.marquee) {
  background: var(--bg);
}

/* Services section gets the same wavy top edge, so it visually
   "matches" the mission top during the sticky reveal transition. */
#services {
  -webkit-clip-path: url(#sectionTopNotch);
          clip-path: url(#sectionTopNotch);
}

@media (min-width: 1101px) {
  #services {
    --services-bottom-notch: clamp(72px, 5.4vw, 104px);
    position: relative;
    overflow: hidden;
  }

  #services::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    z-index: 2;
    height: calc(var(--services-bottom-notch) + 2px);
    background: var(--bg);
    pointer-events: none;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 104' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 0C190 0 282 104 600 104C918 104 1010 0 1200 0V104H0Z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 104' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 0C190 0 282 104 600 104C918 104 1010 0 1200 0V104H0Z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
  }

  #services > .container {
    position: relative;
    z-index: 1;
  }

  #projets {
    border-top: 0;
  }
}

.mission-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Cover the 16:9 section frame exactly; since the source is also 16:9
     there's no actual crop, the full video stays visible. */
  object-fit: cover;
  z-index: 0;
  filter: contrast(1.05) brightness(0.65);
  pointer-events: none;
}

/* Vignette + bottom darken so the text sits comfortably */
.mission-overlay {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 85% 65% at center,
      rgba(0, 0, 0, 0.10) 0%,
      rgba(0, 0, 0, 0.55) 65%,
      rgba(0, 0, 0, 0.85) 100%);
  z-index: 1;
  pointer-events: none;
}

.mission-content {
  position: relative;
  z-index: 3;
  text-align: center;
}

/* Notched edge dividers — two light "bays" curve down (top) or up
   (bottom) into the dark section, framing the video like a stage
   curtain. The SVG paths are mirrored vertically between top/bottom. */
.mission-divider {
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  height: clamp(48px, 5.5vw, 88px);
  z-index: 2;
  pointer-events: none;
  line-height: 0;
}

.mission-divider-top    { top: 0; }
.mission-divider-bottom { bottom: 0; }

.mission-divider svg {
  display: block;
  width: 100%;
  height: 100%;
}

.mission-divider svg path {
  fill: var(--bg);
}

/* Centered numbered eyebrow — overrides the default flex-start
   alignment of .about-row-num so it sits centered with the title. */
.mission-eyebrow {
  justify-content: center;
  margin-left: auto;
  margin-right: auto;
}

.mission-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(36px, 5.8vw, 92px);
  line-height: 1.02;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: #fff;
  margin: 24px auto 32px;
  /* Wide enough that each manually-broken line stays on its own line
     and does not wrap into a 4th/5th line at common viewports. */
  max-width: 36ch;
}

.mission-title .mission-accent {
  color: var(--accent-green);
  display: inline-block;
}

.mission-title .mission-accent-dot {
  /* Tighten the period against the word — small visual nicety */
  margin-left: -0.05em;
}

.mission-lead {
  color: rgba(255, 255, 255, 0.82);
  font-size: clamp(16px, 1.35vw, 19px);
  line-height: 1.65;
  max-width: 760px;
  margin: 0 auto;
}

.section-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 28px;
}

.section-eyebrow.light { color: rgba(255,255,255,.55); }

.section-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 6.5vw, 92px);
  line-height: 0.95;
  letter-spacing: -0.01em;
  margin: 0 0 16px;
  text-transform: uppercase;
}

.section-title.light { color: #fff; }

.section-title.huge {
  font-size: clamp(64px, 13vw, 200px);
}

.section-title .accent {
  color: var(--accent-blue);
}

.section-head {
  margin-bottom: clamp(40px, 6vw, 80px);
  max-width: 900px;
}

.section-head.row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  max-width: none;
  flex-wrap: wrap;
}

/* ---------- Header ---------- */

.site-header {
  position: fixed;
  top: 24px;
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 28px);
  max-width: 860px;
  z-index: 50;
  pointer-events: none; /* let the inner pill take pointer events */
}

.header-inner {
  pointer-events: auto;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  height: 66px;
  padding: 8px 8px 8px 24px;
  border-radius: 16px;
  background: rgba(15, 16, 18, 0.26);
  border: 1px solid rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.05) inset,
    0 16px 40px -16px rgba(0, 0, 0, 0.4);
  color: #fff;
  transition: background .25s var(--ease), border-color .25s var(--ease);
}

.site-header.scrolled .header-inner {
  background: rgba(10, 11, 13, 0.46);
  border-color: rgba(255, 255, 255, 0.12);
}

.brand {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

.brand-logo {
  height: 36px;
  width: auto;
  display: block;
  /* Logo is black artwork — invert it for the dark floating nav */
  filter: invert(1);
}

.footer-logo {
  height: 72px;
  filter: none;
}

/* ---------- Inline nav (desktop) ---------- */

.nav-inline {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: 4px;
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.85);
}

.nav-inline > a,
.nav-inline .nav-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 8px 14px;
  border-radius: 999px;
  color: inherit;
  transition: color .2s var(--ease), background .2s var(--ease);
}

.nav-inline > a:hover,
.nav-inline .nav-trigger:hover,
.nav-inline .nav-item:hover .nav-trigger,
.nav-inline .nav-item:focus-within .nav-trigger,
.nav-inline .nav-item.is-open .nav-trigger {
  color: var(--accent-green);
  background: rgba(255, 255, 255, 0.04);
}

.nav-chev {
  font-size: 11px;
  opacity: 0.7;
  transform: translateY(-1px);
  transition: transform .25s var(--ease);
}

.nav-inline > a:hover .nav-chev,
.nav-inline .nav-item:hover .nav-chev,
.nav-inline .nav-item:focus-within .nav-chev,
.nav-inline .nav-item.is-open .nav-chev {
  transform: rotate(180deg);
}

/* ---------- Header dropdown (Services sub-items) ---------- */

.nav-inline .nav-item {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.nav-dropdown {
  position: absolute;
  /* Sit flush against the BOTTOM EDGE of the floating nav pill rather
     than just below the trigger text. The 22px offset covers the
     vertical centring space inside .header-inner so the dropdown
     starts exactly where the pill ends, with no visible gap. */
  top: calc(100% + 22px);
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  min-width: 320px;
  padding: 8px;
  /* Match the navigation pill's glass look exactly */
  background: rgba(15, 16, 18, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 16px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.05) inset,
    0 16px 40px -16px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  display: flex;
  flex-direction: column;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .22s var(--ease), transform .22s var(--ease), visibility 0s linear .22s;
  z-index: 50;
}

/* Invisible "bridge" zone that fills the vertical space between the nav
   pill's bottom edge and the dropdown panel. Without this, moving the
   mouse down from the trigger crosses an unhovered area and the
   :hover state on .nav-item drops, dismissing the dropdown. The
   pseudo-element is a child of .nav-dropdown which is a descendant of
   .nav-item, so hovering it keeps .nav-item:hover true. */
.nav-dropdown::before {
  content: '';
  position: absolute;
  top: -26px;
  left: 0;
  right: 0;
  height: 26px;
}

/* ============================================================
   Two-column "detail" dropdown — used for À propos
   ============================================================
   Left column: list of section shortcuts with number + arrow
   Right column: description panel that swaps content when a list
   item is hovered (JS toggles .is-active on item + matching detail).

   Positioning: fixed to the viewport so the panel ALWAYS spans the
   exact same horizontal bounds as the floating nav-bar pill above it
   (matching the .site-header's width/max-width and centering).
   This is what makes the panel feel "attached" to the nav rather
   than floating below a single trigger. */
.nav-dropdown-detail {
  position: fixed;
  /* Sit closer to the nav pill — top now at viewport y=70 instead of
     78, so the panel begins right inside the pill's bottom-padding
     area. Combined with the high-opacity background and the square
     top corners, the panel reads as an extension of the nav bar. */
  top: 70px;
  left: 50%;
  width: calc(100% - 28px);
  max-width: 860px;
  min-width: 0;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 0;
  overflow: hidden;
  /* Higher opacity for clear readability + square top corners so the
     panel butts cleanly against the bottom of the nav pill. */
  background: rgba(15, 16, 18, 0.92);
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

/* Reliable hover bridge for ALL nav-item dropdowns (Services and À
   propos). The ::before on the dropdown itself is unreliable because
   overflow:hidden on the panel clips pseudo-elements that extend
   beyond its box, AND when the dropdown is not :hover its pointer-
   events drop to none making any bridge inside it unhittable. So we
   put the bridge on the trigger as ::after instead — it extends
   DOWNWARD from the trigger into the dropdown's hit zone. As a
   descendant of .nav-trigger / .nav-item, hovering it keeps :hover
   true on the .nav-item and the dropdown stays open. */
.nav-dropdown::before,
.nav-dropdown-detail::before {
  display: none;
}

.nav-item .nav-trigger {
  position: relative;
}

.nav-item .nav-trigger::after {
  content: '';
  position: absolute;
  top: 100%;
  /* Wide enough to catch a diagonal cursor path toward the dropdown
     but not so wide that it overlaps the next nav trigger. */
  left: -50px;
  right: -50px;
  /* Extend DOWN past the dropdown's top edge so the bridge always
     overlaps with the dropdown's hit area — no gap possible. */
  height: 40px;
}

.nav-dropdown-detail .nav-dropdown-list {
  display: flex;
  flex-direction: column;
  padding: 10px;
  gap: 2px;
  border-right: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(0, 0, 0, 0.15);
}

.nav-dropdown-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.78);
  text-decoration: none;
  transition: background .2s var(--ease), color .2s var(--ease);
  cursor: pointer;
}

.nav-dropdown-item:hover,
.nav-dropdown-item.is-active {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
}

.nav-dropdown-item .nav-dropdown-num {
  flex: 0 0 auto;
  width: auto;
  height: auto;
  border: 0;
  background: transparent;
  border-radius: 0;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.45);
  margin: 0;
  padding: 0;
}

.nav-dropdown-item.is-active .nav-dropdown-num,
.nav-dropdown-item:hover .nav-dropdown-num {
  color: var(--accent-green);
}

.nav-dropdown-item .nav-dropdown-text {
  flex: 1;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.nav-dropdown-arrow {
  flex: 0 0 auto;
  font-size: 16px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.4);
  transition: color .2s var(--ease), transform .2s var(--ease);
}

.nav-dropdown-item:hover .nav-dropdown-arrow,
.nav-dropdown-item.is-active .nav-dropdown-arrow {
  color: var(--accent-green);
  transform: translateX(3px);
}

.nav-dropdown-detail .nav-dropdown-panel {
  position: relative;
  padding: 22px 26px;
  min-height: 180px;
}

.nav-detail {
  position: absolute;
  inset: 22px 26px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}

.nav-detail.is-active {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.nav-detail-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.005em;
  color: #fff;
  margin: 0 0 10px;
}

.nav-detail-desc {
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.7);
  margin: 0 0 16px;
}

.nav-detail-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent-green);
  text-decoration: none;
  transition: gap .2s var(--ease);
}

.nav-detail-link span {
  transition: transform .2s var(--ease);
  display: inline-block;
}

.nav-detail-link:hover {
  gap: 10px;
}
.nav-detail-link:hover span {
  transform: translateX(2px);
}

@media (max-width: 720px) {
  .nav-dropdown-detail {
    min-width: 0;
    grid-template-columns: 1fr;
  }
  .nav-dropdown-detail .nav-dropdown-list {
    border-right: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  }
  .nav-dropdown-detail .nav-dropdown-panel {
    min-height: auto;
  }
  .nav-detail {
    position: relative;
    inset: auto;
    display: none;
  }
  .nav-detail.is-active {
    display: block;
  }
}

.nav-inline .nav-item:hover .nav-dropdown,
.nav-inline .nav-item:focus-within .nav-dropdown,
.nav-inline .nav-item.is-open .nav-dropdown {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
  transition: opacity .22s var(--ease), transform .22s var(--ease), visibility 0s linear 0s;
}

.nav-dropdown a {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 12px 14px;
  border-radius: 12px;
  color: rgba(255, 255, 255, 0.9);
  text-decoration: none;
  transition: background .2s var(--ease), color .2s var(--ease);
}

.nav-dropdown a:hover {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
}

.nav-dropdown-num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 14px;
  color: var(--accent-blue);
  margin-top: 2px;
}

.nav-dropdown-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.nav-dropdown-title {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
  line-height: 1.2;
}

.nav-dropdown-sub {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
  line-height: 1.35;
}

/* ---------- Header actions (right side) ---------- */

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* Search pill (Ask GET) */
.search-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 6px 0 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.03);
  transition: border-color .2s var(--ease), background .2s var(--ease);
  min-width: 220px;
}

.search-pill:focus-within {
  border-color: rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.06);
}

.search-icon {
  display: inline-flex;
  color: var(--accent-green);
  filter: drop-shadow(0 0 6px rgba(126, 217, 87, 0.5));
}

.search-pill input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: 0;
  color: #fff;
  font-family: var(--font-body);
  font-size: 13px;
  letter-spacing: 0.02em;
}

.search-pill input::placeholder {
  color: rgba(255, 255, 255, 0.55);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
}

.search-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}

.search-submit:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.4);
}

/* Icon button — ghost. Variants: round (default) and square (rounded square) */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: transparent;
  color: #fff;
  transition: background .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease);
}

.icon-btn.icon-square {
  border-radius: 12px;
}

.icon-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.4);
}

/* CTA buttons — secondary white, primary brand-green */
.cta-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 20px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 999px;
  border: 0;
  transition: background .2s var(--ease), color .2s var(--ease), filter .2s var(--ease);
}

.cta-secondary {
  background: #fff;
  color: var(--ink);
}
.cta-secondary:hover {
  background: rgba(255, 255, 255, 0.88);
}

.cta-primary {
  background: var(--accent-green);
  color: var(--ink);
}
.cta-primary:hover {
  filter: brightness(0.94);
}

.cta-link .arrow {
  transition: transform .3s var(--ease);
  font-size: 14px;
}

.cta-link:hover .arrow {
  transform: translateX(3px);
}

/* Mobile-only hamburger trigger (kept for the slide-down menu panel) */
.menu-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 12px;
  background: var(--accent-green);
  color: var(--ink);
  cursor: pointer;
  transition: filter .2s var(--ease), background .2s var(--ease);
}

.menu-toggle:hover { filter: brightness(0.94); }

.menu-toggle[aria-expanded="true"] {
  background: var(--accent-blue);
  color: #fff;
}

.menu-toggle-label { line-height: 1; }

.menu-toggle-icon {
  position: relative;
  display: inline-block;
  width: 16px;
  height: 12px;
}

.menu-toggle-icon span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transition: transform .3s var(--ease), top .3s var(--ease), opacity .2s var(--ease);
}

.menu-toggle-icon span:nth-child(1) { top: 3px; }
.menu-toggle-icon span:nth-child(2) { top: 8px; }

.menu-toggle[aria-expanded="true"] .menu-toggle-icon span:nth-child(1) {
  top: 5px;
  transform: rotate(45deg);
}
.menu-toggle[aria-expanded="true"] .menu-toggle-icon span:nth-child(2) {
  top: 5px;
  transform: rotate(-45deg);
}

/* ---------- Nav menu panel (slide-down dropdown) ---------- */

.nav-menu {
  position: fixed;
  top: 92px;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  /* Dark glass — same language as the desktop dropdown (.nav-dropdown). */
  background: rgba(15, 16, 18, 0.92);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  color: #fff;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  overflow-y: auto;
  /* Slide-down + fade reveal. Hidden state via translateY + opacity + visibility. */
  transform: translateY(-12px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    transform .45s cubic-bezier(.2,.7,.2,1),
    opacity .35s var(--ease),
    visibility 0s linear .4s;
}

.nav-menu.open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition:
    transform .55s cubic-bezier(.2,.7,.2,1),
    opacity .35s var(--ease),
    visibility 0s linear 0s;
}

.nav-menu-inner {
  padding-top: clamp(40px, 6vw, 72px);
  padding-bottom: clamp(40px, 6vw, 72px);
  display: grid;
  gap: clamp(32px, 5vw, 64px);
}

.nav-menu-list {
  display: flex;
  flex-direction: column;
}

/* Top-level rows — plain links (Projets/Contact) AND accordion toggle
   buttons (Services/À propos) share the big display styling so a tappable
   section reads identically to a direct link. */
.nav-menu-row {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 28px;
  width: 100%;
  margin: 0;
  padding: clamp(20px, 2.6vw, 32px) 4px;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  background: none;
  text-align: left;
  font-family: var(--font-display);
  font-size: clamp(40px, 7vw, 88px);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  color: #fff;
  cursor: pointer;
  -webkit-appearance: none;
          appearance: none;
  /* Items slide-up reveal — each one staggered via .open state */
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .5s var(--ease), transform .5s cubic-bezier(.2,.7,.2,1), color .25s var(--ease);
}

.nav-menu.open .nav-menu-row {
  opacity: 1;
  transform: translateY(0);
}
.nav-menu.open .nav-menu-group:nth-child(1) .nav-menu-row { transition-delay: 0.10s; }
.nav-menu.open .nav-menu-group:nth-child(2) .nav-menu-row { transition-delay: 0.18s; }
.nav-menu.open .nav-menu-group:nth-child(3) .nav-menu-row { transition-delay: 0.26s; }
.nav-menu.open .nav-menu-group:nth-child(4) .nav-menu-row { transition-delay: 0.34s; }

.nav-menu-group:first-child .nav-menu-row { border-top: 1px solid rgba(255, 255, 255, 0.1); }

.nav-menu-row:hover {
  color: var(--accent-green);
}

.nav-num {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.45);
  align-self: start;
  padding-top: 0.5em;
}

.nav-arrow {
  font-size: 0.4em;
  transform: translateX(0);
  transition: transform .3s var(--ease), color .25s var(--ease);
  color: rgba(255, 255, 255, 0.4);
}

/* Only links nudge their arrow on hover; accordion chevrons rotate instead. */
a.nav-menu-row:hover .nav-arrow {
  transform: translateX(8px);
  color: var(--accent-green);
}

/* Accordion chevron flips up when its group is open. */
.nav-acc-chev {
  font-size: 0.42em;
}
.nav-menu-group.is-open .nav-acc-chev {
  transform: rotate(180deg);
  color: var(--accent-green);
}

/* When a section is open the big row drops its divider so the dark panel
   below butts straight against it — they read as one connected unit, the
   way the desktop trigger + dropdown do (text stays dark on the white bar). */
.nav-menu-group.is-open .nav-menu-row {
  border-bottom-color: transparent;
}
.nav-menu-group.is-open .nav-acc-chev { color: var(--accent-green); }

/* Sub-menu (accordion body) — mirrors the desktop dropdown: a dark glass
   panel of rounded "pill" items (mono number, light text, brand-green on
   tap). It butts flush against the row above (square top corners, no top
   gap) so it unrolls *connected* to the menu bar. Collapsed via max-height. */
.nav-menu-sub {
  overflow: hidden;
  max-height: 0;
  transition: max-height .45s cubic-bezier(.2,.7,.2,1);
}
.nav-menu-group.is-open .nav-menu-sub {
  max-height: 600px;
  margin: 0 0 16px;
  padding: 6px;
  /* Subtle lighter inset so the panel reads as a raised block on the
     already-dark glass drawer. */
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-top: none;
  /* Square top (joins the bar), rounded bottom (panel drops down). */
  border-radius: 0 0 16px 16px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

/* Pill item — matches .nav-dropdown-item from the desktop dropdown. */
.nav-menu-sub a {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 14px;
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.78);
  text-decoration: none;
  transition: background .2s var(--ease), color .2s var(--ease);
}
.nav-menu-sub a + a { margin-top: 1px; }
.nav-menu-sub a:hover,
.nav-menu-sub a:active {
  background: rgba(255, 255, 255, 0.07);
  color: #fff;
}

.nav-sub-num {
  flex: 0 0 auto;
  min-width: 18px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.4);
  transition: color .2s var(--ease);
}
.nav-menu-sub a:hover .nav-sub-num,
.nav-menu-sub a:active .nav-sub-num { color: var(--accent-green); }

.nav-sub-text {
  flex: 1;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-transform: none;
}

.nav-sub-arrow {
  flex: 0 0 auto;
  font-size: 17px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.35);
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.nav-menu-sub a:hover .nav-sub-arrow,
.nav-menu-sub a:active .nav-sub-arrow {
  color: var(--accent-green);
  transform: translateX(3px);
}

.nav-menu-meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 4vw, 56px);
  padding-top: clamp(20px, 3vw, 32px);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .5s var(--ease) .45s, transform .5s var(--ease) .45s;
}

.nav-menu.open .nav-menu-meta {
  opacity: 1;
  transform: translateY(0);
}

.nav-meta-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
  margin: 0 0 12px;
}

.nav-meta-value {
  display: block;
  font-size: 16px;
  color: rgba(255, 255, 255, 0.85);
  margin: 0 0 4px;
  transition: color .2s var(--ease);
}

a.nav-meta-value:hover { color: var(--accent-green); }

.menu-open { overflow: hidden; }

/* Hide the search pill on smaller screens. The inline (PC) nav stays visible
   down to tablet — only the search pill drops here. */
@media (max-width: 1180px) {
  .search-pill { display: none; }
}

@media (max-width: 960px) {
  .cta-secondary { display: none; }
}

/* Tablet keeps the desktop (PC) nav links + dropdown sub-menus. The mobile
   hamburger only takes over below 860px (phones / small portrait tablets). */
@media (max-width: 860px) {
  .nav-inline { display: none; }
  .menu-toggle { display: inline-flex; }
}

@media (max-width: 720px) {
  .site-header { top: 10px; width: calc(100% - 20px); }
  .header-inner { height: 56px; padding: 5px 5px 5px 16px; }
  .brand-logo { height: 30px; }
  .icon-btn, .cta-link, .menu-toggle { height: 40px; width: 40px; }
  .cta-link { padding: 0 14px; font-size: 12px; width: auto; }
  .nav-menu { top: 80px; }
  .nav-menu-meta { grid-template-columns: 1fr; }
}

@media (max-width: 540px) {
  .cta-primary { display: none; }
}

@media (max-width: 380px) {
  .icon-btn { display: none; }
}

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

.hero {
  position: relative;
  padding: clamp(80px, 11vw, 160px) 0 clamp(60px, 8vw, 120px);
}

.hero > .container {
  position: relative;
  z-index: 2;
}

/* ---------- Hero split : text scrolls, video sticky right ---------- */

.hero-split {
  /* Trimmed bottom padding so the white gap between the captions
     column and the railway section's rail tips no longer reads as a
     "blocking" band above the dashed tracks. */
  padding: clamp(72px, 7vw, 118px) 0 clamp(32px, 4vw, 60px);
  display: flex;
  flex-direction: column;
}

.hero-grid {
  flex: 1;
  display: grid;
  /* Floor on the text column so it can't collapse to its (intrinsic-zero)
     caption content — the absolutely-positioned caption track gives the
     column no natural width, so without this floor the 1.5fr track shrinks
     to ~0. */
  /* Bias width to the video so it reads near-fullscreen (Terminal-style):
     the text column keeps a floor (its caption track is absolutely
     positioned, so without a floor it collapses to ~0), the video column
     takes the larger share and the frame caps itself by viewport height. */
  grid-template-columns: minmax(360px, 0.72fr) minmax(0, 1.78fr);
  /* Ligne 1 = numérateur d'ouverture (auto, hauteur naturelle) ;
     ligne 2 = texte + vidéo (1fr) qui absorbe tout le min-height géant posé
     par le JS (scroll des captions). Sans ça, align-content:stretch répartit
     ce min-height sur les deux lignes et pousse le contenu ~2000px plus bas. */
  grid-template-rows: auto 1fr;
  gap: clamp(14px, 1.6vw, 32px);
  align-items: start;
}

/* En-tête d'ouverture du hero : numérateur + grand titre, pleine largeur
   au-dessus de la grille, aligné à gauche comme les captions (les autres
   sections le centrent, pas ici). Ancre le passage banner sombre → hero
   blanc comme une vraie ouverture de section, cohérente avec les
   numérateurs 01/02/… qui suivent. */
.hero-opening {
  grid-column: 1 / -1;
  /* Bottom margin bumped to roughly match the section's top padding
     above so the title block reads as vertically centred with equal
     breathing room above and below before the captions/video. */
  margin: 0 0 clamp(56px, 6vw, 110px);
  text-align: center;
}

.hero-opening .hero-row-num {
  justify-content: center;
  margin: 0 0 clamp(12px, 1.6vw, 22px);
}

/* Titre court grande typo (League Gothic — la police d'affichage du site). */
.hero-opening-title {
  font-family: var(--font-display);
  font-size: clamp(60px, 9vw, 168px);
  line-height: 0.9;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
  opacity: 0;
  filter: blur(14px);
}

.hero-opening-title.is-revealed {
  animation: heroOpeningFocusIn 1800ms cubic-bezier(.16, 1, .3, 1) 120ms both;
}

@keyframes heroOpeningFocusIn {
  from {
    opacity: 0;
    filter: blur(14px);
  }
  to {
    opacity: 1;
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-opening-title,
  .hero-opening-title.is-revealed {
    opacity: 1;
    filter: none;
    animation: none;
  }
}

.hero-text {
  min-width: 0;
  position: sticky;
  /* Match the video's top offset so the big caption column reads as a
     full-height panel beside the video (Terminal-style near-fullscreen). */
  top: calc(0.06 * var(--vh100));
  align-self: start;
  /* Make the sticky text column fill from its top offset down to a hair
     above the viewport bottom, then push the clients bar to flex-end so
     it always sits just inside the bottom edge — no matter how the
     captions block is sized. */
  display: flex;
  flex-direction: column;
  height: calc(0.92 * var(--vh100));
}

.hero-text .hero-partners {
  margin-top: auto;
}

/* Smaller title in split layout */
.hero-split .hero-title {
  font-size: clamp(52px, 8vw, 138px);
  margin-bottom: 28px;
}

.hero-split .hero-lead {
  font-size: clamp(16px, 1.4vw, 19px);
  margin-bottom: 36px;
}

.hero-split .hero-meta { grid-template-columns: repeat(2, 1fr); }

/* Sticky video column */
.hero-video {
  position: sticky;
  /* Keeps the existing breathing room under the floating nav.
     --vh100 keeps the offset proportionally identical under page
     zoom (raw vh would over-shoot by the zoom factor). */
  top: calc(0.05 * var(--vh100));
  align-self: start;
  /* Extends toward the viewport edge but leaves breathing room on the right */
  width: calc(100% + max(0px, (var(--vw100) - var(--max)) / 2) + var(--pad) - 64px);
}

/* On zoomed / large screens the video frame sits too low because the
   proportional offset stacks with the zoom scaling. Pull it up. */
@media (min-width: 1729px) {
  .hero-video { top: calc(0.02 * var(--vh100)); }
  .hero-text  { top: calc(0.02 * var(--vh100)); height: calc(0.96 * var(--vh100)); }
}

.hero-video-frame {
  position: relative;
  /* Terminal-style vertical panel: large, close to square, but tall enough
     to occupy nearly the full sticky viewport. */
  aspect-ratio: 0.94;
  width: 100%;
  /* Cap to the available sticky viewport height, then derive width from
     the near-square aspect ratio so the panel reads like Terminal. */
  max-width: calc((0.92 * var(--vh100) - 5px) * 0.94);
  margin-left: auto;
  overflow: hidden;
  background: var(--bg-alt);
  border-radius: 16px;
  --hero-notch-y: 50%;
  /* Same geometry language as the About page figures: a real clipped
     trapezoid, with 28px depth and 56px cosine-S chamfers. */
  clip-path: polygon(
    16px 0%,
    calc(100% - 16px) 0%,
    calc(100% - 10px) 1px, calc(100% - 5px) 5px, calc(100% - 1px) 10px, 100% 16px,
    100% calc(100% - 16px),
    calc(100% - 1px) calc(100% - 10px), calc(100% - 5px) calc(100% - 5px), calc(100% - 10px) calc(100% - 1px), calc(100% - 16px) 100%,
    16px 100%,
    10px calc(100% - 1px), 5px calc(100% - 5px), 1px calc(100% - 10px), 0px calc(100% - 16px),
    0px calc(var(--hero-notch-y) + 160px),
    0.351px calc(var(--hero-notch-y) + 156px), 1.386px calc(var(--hero-notch-y) + 152px), 3.054px calc(var(--hero-notch-y) + 148px), 5.271px calc(var(--hero-notch-y) + 144px), 7.926px calc(var(--hero-notch-y) + 140px), 10.885px calc(var(--hero-notch-y) + 136px), 14px calc(var(--hero-notch-y) + 132px), 17.115px calc(var(--hero-notch-y) + 128px), 20.074px calc(var(--hero-notch-y) + 124px), 22.729px calc(var(--hero-notch-y) + 120px), 24.946px calc(var(--hero-notch-y) + 116px), 26.614px calc(var(--hero-notch-y) + 112px), 27.649px calc(var(--hero-notch-y) + 108px),
    28px calc(var(--hero-notch-y) + 104px),
    28px calc(var(--hero-notch-y) - 104px),
    27.649px calc(var(--hero-notch-y) - 108px), 26.614px calc(var(--hero-notch-y) - 112px), 24.946px calc(var(--hero-notch-y) - 116px), 22.729px calc(var(--hero-notch-y) - 120px), 20.074px calc(var(--hero-notch-y) - 124px), 17.115px calc(var(--hero-notch-y) - 128px), 14px calc(var(--hero-notch-y) - 132px), 10.885px calc(var(--hero-notch-y) - 136px), 7.926px calc(var(--hero-notch-y) - 140px), 5.271px calc(var(--hero-notch-y) - 144px), 3.054px calc(var(--hero-notch-y) - 148px), 1.386px calc(var(--hero-notch-y) - 152px), 0.351px calc(var(--hero-notch-y) - 156px),
    0px calc(var(--hero-notch-y) - 160px),
    0px 16px,
    1px 10px, 5px 5px, 10px 1px
  );
}

.hero-video-el {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.hero-video-tag {
  position: absolute;
  top: 14px;
  right: 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-radius: 999px;
  background: rgba(15, 16, 18, 0.78);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  z-index: 2;
}

.hero-video-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--accent-green);
  box-shadow: 0 0 8px rgba(126, 217, 87, 0.65);
  animation: cpLed 2s cubic-bezier(.4,0,.2,1) infinite;
}

/* Was the ≤960 "compact stacked" hero. Scoped to ≤720 only so the tablet
   range (721–1100) instead gets the mobile carousel layout below. At ≤720 it
   is overridden by that carousel block anyway, so mobile is unchanged. */
@media (max-width: 720px) {
  .hero-split {
    padding: clamp(56px, 9vw, 96px) 0 clamp(64px, 10vw, 110px);
  }

  .hero-grid {
    display: flex;
    flex-direction: column;
    gap: clamp(28px, 6vw, 56px);
    min-height: 0 !important;
  }

  /* Garder l'en-tête d'ouverture au-dessus de la vidéo et du texte. */
  .hero-opening {
    order: -1;
  }

  .hero-video {
    order: 0;
    position: relative;
    top: auto;
    width: 100%;
    max-width: none;
    margin: 0;
  }

  .hero-video-frame {
    aspect-ratio: 16 / 10;
    max-width: none;
    border-radius: 16px;
    clip-path: inset(0 round 16px);
  }

  .hero-text {
    order: 1;
    position: relative;
    top: auto;
    height: auto;
    width: 100%;
  }

  .hero-captions {
    margin: 0;
    height: auto;
  }

  .hero-counter {
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    margin-bottom: 18px;
  }

  .hero-caption-viewport {
    height: auto;
    margin-left: 0;
    width: 100%;
    overflow: visible;
    -webkit-mask-image: none;
            mask-image: none;
  }

  .hero-caption-track {
    position: relative;
    gap: clamp(28px, 6vw, 48px);
    transform: none;
  }

  .hero-caption {
    max-width: min(760px, 100%);
    font-size: clamp(28px, 6vw, 46px);
    line-height: 1.08;
  }

  .hero-caption-char,
  .hero-caption-char.is-shown {
    animation: none;
    color: #000;
  }

  .hero-text .hero-partners {
    margin-top: clamp(40px, 8vw, 72px);
  }
}

/* Mobile carousel hero layout — extended to the whole tablet range so section
   01 uses the same layout as mobile (video banner + caption carousel + step
   nav), but adaptive: the video height is capped with svh so it doesn't blow
   up on wide/tall tablets. The carousel JS runs here too (heroMobileCaption
   Query is ≤1100). */
@media (max-width: 1100px) {
  .hero-split {
    padding: clamp(42px, 10vw, 72px) 0 clamp(56px, 12vw, 88px);
    --hero-mobile-video-top: calc(max(14px, env(safe-area-inset-top)) + 82px);
    --hero-mobile-video-height: min(calc(100vw * 0.75), 52svh);
    --hero-mobile-text-top: calc(var(--hero-mobile-video-top) + var(--hero-mobile-video-height) + 18px);
  }

  .hero-grid {
    gap: clamp(16px, 4.5vw, 22px);
  }

  .hero-opening {
    margin-bottom: 0;
  }

  .hero-video {
    position: relative;
    top: auto;
    z-index: 5;
    width: calc(100% + (2 * var(--pad)));
    max-width: none;
    margin: 0 calc(-1 * var(--pad)) 16px;
    box-shadow: none;
    filter: none;
  }

  .hero-video-frame {
    aspect-ratio: 4 / 3;
    border-radius: 0 0 14px 14px;
    clip-path: inset(0 round 0 0 14px 14px);
    box-shadow: none;
    filter: none;
  }

  .hero-video-frame::after {
    content: '';
    position: absolute;
    left: -88px;
    right: -88px;
    bottom: -1px;
    z-index: 3;
    height: 38px;
    background: var(--bg);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 38' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0,13H70C76,13,81,15.4,86,19L101,30C106,33.6,112,35,119,35H193C200,35,206,33.6,211,30L226,19C231,15.4,236,13,242,13H420V38H0Z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 38' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0,13H70C76,13,81,15.4,86,19L101,30C106,33.6,112,35,119,35H193C200,35,206,33.6,211,30L226,19C231,15.4,236,13,242,13H420V38H0Z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    transform: translateX(var(--hero-mobile-trap-x, 0px));
    transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
    box-shadow: none;
    filter: none;
    pointer-events: none;
  }

  .hero-video-tag {
    top: 10px;
    right: 10px;
    max-width: calc(100% - 20px);
    padding: 6px 10px;
    font-size: 9.5px;
    letter-spacing: 0.12em;
  }

  .hero-mobile-controls {
    position: absolute;
    right: 12px;
    bottom: 32px;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .hero-mobile-control {
    display: inline-grid;
    place-items: center;
    width: 52px;
    height: 52px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.38);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.54);
    color: rgba(10, 10, 10, 0.78);
    box-shadow: none;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    cursor: pointer;
    transition:
      transform 0.22s var(--ease),
      background 0.22s var(--ease),
      opacity 0.22s var(--ease);
  }

  .hero-mobile-control--next {
    background: rgba(255, 255, 255, 0.92);
    color: var(--ink);
  }

  .hero-mobile-control span {
    display: block;
    font-family: var(--font-body);
    font-size: 36px;
    font-weight: 400;
    line-height: 0.82;
    transform: translateY(-1px);
  }

  .hero-mobile-control:active {
    transform: scale(0.96);
  }

  .hero-mobile-control:disabled {
    opacity: 0.38;
    cursor: default;
    pointer-events: none;
  }

  .hero-counter {
    display: none;
  }

  .hero-caption-track {
    gap: clamp(24px, 8vw, 40px);
  }

  .hero-caption {
    font-size: clamp(25px, 8vw, 36px);
    line-height: 1.12;
    letter-spacing: 0;
  }
}

@media (max-width: 480px) {
  .hero-split {
    --hero-mobile-video-height: calc(100vw - (2 * var(--pad)));
  }

  .hero-video-frame {
    aspect-ratio: 1 / 1;
  }

  .hero-caption {
    font-size: clamp(23px, 7.2vw, 31px);
  }

  .hero-partners-label {
    margin-bottom: 22px;
  }
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 32px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.eyebrow-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--accent-green);
  box-shadow: 0 0 0 4px rgba(126,217,87,.18);
  animation: pulse 2.4s var(--ease) infinite;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(126,217,87,.18); }
  50%      { box-shadow: 0 0 0 8px rgba(126,217,87,.04); }
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(64px, 13vw, 220px);
  line-height: 0.96;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  margin: 0 0 32px;
}

.hero-title .accent { color: var(--accent-blue); }

.hero-lead {
  font-size: clamp(18px, 1.6vw, 22px);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 640px;
  margin: 0 0 40px;
}

.hero-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: clamp(28px, 3.5vw, 48px);
}

/* Visually hidden but readable by screen readers / crawlers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ---------- Hero rotating captions — scroll-synced (Terminal style) ----------
   Replaces the big display headline with sentence-case captions that advance
   one per scroll-slice, driven by the same progress value as the sticky video
   scrub (see script.js → updateCaptions). The track translates up by the
   active caption's measured offset, so captions of any length stay aligned
   to the focus line. The active caption is full-ink; the rest are dimmed,
   with a soft bottom mask dissolving the upcoming line. */
.hero-captions {
  position: relative;
  display: block;
  /* Negative left margin pulls BOTH the counter (positioned absolute
     left:0 of this element) AND the caption viewport together toward
     the section edge, past the container's natural left padding. */
  margin: 0 0 0 clamp(-104px, -5.5vw, -56px);
  /* Focus area: captions travel bottom → top through it, the active one
     centred beside the counter. Uses --vh100 so it stays a fixed fraction
     of the viewport under page zoom. */
  height: max(440px, calc(0.76 * var(--vh100)));
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
}

/* Counter pinned beside the focus line, to the LEFT of the caption column —
   it marks where the reader's attention sits (matches FOCUS_FRAC in script.js).
   The number rolls vertically (odometer) as the active caption changes. */
.hero-counter {
  position: absolute;
  /* Sits a hair LEFT of the .hero-captions origin so the fraction
     "05/0X" reads slightly tucked in toward the section edge — the
     viewport's own margin-left below still carves out the clearance
     so the active caption never overruns it. */
  left: clamp(-28px, -2vw, -12px);
  /* Sits at the active caption's vertical centre (the focus line) — the
     transform recentres the counter's own height on that line. */
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: flex-start;
  /* Zero gap: total / slash / static "0" / rolling digit must render
     as one continuous fraction ("05/01") with no extra spacing. */
  gap: 0;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: clamp(14px, 1.05vw, 18px);
  letter-spacing: 0.1em;
  line-height: 1.25;
}
/* Total, slash and static-zero glyphs all share the same baseline
   styling as a rolling cell so the four spans stack visually as a
   single fraction "05/01". */
.hero-counter-static,
.hero-counter-slash,
.hero-counter-total {
  height: 1.25em;
  line-height: 1.25;
  color: var(--ink);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* Tighten the slash slightly against its neighbours so the fraction
   reads as one tight unit; tabular-nums above keeps every digit at
   the same width. */
.hero-counter-slash {
  padding: 0 1px;
  opacity: 0.7;
}

.hero-counter-window {
  position: relative;
  overflow: hidden;
  height: 1.25em;           /* exactly one cell tall — the odometer window */
}
.hero-counter-roll {
  display: flex;
  flex-direction: column;
  transition: transform 0.62s cubic-bezier(.22, .8, .2, 1);
  will-change: transform;
}
.hero-counter-cell {
  height: 1.25em;
  line-height: 1.25;
  color: var(--ink);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.hero-counter-total { color: rgba(15, 16, 18, 0.34); }

.hero-mobile-step-nav,
.hero-mobile-controls,
.hero-mobile-progress {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .hero-counter-roll { transition: none; }
}

.hero-caption-viewport {
  display: block;
  position: relative;
  height: 100%;
  margin-left: clamp(80px, 6.5vw, 116px);
  width: calc(100% + clamp(48px, 6vw, 130px));
  overflow: hidden;
  /* Captions dissolve as they rise out the top and fade in from the bottom.
     Tight band so essentially one big caption reads at a time. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
}

.hero-caption-track {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(160px, 20vh, 340px);
  /* No transition — the track follows the scroll-driven transform set
     by updateHero() directly, so reading the captions feels like
     free-scrolling a column of paragraphs rather than snapping
     between fixed indices. Lenis already smooths the wheel input
     upstream, so the result still glides. */
  will-change: transform;
}

@keyframes heroTerminalColor {
  0% { color: #dddddd; }
  /* Brand-green flash (GET Énergie --accent-green #7ed957) as each
     character lights up before settling into the dark final colour. */
  30% { color: #7ed957; }
  100% { color: #000; }
}

/* Terminal-style caption reveal: every paragraph is split into individual
   characters. Future text stays light grey; when an item reaches the
   counter, its letters flip grey -> lime -> dark one after another. */
.hero-caption {
  display: block;
  margin: 0;
  max-width: clamp(520px, 50vw, 780px);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: clamp(22px, 2.1vw, 34px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: #dddddd;
}

.hero-caption-char {
  color: #dddddd;
  transition: color 0.4s;
}

.hero-caption-char.is-shown {
  animation: heroTerminalColor 0.5s;
  animation-delay: var(--v-delay, 0s);
  animation-fill-mode: both;
  color: #000;
}

/* Reduced-motion users get a flat colour swap on the active caption
   only — no scroll-driven gradient. */
@media (prefers-reduced-motion: reduce) {
  .hero-caption-char {
    animation: none !important;
    transition: none;
  }
  .hero-caption-char.is-shown { color: #000; }
}

/* Terminal-style responsive handoff: keep the cinematic split on desktop,
   then simplify to a stable stacked layout for tablets and phones. This
   block intentionally lives after the caption base rules so it wins the
   cascade on compact screens. */
@media (max-width: 1100px) {
  .hero-split {
    padding: clamp(56px, 8vw, 96px) 0 clamp(64px, 10vw, 110px);
  }

  .hero-grid {
    display: flex;
    flex-direction: column;
    gap: clamp(28px, 6vw, 56px);
    width: 100%;
    min-width: 0;
    min-height: 0 !important;
  }

  /* Garder l'en-tête d'ouverture au-dessus de la vidéo et du texte. */
  .hero-opening {
    order: -1;
  }

  .hero-video {
    order: 0;
    position: relative;
    top: auto;
    width: 100%;
    max-width: none;
    margin: 0;
  }

  .hero-video-frame {
    aspect-ratio: 16 / 10;
    width: 100%;
    max-width: none;
    border-radius: 16px;
    clip-path: inset(0 round 16px);
  }

  .hero-text {
    order: 1;
    position: relative;
    top: auto;
    width: 100%;
    height: auto;
    min-width: 0;
  }

  .hero-captions {
    margin: 0;
    height: auto;
  }

  .hero-counter {
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    margin-bottom: 18px;
  }

  .hero-caption-viewport {
    height: auto;
    margin-left: 0;
    width: 100%;
    overflow: visible;
    -webkit-mask-image: none;
            mask-image: none;
  }

  .hero-caption-track {
    position: relative;
    gap: clamp(28px, 6vw, 48px);
    transform: none;
  }

  .hero-caption {
    max-width: min(760px, 100%);
    font-size: clamp(28px, 5.4vw, 44px);
    line-height: 1.1;
    letter-spacing: 0;
  }

  .hero-caption-char,
  .hero-caption-char.is-shown {
    animation: none;
    color: #000;
  }

  .hero-text .hero-partners {
    margin-top: clamp(40px, 8vw, 72px);
  }
}

@media (max-width: 720px) {
  :root { --pad: clamp(16px, 5vw, 22px); }
}

/* Section 01 hero: the mobile caption carousel (viewport clip + horizontal
   track + step-nav + progress) now also covers the tablet range so it matches
   mobile. The carousel JS runs here too (heroMobileCaptionQuery ≤1100) and
   translates the track by real slide offset, so the wider tablet slides
   (sized in the block at the end of the file) work automatically. */
@media (max-width: 1100px) {
  .hero-split {
    padding: clamp(42px, 10vw, 72px) 0 clamp(56px, 12vw, 88px);
    --hero-mobile-video-top: calc(max(14px, env(safe-area-inset-top)) + 82px);
    --hero-mobile-video-height: calc(100vw * 0.75);
    --hero-mobile-text-top: calc(var(--hero-mobile-video-top) + var(--hero-mobile-video-height) + 18px);
  }

  .hero-grid {
    position: relative;
    gap: clamp(16px, 4.5vw, 22px);
  }

  .hero-opening {
    margin-bottom: 0;
  }

  .hero-video {
    position: relative;
    top: auto;
    z-index: 5;
    width: calc(100% + (2 * var(--pad)));
    max-width: none;
    margin: 0 calc(-1 * var(--pad)) 16px;
    box-shadow: none;
    filter: none;
  }

  .hero-video-frame {
    aspect-ratio: 4 / 3;
    border-radius: 0 0 14px 14px;
    clip-path: inset(0 round 0 0 14px 14px);
    box-shadow: none;
    filter: none;
  }

  .hero-video-tag {
    top: 10px;
    right: 10px;
    max-width: calc(100% - 20px);
    padding: 6px 10px;
    font-size: 9.5px;
    letter-spacing: 0.12em;
  }

  .hero-mobile-controls {
    position: absolute;
    right: 12px;
    bottom: 32px;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .hero-mobile-control {
    display: inline-grid;
    place-items: center;
    width: 52px;
    height: 52px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.38);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.54);
    color: rgba(10, 10, 10, 0.78);
    box-shadow: none;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    cursor: pointer;
    transition:
      transform 0.22s var(--ease),
      background 0.22s var(--ease),
      opacity 0.22s var(--ease);
  }

  .hero-mobile-control--next {
    background: rgba(255, 255, 255, 0.92);
    color: var(--ink);
  }

  .hero-mobile-control span {
    display: block;
    font-family: var(--font-body);
    font-size: 36px;
    font-weight: 400;
    line-height: 0.82;
    transform: translateY(-1px);
  }

  .hero-mobile-control:active {
    transform: scale(0.96);
  }

  .hero-mobile-control:disabled {
    opacity: 0.38;
    cursor: default;
    pointer-events: none;
  }

  .hero-counter {
    display: none;
  }

  .hero-caption-track {
    gap: clamp(24px, 8vw, 40px);
  }

  .hero-caption {
    font-size: clamp(25px, 8vw, 36px);
    line-height: 1.12;
  }

  .hero-text {
    position: relative;
    top: auto;
    z-index: 4;
    min-height: 0;
    background: var(--bg);
  }

  .hero-captions {
    --hero-mobile-trap-x: 0px;
    --hero-mobile-progress-x: 0%;
    position: relative;
    top: auto;
    z-index: 1;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    height: auto;
    min-height: 0;
    overflow: visible;
  }

  .hero-mobile-step-nav {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: clamp(16px, 5.6vw, 28px);
    margin: 0 0 clamp(18px, 5vw, 24px);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.25;
    letter-spacing: 0.1em;
    font-variant-numeric: tabular-nums;
  }

  .hero-mobile-step {
    color: rgba(10, 10, 10, 0.14);
    transition: color 0.32s var(--ease), opacity 0.32s var(--ease);
  }

  .hero-mobile-step.is-active {
    color: var(--ink);
  }

  .hero-caption-viewport {
    position: relative;
    z-index: 1;
    height: auto;
    overflow: hidden;
    margin-left: 0;
    width: 100%;
  }

  .hero-caption-track {
    --hero-mobile-caption-gap: clamp(28px, 10vw, 44px);
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: var(--hero-mobile-caption-gap);
    width: max-content;
    height: auto;
    transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
  }

  .hero-caption {
    position: relative;
    flex: 0 0 min(76vw, 310px);
    width: min(76vw, 310px);
    max-width: none;
    min-height: clamp(62px, 18vw, 88px);
    padding: 0;
    font-size: clamp(16px, 4.8vw, 20px);
    line-height: 1.18;
    letter-spacing: 0;
    color: var(--ink);
    opacity: 0.28;
    transition: opacity 0.36s var(--ease);
  }

  .hero-caption.is-active {
    opacity: 1;
  }

  .hero-caption + .hero-caption {
    margin-top: 0;
  }

  .hero-mobile-progress {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    height: 2px;
    margin-top: clamp(18px, 5.5vw, 28px);
    overflow: hidden;
    background: rgba(10, 10, 10, 0.09);
  }

  .hero-mobile-progress-bar {
    position: absolute;
    left: 0;
    top: 0;
    width: clamp(56px, 17vw, 74px);
    height: 100%;
    border-radius: 999px;
    background: var(--ink);
    transform: translateX(var(--hero-mobile-progress-x));
    transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
  }

  /* Bring the desktop lime "reading" highlight to the phone caption band:
     re-enable the per-character grey → lime → dark sweep that the ≤960 / ≤1100
     blocks switch off. Higher specificity (.hero-split …) beats those
     plain-element resets; prefers-reduced-motion still wins via !important. */
  .hero-split .hero-caption-char {
    animation: none;
    color: rgba(10, 10, 10, 0.25);
    transition: color 0.36s var(--ease);
  }

  .hero-split .hero-caption-char.is-shown {
    animation: none;
    color: rgba(10, 10, 10, 0.25);
  }

  .hero-split .hero-caption.is-active .hero-caption-char,
  .hero-split .hero-caption.is-active .hero-caption-char.is-shown {
    color: var(--ink);
  }

  .hero-split .hero-caption.is-switching .hero-caption-char {
    animation: heroMobileTextGlow 640ms cubic-bezier(.22, .8, .2, 1) both;
    animation-delay: var(--v-delay, 0s);
  }

  @keyframes heroMobileTextGlow {
    0% {
      color: rgba(10, 10, 10, 0.25);
      text-shadow: none;
    }
    36% {
      color: var(--accent-green);
      text-shadow:
        0 0 10px rgba(126, 217, 87, 0.58),
        0 0 22px rgba(126, 217, 87, 0.28);
    }
    100% {
      color: var(--ink);
      text-shadow: none;
    }
  }

  .hero-text .hero-partners {
    margin-top: clamp(26px, 7.5vw, 44px);
  }

  .hero-split .hero-partners-track img {
    height: clamp(40px, 10.5vw, 50px);
    margin-right: clamp(42px, 12vw, 72px);
  }
}

@media (max-width: 480px) {
  .hero-split {
    --hero-mobile-video-height: 100vw;
  }

  .hero-video-frame {
    aspect-ratio: 1 / 1;
  }

  .hero-caption {
    font-size: clamp(16px, 4.6vw, 19px);
    line-height: 1.18;
  }

  .hero-split .hero-partners-label {
    margin-bottom: 8px;
  }
}

/* ============================================================
   Hero text — luxurious "power-on" reveal
   The hero text starts hidden; JS adds `.is-revealed` to
   `.hero-text` once the cinematic intro overlay finishes (or
   immediately when there is no intro). Each title line wipes
   in left-to-right via a soft gradient mask. Eyebrow, lead,
   and buttons follow with staggered fade-ups.
   ============================================================ */

/* Initial hidden states (only applied while .hero-text exists
   and hasn't been revealed yet — so the static fallback for
   crawlers/no-JS still shows the content). */
.hero-split .hero-text .eyebrow,
.hero-split .hero-text .hero-lead,
.hero-split .hero-text .hero-actions {
  opacity: 0;
  transform: translateY(28px);
  filter: blur(6px);
  will-change: opacity, transform, filter;
}

.hero-title .hero-line {
  display: block;
  position: relative;
  /* Soft gradient-edged mask wipes across each line from left to right —
     the reveal edge is feathered, not a hard cut, for an editorial feel.
     Gradient: opaque on the left, fading to transparent on the right.
     Initial position parks the mask entirely off-screen to the left so
     the element is fully hidden; the animation slides it to the right
     until the opaque half covers the entire line. */
  -webkit-mask-image: linear-gradient(95deg, #000 0%, #000 50%, transparent 100%);
          mask-image: linear-gradient(95deg, #000 0%, #000 50%, transparent 100%);
  -webkit-mask-size: 200% 100%;
          mask-size: 200% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: 200% 0;
          mask-position: 200% 0;
  /* Subtle physical "settle": a few pixels up + slightly tracked-in
     letter-spacing that opens to its natural value. */
  transform: translateY(10px);
  letter-spacing: -0.005em;
  will-change: mask-position, transform, letter-spacing;
}

/* --- Revealed state: kick off the choreographed sequence ---
   Timings tightened — full reveal now completes in ~1 s instead of
   the previous ~1.75 s so the headline doesn't keep the user
   waiting after the intro. */
.hero-split .hero-text.is-revealed .eyebrow {
  animation: heroFadeUp 380ms cubic-bezier(.2,.7,.2,1) 30ms forwards;
}

.hero-split .hero-text.is-revealed .hero-title .hero-line {
  animation: heroLineMask 600ms cubic-bezier(.22, .8, .2, 1) forwards;
}
.hero-split .hero-text.is-revealed .hero-title .hero-line:nth-of-type(1) { animation-delay:  60ms; }
.hero-split .hero-text.is-revealed .hero-title .hero-line:nth-of-type(2) { animation-delay: 160ms; }
.hero-split .hero-text.is-revealed .hero-title .hero-line:nth-of-type(3) { animation-delay: 260ms; }

.hero-split .hero-text.is-revealed .hero-lead {
  animation: heroFadeUp 450ms cubic-bezier(.2,.7,.2,1) 520ms forwards;
}

.hero-split .hero-text.is-revealed .hero-actions {
  animation: heroFadeUp 450ms cubic-bezier(.2,.7,.2,1) 650ms forwards;
}

@keyframes heroFadeUp {
  to { opacity: 1; transform: translateY(0); filter: blur(0); }
}

/* Mask sweeps in from the left (200% → 0%), line lifts a few px,
   letter-spacing opens to its natural value — all settling in concert. */
@keyframes heroLineMask {
  to {
    -webkit-mask-position: 0% 0;
            mask-position: 0% 0;
    transform: translateY(0);
    letter-spacing: -0.015em;
  }
}

/* Reduced-motion: skip animation, show final state immediately. */
@media (prefers-reduced-motion: reduce) {
  .hero-split .hero-text .eyebrow,
  .hero-split .hero-text .hero-lead,
  .hero-split .hero-text .hero-actions {
    opacity: 1;
    transform: none;
    filter: none;
  }
  .hero-title .hero-line {
    -webkit-mask-image: none;
            mask-image: none;
    transform: none;
    letter-spacing: -0.015em;
  }
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  border-radius: 999px;
  font-size: 15px;
  font-weight: 600;
  border: 1px solid transparent;
  transition: background .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
.btn .arrow { transition: transform .3s var(--ease); }
.btn:hover .arrow { transform: translateX(4px); }
.btn.small { padding: 10px 18px; font-size: 14px; }

.btn-primary {
  background: var(--ink);
  color: #fff;
}
.btn-primary:hover { background: var(--accent-blue); }

.btn-ghost {
  background: var(--accent-green);
  color: var(--ink);
  border-color: var(--accent-green);
}
.btn-ghost:hover,
.btn-ghost:focus-visible {
  background: #5dbf3a;
  border-color: #5dbf3a;
  color: var(--ink);
}
.btn-ghost:active {
  background: #4ea832;
  border-color: #4ea832;
}

.hero-meta {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(24px, 4vw, 56px);
  border-top: 1px solid var(--line);
  padding-top: clamp(28px, 4vw, 40px);
}

.hero-meta > div { display: flex; flex-direction: column; gap: 6px; }

.meta-num {
  font-family: var(--font-display);
  font-size: clamp(32px, 4vw, 56px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: -0.01em;
}
.meta-num small {
  font-size: 0.45em;
  font-family: var(--font-body);
  font-weight: 600;
  margin-left: 4px;
  vertical-align: 0.4em;
  color: var(--ink-soft);
}

.meta-label {
  font-size: 13px;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}

/* ---------- Entry: text/grid → crossfade → scroll-scrubbed video ----------
   One pinned section. Two CSS custom props drive the layered reveal:
     --p-intro : 0 during the intro phase, 0→1 across the crossfade,
                 1 once the video has fully taken over.
     --p       : 0→1 across the video-scrub portion (after the intro).
   Both are written by JS on every scroll frame. */

.entry {
  position: relative;
  /* White below the compact banner — the area under the pinned stage (which
     is now only as tall as its content) shows this, matching the rounded
     divider so there's no empty dark rectangle. */
  background: var(--bg);
  /* Section height is sized in JS to fit intro + transition + video duration.
     Fallback uses --vh100 (zoom-compensated) so the pre-JS layout doesn't
     over-allocate vertical space on monitors larger than the 16" baseline. */
  height: calc(6 * var(--vh100));
}

.entry.entry-scroll-driven {
  height: auto;
}

.entry.entry-scroll-driven .entry-stage {
  position: relative;
  top: auto;
}

/* Capability fallback (looping video or static still on weaker / data-saving /
   reduced-motion machines): no scroll-scrub runway — the banner becomes a
   normal-height section with the stage in normal flow. */
.entry.entry-fallback { height: auto; }
.entry.entry-fallback .entry-stage { position: relative; top: auto; }

.entry-stage {
  position: sticky;
  top: 0;
  /* Height = content (21:9 video + control panel + closing divider). The
     stage no longer fills the viewport, so the rounded divider sits right
     under the KPI strip and white shows below it. */
  height: auto;
  width: 100%;
  overflow: hidden;
  /* Stack the 21:9 video then the control panel (status row + KPI band)
     so the panel sits right at the bottom edge of the video. Decorative
     layers (vignette, meta) stay absolute and ignore this flow. */
  display: flex;
  flex-direction: column;
  /* Deep navy gradient with a subtle blue glow in the upper third — gives
     the scene the schematic / control-room feel of a HUD background. */
  background:
    radial-gradient(ellipse 120% 70% at 50% 30%, rgba(33, 171, 232, 0.18) 0%, transparent 55%),
    radial-gradient(ellipse 80% 60% at 80% 90%, rgba(126, 217, 87, 0.06) 0%, transparent 60%),
    linear-gradient(180deg, #06101e 0%, #03070f 50%, #020611 100%);
}

/* --- Layer 1 : grid backdrop (intro phase) --- */

.entry-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* No fill — let the dark stage background show through. The grid lines
     and accent blocks below are layered on top. */
  background: transparent;
  /* Soft mask so the grid is denser in the middle, fades toward edges. */
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 50%, black 35%, transparent 100%);
          mask-image: radial-gradient(ellipse 90% 80% at 50% 50%, black 35%, transparent 100%);
  opacity: calc(1 - var(--p-intro, 0));
}

.grid-bg-lines {
  position: absolute;
  inset: -60px;
  background-image:
    linear-gradient(to right, rgba(33, 171, 232, 0.22) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(33, 171, 232, 0.22) 1px, transparent 1px);
  background-size: 56px 56px;
  animation: gridDrift 22s linear infinite;
  opacity: 1;
}

/* Major grid every 4 cells — like a HUD primary divider */
.grid-bg-lines::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(33, 171, 232, 0.10) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(33, 171, 232, 0.10) 1px, transparent 1px);
  background-size: 224px 224px;
  pointer-events: none;
}

@keyframes gridDrift {
  0%   { background-position: 0 0; }
  100% { background-position: 56px 56px; }
}

/* Pulsing accent dots scattered on grid intersections — brighter on
   the dark scene so they read as "live nodes" on a HUD. */
.grid-dot {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: var(--accent-blue);
  box-shadow: 0 0 18px rgba(33,171,232,.85), 0 0 32px rgba(33,171,232,.4);
  opacity: 0;
  animation: gridDotPulse 4.5s ease-in-out infinite;
}

.grid-dot.d1 { top: 18%; left: 12%;  animation-delay: 0.0s;  background: var(--accent-blue);  box-shadow: 0 0 18px rgba(33,171,232,.85), 0 0 32px rgba(33,171,232,.4); }
.grid-dot.d2 { top: 30%; right: 18%; animation-delay: 1.1s;  background: var(--accent-green); box-shadow: 0 0 18px rgba(126,217,87,.85), 0 0 32px rgba(126,217,87,.4); }
.grid-dot.d3 { bottom: 22%; left: 24%; animation-delay: 2.2s; background: var(--accent-blue); box-shadow: 0 0 18px rgba(33,171,232,.85), 0 0 32px rgba(33,171,232,.4); }
.grid-dot.d4 { bottom: 30%; right: 14%; animation-delay: 3.3s; background: var(--accent-blue); box-shadow: 0 0 18px rgba(33,171,232,.85), 0 0 32px rgba(33,171,232,.4); }
.grid-dot.d5 { top: 60%;  left: 50%;  animation-delay: 0.6s;  background: var(--accent-green); box-shadow: 0 0 18px rgba(126,217,87,.85), 0 0 32px rgba(126,217,87,.4); }

@keyframes gridDotPulse {
  0%, 100% { opacity: 0;   transform: scale(0.6); }
  20%      { opacity: 0.9; transform: scale(1); }
  60%      { opacity: 0.3; transform: scale(0.85); }
}

/* --- Layer 3 : tagline text (fades out as intro ends) --- */

.entry-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  z-index: 3;
  /* Hold full opacity during the intro, fade out across the crossfade. */
  opacity: calc(1 - var(--p-intro, 0));
  transform: translateY(calc(var(--p-intro, 0) * -40px));
  pointer-events: none;
}

.entry-text > .container { max-width: 1100px; }

.tagline-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.65);
  margin: 0 0 24px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  justify-content: center;
}

.tagline-eyebrow::before,
.tagline-eyebrow::after {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--accent-blue);
  box-shadow: 0 0 8px rgba(33, 171, 232, 0.5);
}

.tagline-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(44px, 8vw, 130px);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  margin: 0 auto;
  max-width: 18ch;
  color: #ffffff;
  text-shadow: 0 0 40px rgba(33, 171, 232, 0.15);
}

.tagline-title .accent {
  color: var(--accent-green);
  text-shadow: 0 0 16px rgba(126, 217, 87, 0.6);
}

.tagline-hint {
  margin: clamp(40px, 6vw, 64px) 0 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

.tagline-hint .scroll-arrow {
  border-right-color: rgba(255, 255, 255, 0.85);
  border-bottom-color: rgba(255, 255, 255, 0.85);
}

@media (prefers-reduced-motion: reduce) {
  .grid-bg-lines { animation: none; }
  .grid-dot { animation: none; opacity: 0.4; }
}

/* --- Layer 2 : video (revealed by --p-intro, scrubbed by --p) --- */

/* Cinematic 21:9 banner box — flex item at the top of the stage, directly
   under the nav bar. The control panel flows immediately below it; the RBQ
   metadata sits in its bottom-right corner. */
.entry-video-wrap {
  position: relative;
  flex: 0 0 auto;
  width: var(--vw100);
  margin-left: calc(50% - var(--vw100) / 2);
  aspect-ratio: 5102 / 2160;
  overflow: hidden;
}

.entry-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  /* Keep the desktop banner rendered at native sharpness. Scaling the
     decoded video softens the 4K source, especially during scrub. */
  transform: none;
  transform-origin: center;
  filter: none;
  background: #050505;
  /* Always visible — the intro grid + tagline overlay that used to
     fade out across --p-intro have been removed, so the video should
     be fully visible from page load (no more dark-blue waiting state
     while --p-intro stays at 0). */
  opacity: 1;
  z-index: 1;
}

/* High-quality still that sits ABOVE the entry-video while the user
   hasn't started scrolling into the section yet. JS drops its opacity
   in lockstep with the scroll progress so the photo-quality first
   frame transitions cleanly into the live decoded video as the
   scrub begins. */
.entry-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  transform: none;
  transform-origin: center;
  filter: none;
  background: #050505;
  z-index: 2;
  pointer-events: none;
  opacity: 1;
  /* No fade — the HD still is only the first frame, then it cuts straight to
     the video/canvas scrub (handled by setStillFromScrub). */
  transition: none;
}

.entry-still.is-faded { opacity: 0; }

/* Scroll frame-sequence canvas. The MP4 remains as fallback, but the canvas
   gives accurate frame-by-frame scrubbing instead of browser video seeking. */
.entry-seq {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  background: #050505;
  z-index: 1;
}

.entry-vignette {
  display: none;
}

/* ---------- Scroll-synced scene captions ----------
   Four white lines, each revealed over its quarter of the video scrub.
   A green "highlighter" stripe is drawn left→right behind the active
   line, its width tied to scroll progress within that quarter (--hl). */
.entry-scenes {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  /* Bottom-center, lifted above the console overlay at the very bottom. */
  place-items: end center;
  padding: 0 clamp(20px, 8vw, 160px) clamp(120px, 17vh, 190px);
  pointer-events: none;
  text-align: center;
  /* Appear once the video is revealed (--p-intro), and fade out over the
     scrub tail (--scenes-out) so the end frame shows no text. */
  opacity: calc(var(--p-intro, 0) * var(--scenes-out, 1));
}

.entry-scene {
  grid-area: 1 / 1;            /* stack all lines in the same cell */
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(34px, 5.4vw, 92px);
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: #fff;
  text-shadow: 0 2px 28px rgba(0, 0, 0, 0.45);
  max-width: 20ch;
  /* Hidden until JS marks it active for the current scrub quarter. */
  opacity: 0;
  transition: opacity .35s var(--ease);
}

.entry-scene.is-active {
  opacity: 1;
}

/* Typewriter reveal: JS splits the line into .es-char spans and reveals
   them left→right as the user scrolls the scene's slice. The character
   currently being "typed" (the leading edge) is green, then settles white. */
.es-mark { display: inline; }

.es-char {
  opacity: 0;
  color: #fff;
  transition: opacity .12s linear, color .3s linear;
}

.es-char.is-shown { opacity: 1; }
.es-char.is-cursor { color: var(--accent-green); }

@media (prefers-reduced-motion: reduce) {
  .entry-scene { transition: opacity .2s linear; }
  .es-char { transition: none; }
}

/* ---------- Discrete RBQ metadata (corner of the banner) ----------
   Raw monospace text — no pill, no decorative dot, just a tagged
   line of metadata. Lowercase + slashes read like a build/version
   identifier rather than a UI element. */

.entry-meta {
  /* Top-right corner of the 21:9 video box (.entry-video-wrap). */
  position: absolute;
  top: clamp(14px, 2vw, 26px);
  right: clamp(14px, 2vw, 28px);
  z-index: 5;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 10.5px;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.5);
  /* Fades in with the video (kept on purpose) */
  opacity: var(--p-intro, 0);
  transform: translateY(calc((1 - var(--p-intro, 0)) * -6px));
  pointer-events: auto;
  user-select: text;
}

@media (max-width: 720px) {
  .entry-meta {
    right: 14px;
    font-size: 9.5px;
  }
}

/* ---------- Control panel (bottom overlay, appears with the video) ---------- */

.control-panel {
  /* Overlaid on the bottom of the 21:9 video (absolute inside the wrap), so
     the rounded divider below keeps its size. Bar on top, KPI console under. */
  /* Status bar overlaid on the bottom edge of the video. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  color: #fff;
  /* Present from the very start of the video (no intro-gated fade). */
  opacity: 1;
  font-family: var(--font-body);
  background:
    linear-gradient(180deg, rgba(8, 12, 15, 0.28) 0%, rgba(5, 5, 5, 0.72) 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 -18px 42px rgba(0, 0, 0, 0.18);
  backdrop-filter: blur(18px) saturate(145%);
  -webkit-backdrop-filter: blur(18px) saturate(145%);
}

/* ---------- KPI console — dark band below the video, above the divider ---------- */
.entry-console {
  flex: 0 0 auto;
  width: 100%;
  /* Same dark as the divider's top so the two read as one block that
     closes with the rounded white curve. */
  background: #050505;
  color: #fff;
  font-family: var(--font-body);
}

/* Tighten the gap between the control-panel bar (overlaid on the video)
   and the KPI strip below — drop the shared top padding here so the
   values sit snug under the status bar. The .cp-row-bottom keeps a 4px
   breath of its own. */
.entry-console .cp-inner {
  padding-top: 0;
}


.cp-strip {
  height: 2px;
  background: linear-gradient(
    to right,
    transparent 0%,
    var(--accent-blue) 20%,
    var(--accent-blue) 22%,
    transparent 22.5%,
    transparent 28%,
    var(--accent-green) 30%,
    var(--accent-green) 32%,
    transparent 32.5%,
    transparent 60%,
    var(--accent-blue) 62%,
    var(--accent-blue) 80%,
    transparent 80.5%
  );
  opacity: 0.85;
}

.cp-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 14px clamp(20px, 4vw, 40px) 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.cp-row {
  display: flex;
  align-items: center;
  gap: clamp(14px, 3vw, 32px);
}

.cp-row-top {
  justify-content: space-between;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255,255,255,.85);
}

.cp-row-bottom {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: clamp(14px, 2.4vw, 32px);
  padding-top: 4px;
  /* Always visible (no unroll). */
}

.cp-text { display: inline-flex; align-items: center; gap: 8px; }
.cp-dim  { color: rgba(255,255,255,.45); font-weight: 500; }

.cp-status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--accent-green);
}

.cp-led {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--accent-green);
  box-shadow: 0 0 0 0 rgba(126,217,87,.65);
  animation: cpLed 2s cubic-bezier(.4,0,.2,1) infinite;
}

@keyframes cpLed {
  0%   { box-shadow: 0 0 0 0 rgba(126,217,87,.65); }
  70%  { box-shadow: 0 0 0 12px rgba(126,217,87,0); }
  100% { box-shadow: 0 0 0 0 rgba(126,217,87,0); }
}

.cp-meta {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 28px);
  color: rgba(255,255,255,.75);
}

.cp-progress {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-width: 220px;
}

.cp-bar {
  position: relative;
  flex: 1;
  height: 4px;
  background: rgba(255,255,255,.12);
  border-radius: 2px;
  overflow: hidden;
}

.cp-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: linear-gradient(to right, var(--accent-blue), var(--accent-green));
  box-shadow: 0 0 8px rgba(33,171,232,.6);
  transition: width .08s linear;
}

.cp-pct {
  font-variant-numeric: tabular-nums;
  min-width: 38px;
  text-align: right;
  color: var(--accent-blue);
}

.cp-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: relative;
  padding-left: 14px;
}

.cp-cell::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: rgba(255,255,255,.10);
}

.cp-cell:nth-child(1)::before { background: var(--accent-blue); }
.cp-cell:nth-child(3)::before { background: var(--accent-green); }

.cp-label {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255,255,255,.55);
}

.cp-value {
  font-family: var(--font-display);
  font-size: clamp(22px, 2.8vw, 36px);
  line-height: 1;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  color: #fff;
}

.cp-value small {
  font-family: var(--font-body);
  font-size: 0.42em;
  font-weight: 600;
  margin-left: 4px;
  vertical-align: 0.5em;
  color: rgba(255,255,255,.6);
}

@media (max-width: 960px) {
  .cp-row-top { display: none; }
  .cp-row-bottom { grid-template-columns: repeat(3, 1fr); }
  .cp-cell:nth-child(n+4) { display: none; }
}

@media (max-width: 480px) {
  .cp-row-bottom { grid-template-columns: repeat(2, 1fr); }
  .cp-cell:nth-child(3) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .cp-led { animation: none; }
}

.video-banner-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding-bottom: clamp(60px, 9vw, 120px);
  color: #fff;
  /* Title fades in as you scroll into the section, then fades back out
     as you leave it. Bell-shaped curve via opacity tied to --p. */
  opacity: calc(1 - (var(--p, 0) - 0.5) * (var(--p, 0) - 0.5) * 4);
}

.video-banner-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255,255,255,.65);
  margin: 0 0 18px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

.video-banner-eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--accent-blue);
}

.video-banner-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 7vw, 110px);
  line-height: 0.96;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  margin: 0;
  max-width: 16ch;
  /* Slide up subtly with scroll — translates 30px → 0 → -30px through the section */
  transform: translateY(calc((0.5 - var(--p, 0)) * 60px));
}

.video-banner-title .accent { color: var(--accent-blue); }

.video-banner-hint {
  margin: 28px 0 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255,255,255,.7);
  display: inline-flex;
  align-items: center;
  gap: 12px;
  /* Fade out the hint as the user starts scrolling */
  opacity: calc(1 - var(--p, 0) * 4);
}

.scroll-arrow {
  width: 14px;
  height: 14px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  display: inline-block;
  animation: scrollHint 1.6s ease-in-out infinite;
}

@keyframes scrollHint {
  0%, 100% { transform: rotate(45deg) translate(-2px, -2px); opacity: 0.4; }
  50%      { transform: rotate(45deg) translate(2px, 2px);   opacity: 1; }
}

@media (max-width: 720px) {
  .video-banner-overlay { padding-bottom: 60px; }
}


/* ---------- Entry → next-section divider (interlocking tab) ---------- */

/* Wrapper that holds divider + parallax quote. Stays in normal flow so it
   appears AFTER the banner has fully released its sticky pin — never
   covers the control panel. The "section closing" feel comes from the
   curve at the top meeting the dark banner cleanly. */
.entry-cover {
  position: relative;
  z-index: 2;
  background: var(--bg);
}

.entry-divider {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  height: 128px;
  background:
    linear-gradient(180deg, rgba(3, 7, 15, 0.08), rgba(255, 255, 255, 0) 72%),
    var(--bg);
  line-height: 0;
  overflow: visible;
}

.entry-divider::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 16px;
  height: 112px;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 0%, rgba(3, 7, 15, 0.18), rgba(3, 7, 15, 0.07) 36%, rgba(3, 7, 15, 0) 72%);
  filter: blur(14px);
  opacity: 0.82;
}

.entry-divider-svg {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100px;
}

.entry-divider-svg path {
  fill: #050505;
}

.entry-divider-path--mobile {
  display: none;
}

/* ---------- Parallax quote section ---------- */

.parallax-quote {
  position: relative;
  padding: clamp(50px, 7vw, 100px) 0 clamp(20px, 3vw, 40px);
  background: var(--bg);
  overflow: hidden;
}

.parallax-text {
  font-family: var(--font-display);
  font-weight: 400;
  /* Sized down from clamp(44px, 6.8vw, 108px) so the longest of the three
     authored lines fits the container without re-wrapping. */
  font-size: clamp(36px, 5vw, 88px);
  line-height: 1.04;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  color: var(--ink);
  text-align: center;
  max-width: none;
  margin: 0 auto;
}

/* JS splits the paragraph into one .parallax-line per visual line so
   the blue shimmer can sweep them one at a time. JS updates --shimmer
   per line as the section scrolls through the viewport. */
.parallax-line {
  display: block;
  --shimmer: -25%;
  background-image: linear-gradient(
    100deg,
    var(--ink) 0%,
    var(--ink) calc(var(--shimmer) - 12%),
    var(--accent-green) var(--shimmer),
    var(--ink) calc(var(--shimmer) + 12%),
    var(--ink) 100%
  );
  color: transparent;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Keep the trailing period in the accent green regardless of shimmer position. */
.parallax-line .accent {
  -webkit-text-fill-color: var(--accent-green);
  color: var(--accent-green);
}

@media (prefers-reduced-motion: reduce) {
  .entry-cover { transform: none; }
}

/* ---------- Marquee ---------- */

/* ---------- Hero partners marquee ----------
   Small subtle band below the hero CTAs. Logos are rendered in mono
   (brightness 0 + low opacity) so they read as "social proof" without
   competing visually with the title or CTA buttons. Track is duplicated
   in the markup (sets A + B) and translated -50% in keyframes so the
   loop is perfectly seamless. Mask-image is on the track wrap only —
   the label above stays fully sharp. Wrap width is tuned so roughly
   4 logos sit inside the unmasked window at any time. */
.hero-partners {
  margin-top: clamp(12px, 1.4vw, 20px);
  max-width: 560px;
}

.hero-partners-label {
  margin: 0 0 18px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mute, #9aa0a8);
  padding-left: 2px;
}

.hero-partners-track-wrap {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
}

.hero-partners-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: heroPartnersMarquee 48s linear infinite;
}

.hero-partners-track img {
  height: clamp(52px, 5vw, 72px);
  width: auto;
  flex-shrink: 0;
  /* Spacing via margin (not flex gap) so the gap also exists AFTER the
     last logo of each set — keeps the -50% loop perfectly seamless. */
  margin-right: clamp(56px, 6.5vw, 96px);
  filter: brightness(0);
  transition: filter 0.3s var(--ease);
}

.hero-partners:hover .hero-partners-track {
  animation-play-state: paused;
}

@keyframes heroPartnersMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (min-width: 1101px) {
  .hero-partners-track {
    animation-name: heroPartnersMarqueePc;
    animation-duration: 52s;
    transform: translate3d(0, 0, 0);
    will-change: transform;
  }

  .hero-partners-track img {
    flex: 0 0 112px;
    width: 112px;
    height: 64px;
    max-width: none;
    object-fit: contain;
    margin-right: 72px;
  }
}

@keyframes heroPartnersMarqueePc {
  from { transform: translate3d(0, 0, 0); }
  /* 9 logos x (112 px slot + 72 px spacing) = 1656 px. */
  to   { transform: translate3d(-1656px, 0, 0); }
}

/* Marquee is slow ambient scrolling — safe for motion sensitivities.
   Keep it running regardless of prefers-reduced-motion. */

.marquee {
  background: var(--ink);
  color: #fff;
  padding: 22px 0;
  overflow: hidden;
  border-top: 1px solid var(--line-dark);
  border-bottom: 1px solid var(--line-dark);
}

/* Two copies of the keyword band toggle by viewport. Desktop (>1100) keeps it
   above #ferroviaire (.marquee--top) so the railway rails can still spill from
   Ferroviaire down into Avantage uninterrupted. Mobile AND tablet (<=1100) hide
   that copy and show .marquee--mid, which sits BELOW section 02 (between
   Ferroviaire and Avantage) — safe because the rails are display:none there. */
.marquee--mid { display: none; }

@media (max-width: 1100px) {
  .marquee--top { display: none; }
  .marquee--mid {
    display: block;
    /* Breathing room between the scrolling band and the sections above
       (02 Ferroviaire) and below (03 Avantage). */
    margin: clamp(30px, 9vw, 52px) 0;
  }
}

/* When the marquee is the last element of a .section (e.g. tucked
   into Services), let it sit flush with the section's bottom edge —
   the section's vertical padding would otherwise leave a strip of
   the dark background under the strip and break the transition to
   the next section. Also pulls the marquee up close to the section
   content above with a small gap so the bandeau still reads as its
   own visual unit. */
.section:has(> .marquee:last-child) {
  padding-bottom: 0;
}
.section > .marquee:last-child {
  margin-top: clamp(56px, 7vw, 96px);
}

.marquee-track {
  display: flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: 28px;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  white-space: nowrap;
  animation: marquee 50s linear infinite;
  /* Promote to its own compositor layer so the continuous scroll stays
     smooth (no stutter / jump) on mobile. */
  will-change: transform;
  backface-visibility: hidden;
}

/* Spacing via margin (not flex gap) so the gap also exists AFTER the last
   item of each set — required for the -50% loop to be perfectly seamless
   (flex gap leaves no trailing space, causing a jump each lap). */
.marquee-track > span {
  margin-right: 36px;
}

.marquee-track .dot {
  color: var(--accent-green);
  font-size: 14px;
}

@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@keyframes marqueeMobileRight {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}

/* ---------- Présentation (two-col) ---------- */

.two-col {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: start;
}

.col-left { position: sticky; top: 100px; }

.prose p {
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink-soft);
  margin: 0 0 20px;
}

.prose .lead {
  font-size: 22px;
  line-height: 1.45;
  color: var(--ink);
  font-weight: 500;
  margin-bottom: 32px;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--line);
}

.prose strong { color: var(--ink); font-weight: 600; }

.checklist {
  list-style: none;
  padding: 0;
  margin: 32px 0 0;
  display: grid;
  gap: 14px;
}

.checklist li {
  position: relative;
  padding-left: 28px;
  font-size: 15px;
  color: var(--ink-soft);
  line-height: 1.5;
}

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 14px;
  height: 8px;
  border-left: 2px solid var(--accent-blue);
  border-bottom: 2px solid var(--accent-blue);
  transform: rotate(-45deg);
}

/* ---------- Page hero (sub-pages like À propos) ---------- */

.page-hero {
  padding: clamp(140px, 16vw, 200px) 0 clamp(40px, 6vw, 72px);
  background: var(--bg);
}

/* Services page hero — substation banner on the right, fading to the page
   background on the left so the title/lead remain readable. The image
   itself already has a soft fade baked in; we layer a gradient on top to
   keep the left third clean across all viewport widths. */
.page-hero-services {
  position: relative;
  overflow: hidden;
  background:
    linear-gradient(to right, var(--bg) 0%, var(--bg) 28%, rgba(255,255,255,0) 60%),
    url('assets/services-hero.webp') right center / cover no-repeat,
    var(--bg);
  min-height: clamp(420px, 50vw, 620px);
}

.page-hero-services > .container {
  position: relative;
  z-index: 1;
  margin-left: 0;
  padding-left: clamp(16px, 4vw, 64px);
}

@media (max-width: 720px) {
  .page-hero-services {
    background:
      linear-gradient(to bottom, rgba(255,255,255,.92) 0%, rgba(255,255,255,.7) 60%, rgba(255,255,255,.95) 100%),
      url('assets/services-hero.webp') center / cover no-repeat,
      var(--bg);
    min-height: 380px;
  }
}

@media (min-width: 721px) and (max-width: 1100px) {
  .page-hero-services .services-lead-break {
    display: block;
  }
}

/* ---------- Services page : sticky banner + climbing dark section ---------- */

/* Wrapper holding both the banner and the dark detail section. The banner
   is sticky inside this wrapper so it stays put while the dark section
   scrolls up over it; sticky releases when the wrapper ends, so the
   banner doesn't bleed into the footer. */
.services-page-stack {
  position: relative;
}

.services-page-stack .page-hero-services {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* SVG wave divider between the banner and the dark section. Same style
   as the home page's .entry-divider — a smooth bezier curve where the
   dark area peaks up in the middle and tapers down at the sides, giving
   a soft, organic transition. */
.services-detail-divider {
  position: relative;
  z-index: 2;
  width: 100%;
  height: clamp(45px, 5.5vw, 75px);
  margin-top: clamp(-60px, -4.5vw, -40px); /* overlaps banner */
  margin-bottom: -1px; /* avoids 1px seam against the section below */
  line-height: 0;
  pointer-events: none;
}

.services-detail-divider svg {
  display: block;
  width: 100%;
  height: 100%;
}

.services-detail-divider svg path {
  fill: var(--bg-dark);
}

/* The dark section sits flush below the divider and shares its z-index
   so they climb over the sticky banner together on scroll. The
   border-top inherited from .section would draw a 1px hairline against
   the wave — kill it here. */
.section-dark.services-detail {
  position: relative;
  z-index: 2;
  border-top: 0;
}

.page-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 24px;
}

.page-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(72px, 12vw, 180px);
  line-height: 0.92;
  text-transform: uppercase;
  letter-spacing: -0.015em;
  margin: 0 0 24px;
}

.page-title .accent { color: var(--accent-blue); }

.page-lead {
  font-size: clamp(18px, 1.6vw, 22px);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 640px;
  margin: 0;
}

/* ---------- À propos (map + key info) ---------- */

.about-grid {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: clamp(40px, 6vw, 88px);
  align-items: start;
}

.about-info {
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 3vw, 40px);
}

/* GPS coordinate readout — feels like a technical spec sheet */
.about-coords {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(20px, 3vw, 40px);
  padding: 18px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-alt);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.coord-block {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.coord-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.coord-value {
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.02em;
}

.about-block { display: flex; flex-direction: column; gap: 10px; }

.about-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 0 0 4px;
}

.about-address {
  font-family: var(--font-display);
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.15;
  text-transform: uppercase;
  margin: 0;
}

.about-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  font-size: 13px;
  font-weight: 600;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 4px;
  align-self: flex-start;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}

.about-link:hover {
  color: var(--accent-blue);
  border-color: var(--accent-blue);
}

.about-link .arrow { transition: transform .3s var(--ease); }
.about-link:hover .arrow { transform: translateX(4px); }

/* Compact stats list */
.about-stats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.about-stats li {
  display: grid;
  grid-template-columns: minmax(120px, auto) 1fr;
  gap: 20px;
  align-items: baseline;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.about-stats li:first-child { border-top: 1px solid var(--line); }

.about-stats strong {
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 26px);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: -0.005em;
}

.about-stats span {
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.4;
}

.about-stats li.about-stats-cmeq {
  align-items: center;
  padding-top: 18px;
  padding-bottom: 18px;
}

.about-stats-cmeq strong {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  line-height: 1;
}

.about-stats-cmeq img {
  width: 30px;
  height: 30px;
  object-fit: contain;
}

.about-stats-cmeq span {
  line-height: 1.38;
}

.about-contact-quick a {
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 26px);
  text-transform: uppercase;
  border-bottom: 1px solid var(--line);
  padding: 8px 0;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}

.about-contact-quick a:first-of-type { border-top: 1px solid var(--line); }
.about-contact-quick a:hover { color: var(--accent-blue); border-color: var(--accent-blue); }

/* Map frame */
.about-map {
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: sticky;
  top: 100px;
}

.map-frame {
  position: relative;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-alt);
}

.map-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  filter: grayscale(15%) contrast(1.02);
}

.map-overlay {
  position: absolute;
  top: 14px;
  right: 14px;
  left: auto;
  pointer-events: none;
  z-index: 2;
  max-width: calc(100% - 28px);
}

.map-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(15, 16, 18, 0.78);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.map-tag::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--accent-green);
  box-shadow: 0 0 8px rgba(126, 217, 87, 0.65);
  animation: cpLed 2s cubic-bezier(.4,0,.2,1) infinite;
}

.map-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}

.map-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--accent-blue);
}

.map-sep { color: var(--line); }

@media (max-width: 960px) {
  .about-grid { grid-template-columns: 1fr; }
  .about-map { position: static; }
  .map-frame { aspect-ratio: 4 / 3; }
}

/* ---------- Services ---------- */

.services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
}

@media (max-width: 1100px) {
  .services-grid { grid-template-columns: 1fr; }
}

.service-card {
  position: relative;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius);
  padding: clamp(28px, 3.2vw, 44px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Cap the card so it never exceeds the viewport. Bullet list inside
     (.service-scroll) takes the remaining space and scrolls
     internally when content overflows. */
  max-height: min(720px, 78vh);
  scroll-margin-top: 100px;
  transition: border-color .3s var(--ease), background .3s var(--ease), transform .3s var(--ease);
}

/* Scrollable bullet area inside each card. `flex: 1` absorbs the
   space remaining after the fixed header (num + title + blurb) and
   the optional CTA at the bottom. `min-height: 0` is required for
   the flex child to be allowed to shrink + scroll. */
.service-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding-right: 8px;
  margin-right: -8px;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
}

.service-scroll::-webkit-scrollbar { width: 6px; }
.service-scroll::-webkit-scrollbar-track { background: transparent; }
.service-scroll::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.16);
  border-radius: 3px;
}
.service-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.3); }

.service-card:hover {
  border-color: rgba(255,255,255,.18);
  background: rgba(255,255,255,.06);
  transform: translateY(-2px);
}

/* Top accent bars removed — matches the home-service treatment. */

.service-num {
  /* Same monospace section-number typo as the railway dots / .num-mark.
     Per-service colour kept. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 500;
  font-size: 18px;
  letter-spacing: 0.08em;
  color: var(--accent-blue);
  margin: 0 0 14px;
}

.services-grid > article:nth-child(2) .service-num { color: var(--accent-green); }
.services-grid > article:nth-child(3) .service-num { color: var(--accent-red); }

/* Title row — title fills the available space, icon sits to the right
   vertically centered. Mirrors .home-service-title-row. */
.service-title-row {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 24px);
  margin-bottom: 20px;
}

.service-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.2vw, 32px);
  line-height: 1.05;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  margin: 0;
  color: #fff;
}

.service-icon {
  width: clamp(56px, 4.8vw, 80px);
  height: clamp(56px, 4.8vw, 80px);
  object-fit: contain;
  flex-shrink: 0;
  /* Faint white halo — just enough lift against the dark card. */
  filter: drop-shadow(0 3px 10px rgba(255, 255, 255, 0.1));
  transition: transform .35s var(--ease);
  pointer-events: none;
}

.service-card:hover .service-icon {
  transform: scale(1.06) translateY(-2px);
}

.service-blurb {
  font-size: 15px;
  line-height: 1.6;
  color: rgba(255,255,255,.72);
  margin: 0 0 22px;
}

.service-list {
  list-style: none;
  padding: 0;
  margin: 0 0 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.service-list li {
  position: relative;
  padding-left: 24px;
  font-size: 14px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
}

/* Checkmark bullets — same as home-service-list (border-left +
   border-bottom rotated to form a ✓ shape, coloured per accent). */
.service-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 14px;
  height: 8px;
  border-left: 2px solid var(--accent-blue);
  border-bottom: 2px solid var(--accent-blue);
  transform: rotate(-45deg);
  transform-origin: top left;
  background: none;
}

.services-grid > article:nth-child(2) .service-list li::before {
  border-color: var(--accent-green);
}

.services-grid > article:nth-child(3) .service-list li::before {
  border-color: var(--accent-red);
}

/* Sub-sections inside a service-card (card 02 Opérations + Maintenance). */
.service-sub {
  scroll-margin-top: 100px;
}

.service-sub + .service-sub {
  margin-top: clamp(20px, 2.6vw, 32px);
  padding-top: clamp(18px, 2.4vw, 28px);
  border-top: 1px solid rgba(255,255,255,.08);
}

.service-sub-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(16px, 1.4vw, 20px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,.85);
  margin: 0 0 14px;
}

/* Urgence CTA on services page — mirrors .home-service-cta--urgence. */
.service-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: auto;
  padding: 0 0 4px;
  align-self: flex-start;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, 0.28);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), gap 0.2s var(--ease);
}

.service-cta:hover { gap: 14px; }

.service-cta--urgence {
  color: var(--accent-red);
  border-color: rgba(255, 59, 48, 0.4);
}

.service-cta--urgence:hover {
  color: #fff;
  border-color: var(--accent-red);
}

/* Optional lead paragraph under section-title (used on home services summary). */
.section-lead {
  font-size: clamp(16px, 1.4vw, 19px);
  line-height: 1.55;
  max-width: 60ch;
  margin: 0;
  color: var(--ink-soft);
}
.section-lead.light { color: rgba(255,255,255,.72); }

/* ---------- Home services summary (3-column teaser linking to services.html) ---------- */

.home-services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
  margin-bottom: clamp(40px, 5vw, 64px);
}

.home-service {
  position: relative;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius);
  padding: clamp(28px, 3.2vw, 44px);
  overflow: hidden;
  /* Flex column lets the CTA at the bottom anchor itself via
     margin-top:auto, so every card's button lines up regardless of
     bullet count or text length above. */
  display: flex;
  flex-direction: column;
  transition: border-color .3s var(--ease), background .3s var(--ease), transform .3s var(--ease);
}

.home-service:hover {
  border-color: rgba(255,255,255,.18);
  background: rgba(255,255,255,.06);
  transform: translateY(-2px);
}

/* Top accent bars removed — relying on icon + title for visual
   identity. Per-card accent colours preserved via list checkmarks
   and CTA hover states. */

/* Service icon — sits at the right end of the header row, next to
   the numbered eyebrow. No absolute positioning, no padding-right on
   the title needed. */
.home-service-icon {
  width: clamp(56px, 4.8vw, 80px);
  height: clamp(56px, 4.8vw, 80px);
  object-fit: contain;
  flex-shrink: 0;
  /* Faint white halo — just enough lift against the dark card
     without competing with the icon's own colours. */
  filter: drop-shadow(0 3px 10px rgba(255, 255, 255, 0.1));
  transition: transform .35s var(--ease);
  pointer-events: none;
}

.home-service:hover .home-service-icon {
  transform: scale(1.06) translateY(-2px);
}

.home-service-num {
  /* Same monospace section-number typo as the railway dots / .num-mark.
     Per-service colour kept. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 500;
  font-size: 18px;
  letter-spacing: 0.08em;
  color: var(--accent-blue);
  margin: 0 0 14px;
}

/* Title row — title fills the available space, icon sits to the
   right, vertically centered. Eliminates the dead zone the previous
   icon-on-its-own-row layout introduced above the title. */
.home-service-title-row {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 24px);
  margin-bottom: 20px;
}

.home-service-title-row .home-service-title {
  flex: 1;
  min-width: 0;
  margin: 0;
}

.home-services-grid > article:nth-child(2) .home-service-num { color: var(--accent-green); }
.home-services-grid > article:nth-child(3) .home-service-num { color: var(--accent-red); }

.home-service-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.2vw, 32px);
  line-height: 1.05;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  margin: 0 0 14px;
  color: #fff;
}

.home-service-text {
  font-size: 15px;
  line-height: 1.6;
  color: rgba(255,255,255,.72);
  margin: 0 0 22px;
}

/* Bulleted feature list. Each item gets a checkmark in the card's
   accent colour (blue / green / red for cards 1 / 2 / 3). */
.home-service-list {
  list-style: none;
  margin: 0 0 28px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-service-list li {
  position: relative;
  padding-left: 24px;
  font-size: 14px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
}

.home-service-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 14px;
  height: 8px;
  border-left: 2px solid var(--accent-blue);
  border-bottom: 2px solid var(--accent-blue);
  transform: rotate(-45deg);
  transform-origin: top left;
}

.home-services-grid > article:nth-child(2) .home-service-list li::before {
  border-color: var(--accent-green);
}

.home-services-grid > article:nth-child(3) .home-service-list li::before {
  border-color: var(--accent-red);
}

/* CTA — text link with an underline that brightens on hover and a
   gap that widens slightly to nudge the arrow forward. */
.home-service-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: auto;
  padding: 0 0 4px;
  align-self: flex-start;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, 0.28);
  transition:
    color 0.2s var(--ease),
    border-color 0.2s var(--ease),
    gap 0.2s var(--ease);
}

.home-service-cta .arrow {
  transition: transform 0.2s var(--ease);
}

.home-service-cta:hover {
  color: var(--accent-blue);
  border-color: var(--accent-blue);
  gap: 14px;
}

.home-services-grid > article:nth-child(2) .home-service-cta:hover {
  color: var(--accent-green);
  border-color: var(--accent-green);
}

/* Urgence CTA — red dot pulses, text is red by default, hover
   brings it to white-on-red for emphasis. */
.home-service-cta--urgence {
  color: var(--accent-red);
  border-color: rgba(255, 59, 48, 0.4);
}

.home-service-cta--urgence:hover {
  color: #fff;
  border-color: var(--accent-red);
}

.urgence-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--accent-red);
  box-shadow: 0 0 8px rgba(255, 59, 48, 0.8);
  animation: urgenceDotPulse 1.4s cubic-bezier(.4, 0, .6, 1) infinite;
  flex-shrink: 0;
}

@keyframes urgenceDotPulse {
  0%, 100% { opacity: 1;    box-shadow: 0 0 10px rgba(255, 59, 48, 0.9); }
  50%      { opacity: 0.35; box-shadow: 0 0 3px  rgba(255, 59, 48, 0.3); }
}

.home-services-cta {
  display: flex;
  justify-content: center;
}

.btn-on-dark {
  background: #fff;
  color: var(--ink);
}
.btn-on-dark:hover { background: var(--accent-blue); color: #fff; }

@media (max-width: 900px) {
  .home-services-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   03 — Nos partenaires : logo grid of trusted companies.
   Premium layout with scroll-reveal stagger, accent lines,
   and generous sizing.
   ============================================================ */
.section-partners {
  position: relative;
  background: var(--bg);
  overflow: hidden;
  /* Trim the bottom padding so the empty space below the HUD halo
     matches the space above it — keeps the visible halo visually
     centred around the logo grid. */
  padding-bottom: clamp(40px, 5vw, 80px);
}

/* HUD grid backdrop is painted on .partners-grid below so the 176 px
   tiles align perfectly with the 176 px cells. Kept here intentionally
   blank to avoid a stale section-level pattern fighting the grid. */

.section-partners > .container {
  position: relative;
  z-index: 1;
}

.section-partners .section-head {
  text-align: center;
  max-width: 800px;
  margin: 0 auto clamp(20px, 3vw, 32px);
}

.section-partners .about-row-num {
  justify-content: center;
}

.section-partners .section-title {
  font-size: clamp(36px, 4.6vw, 72px);
}

.partners-subtitle {
  font-size: clamp(15px, 1.3vw, 18px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 52ch;
  margin: 0 auto clamp(48px, 6vw, 80px);
  text-align: center;
}

/* 5 × 3 grid of landscape-oriented cells (220 × 140 px) so each
   "rectangle" reads as horizontal rather than square. 9 logos are
   placed on cols 1, 3, 5 — cols 2 and 4 stay empty so the grid
   spreads out across an airy 1100 × 420 rectangle (ratio 2.62:1).
   Logos still sit in the geometric middle of their tile and the
   HUD backdrop is tiled at the same 220 × 140 size so lines align. */
.partners-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, 220px);
  grid-auto-rows: 140px;
  width: 1100px; /* 5 × 220 — explicit so the ::before HUD backdrop
                    and its radial mask center on the real grid box */
  max-width: 100%;
  margin-inline: auto;
  gap: 0;
}

/* Place each logo on an odd column so the gaps land in cols 2 and 4. */
.partners-grid > .partner-logo:nth-child(3n + 1) { grid-column: 1; }
.partners-grid > .partner-logo:nth-child(3n + 2) { grid-column: 3; }
.partners-grid > .partner-logo:nth-child(3n + 3) { grid-column: 5; }

/* HUD grid lines, anchored to .partners-grid so the 176 px tiles
   line up exactly with the 176 px cells. Extended outward with a
   negative inset (multiple of 176 px) so the faint pattern bleeds
   past the grid without breaking alignment. Soft radial mask fades
   the outer tiles into the page. */
.partners-grid::before {
  content: '';
  position: absolute;
  /* Symmetric vertical bleed (-40 px top & bottom). Horizontally
     the backdrop spans the viewport BUT is capped at 1920 px so
     ultra-wide monitors (24" / 27" / 4K) render the same halo +
     fade geometry as a 16" laptop. Without the cap, the HUD halo
     grows with viewport width and the proportions read differently
     than the design was tuned for. */
  top: -40px;
  bottom: -40px;
  left: 50%;
  width: min(var(--vw100), 1920px);
  max-width: 1920px;
  transform: translateX(-50%);
  /* Same trick as .about-bg-grid: each line has an 8 px gap at BOTH
     ends, so the lines are centered along the cell edges but never
     reach the corners. No crossing, no L, no +. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='140'><g stroke='%230a0a0a' stroke-opacity='0.10' stroke-width='1' fill='none' stroke-linecap='butt'><line x1='8' y1='0.5' x2='212' y2='0.5'/><line x1='0.5' y1='8' x2='0.5' y2='132'/></g></svg>");
  background-size: 220px 140px;
  /* Horizontally center the tile pattern (so tile boundaries line up
     with the partner-grid cell boundaries, since both are centered
     on the viewport). Vertically offset by +40 px to compensate for
     the -40 px top inset so tile rows land on the cell rows. */
  background-position: 50% 40px;
  /* Composite mask: two linear gradients intersected so every edge
     fades to 0 and the intersection rounds the four corners.
     – vertical (8 % / 92 %): symmetric top/bottom fades; logos sit
       inside the plateau.
     – horizontal (7 % / 93 %): because the backdrop now spans 100 vw
       these stops are relative to the viewport, so the HUD literally
       fades to 0 at the left and right screen edges. 7 % keeps the
       1100 px logo grid safely inside the solid plateau down to
       ~1280 px viewports. */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 8%, #000 92%, transparent 100%),
    linear-gradient(to right,  transparent 0%, #000 7%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(to bottom, transparent 0%, #000 8%, #000 92%, transparent 100%),
    linear-gradient(to right,  transparent 0%, #000 7%, #000 93%, transparent 100%);
          mask-composite: intersect;
  pointer-events: none;
  z-index: 0;
}

.partners-grid > .partner-logo { position: relative; z-index: 1; }

/* ---- Cursor-following green hover (Terminal-style) ----
   A second layer (::after) that paints the same HUD grid in accent
   green plus a soft green radial glow, both clipped to a circular
   region around the mouse pointer. JS updates --mx / --my on
   pointermove; the layer fades in/out on grid hover. */
.partners-grid {
  --mx: 50%;
  --my: 50%;
}

.partners-grid::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  transition: opacity .35s var(--ease);
  /* Two stacked layers, both repeating/spanning the partners-grid:
     1) green HUD grid (same SVG geometry as the dark one)
     2) soft radial glow centered on the cursor */
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='140'><g stroke='%237ed957' stroke-opacity='0.85' stroke-width='1.2' fill='none' stroke-linecap='butt'><line x1='8' y1='0.5' x2='212' y2='0.5'/><line x1='0.5' y1='8' x2='0.5' y2='132'/></g></svg>"),
    radial-gradient(circle 220px at var(--mx) var(--my), rgba(126, 217, 87, 0.22), transparent 75%);
  background-size: 220px 140px, 100% 100%;
  background-position: 50% 0, 0 0;
  background-repeat: repeat, no-repeat;
  /* Single mask: only the circular region around the cursor is
     visible — outside the circle both layers fade to 0. */
  -webkit-mask-image: radial-gradient(circle 260px at var(--mx) var(--my), #000 25%, transparent 80%);
          mask-image: radial-gradient(circle 260px at var(--mx) var(--my), #000 25%, transparent 80%);
}

.partners-grid:hover::after { opacity: 1; }

@media (hover: none) {
  .partners-grid::after { display: none; }
}

/* HUD intersection blips — small dots that fade in and out at the
   cell-corner positions we cleared of "+" crossings. Each blip's
   (--x, --y) is the absolute position in the partners-grid local
   coords (0..1100 × 0..420), and --d is its own animation delay so
   the blips fire out of phase. */
.partners-blip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 4px;
  height: 4px;
  background: var(--ink, #0a0a0a);
  border-radius: 50%;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
  pointer-events: none;
  z-index: 0;
  animation: partnersBlip 4.8s var(--d, 0s) ease-in-out infinite;
}

@keyframes partnersBlip {
  0%, 100% { opacity: 0;   transform: translate(-50%, -50%) scale(0.4); }
  45%, 55% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .partners-blip { animation: none; opacity: 0; }
}

/* ---- Logo "breathing" ----
   Same checkerboard wave as before, but the whole cycle is tighter:
   8 s total (was 10 s) so the pattern flips more often. The single
   logo's breath duration stays the same in absolute time (≈ 1.6 s,
   now 20 % of an 8 s cycle); the 0.5 s stagger inside each group is
   unchanged. The phase-B group starts at 4 s instead of 5 s, so the
   two phases sit back-to-back with a brief breath between them.
     Phase A  (0 → ~3.1 s)  Phase B  (4 → ~7.6 s)
       N B N                  B N B
       B N B                  N B N
       N B N                  B N B  */
.partners-grid.is-revealed > .partner-logo > img {
  animation: partnerBreathe 8s var(--breathe-d, 0s) ease-in-out infinite;
}

@keyframes partnerBreathe {
  /* 0 → 10 % : rise to peak (+5 % scale, −3 px lift)
     10 → 20 %: fall back to neutral
     20 → 100 %: rest at neutral (other logos take their turn). */
  0%              { transform: translateY(0)    scale(1); }
  10%             { transform: translateY(-3px) scale(1.05); }
  20%, 100%       { transform: translateY(0)    scale(1); }
}

/* Group B (edges) — staggered sequence inside phase A. */
.partners-grid > .partner-logo:nth-child(2) > img { --breathe-d: 0.0s; }
.partners-grid > .partner-logo:nth-child(4) > img { --breathe-d: 0.5s; }
.partners-grid > .partner-logo:nth-child(6) > img { --breathe-d: 1.0s; }
.partners-grid > .partner-logo:nth-child(8) > img { --breathe-d: 1.5s; }

/* Group A (corners + center) — staggered sequence inside phase B
   (starts 4 s into the 8 s cycle, right after group B finishes). */
.partners-grid > .partner-logo:nth-child(1) > img { --breathe-d: 4.0s; }
.partners-grid > .partner-logo:nth-child(3) > img { --breathe-d: 4.5s; }
.partners-grid > .partner-logo:nth-child(5) > img { --breathe-d: 5.0s; }
.partners-grid > .partner-logo:nth-child(7) > img { --breathe-d: 5.5s; }
.partners-grid > .partner-logo:nth-child(9) > img { --breathe-d: 6.0s; }

@media (prefers-reduced-motion: reduce) {
  .partners-grid.is-revealed > .partner-logo > img { animation: none; }
}

@media (max-width: 720px) {
  .partners-blip {
    display: block;
    left: var(--mobile-x);
    top: var(--mobile-y);
    background: rgba(10, 10, 10, 0.58);
    z-index: 2;
  }

  .partners-grid > .partners-blip:nth-of-type(1)  { --mobile-x: 0%;   --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(2)  { --mobile-x: 50%;  --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(3)  { --mobile-x: 100%; --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(4)  { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(5)  { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(6)  { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(7)  { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(8)  { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(9)  { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(10) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(11) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(12) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(13) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(14) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(15) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(16) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(17) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(18) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(n+19) { display: none; }
}

.partner-logo {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Reduced vertical padding so the +40 % logo can breathe inside
     the 140 px tall cell. */
  padding: 14px 18px;
  transition: transform .35s var(--ease);
}

.partner-logo:hover {
  transform: translateY(-3px);
}

.partner-logo img {
  max-width: 100%;
  max-height: 104px; /* 74 × 1.4 — +40 % glyph size */
  width: auto;
  object-fit: contain;
  filter: brightness(0) opacity(0.7);
  transition: filter .4s var(--ease), transform .4s var(--ease);
}

.partner-logo:hover img {
  filter: brightness(0) opacity(1);
}

@media (max-width: 720px) {
  .partners-grid {
    --partner-mobile-row: clamp(172px, 50vw, 224px);
    width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: var(--partner-mobile-row);
  }
  /* Drop the desktop column-skip placement so the 2-col mobile grid
     fills naturally. */
  .partners-grid > .partner-logo:nth-child(3n + 1),
  .partners-grid > .partner-logo:nth-child(3n + 2),
  .partners-grid > .partner-logo:nth-child(3n + 3) { grid-column: auto; }
  .partner-logo img { max-height: 84px; } /* 60 × 1.4 */
}

@media (max-width: 420px) {
  .partners-grid {
    width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: var(--partner-mobile-row);
  }
}

.partner-logo:hover img {
  filter: brightness(0) opacity(1);
  /* !important so hover beats the partnerBreathe animation that
     also targets the img's transform. */
  transform: scale(1.06) !important;
}

/* ---- Scroll-reveal pop-in zoom ----
   Logos start small + transparent and snap up to full size with a
   slight overshoot (ease-out-back) so each tile reads as a "pop"
   rather than a slide — no translate, no blur, pure scale + opacity. */
.partners-grid > .partner-logo {
  opacity: 0;
  transform: scale(0.5);
  transform-origin: center;
  transition:
    opacity 520ms cubic-bezier(.34, 1.56, .64, 1),
    transform 520ms cubic-bezier(.34, 1.56, .64, 1),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease),
    background .35s var(--ease);
  will-change: transform, opacity;
}

.partners-grid.is-revealed > .partner-logo {
  opacity: 1;
  transform: scale(1);
}

/* Diagonal stagger — (col + row) × 200ms — top-left fires first,
   bottom-right last. 200ms step is deliberately slow so each wave
   reads individually. */
.partners-grid.is-revealed > .partner-logo:nth-child(1) { transition-delay:   0ms; } /* r0 c0 */
.partners-grid.is-revealed > .partner-logo:nth-child(2) { transition-delay: 200ms; } /* r0 c1 */
.partners-grid.is-revealed > .partner-logo:nth-child(3) { transition-delay: 400ms; } /* r0 c2 */
.partners-grid.is-revealed > .partner-logo:nth-child(4) { transition-delay: 200ms; } /* r1 c0 */
.partners-grid.is-revealed > .partner-logo:nth-child(5) { transition-delay: 400ms; } /* r1 c1 */
.partners-grid.is-revealed > .partner-logo:nth-child(6) { transition-delay: 600ms; } /* r1 c2 */
.partners-grid.is-revealed > .partner-logo:nth-child(7) { transition-delay: 400ms; } /* r2 c0 */
.partners-grid.is-revealed > .partner-logo:nth-child(8) { transition-delay: 600ms; } /* r2 c1 */
.partners-grid.is-revealed > .partner-logo:nth-child(9) { transition-delay: 800ms; } /* r2 c2 */

/* Hover gives the popped-in logo a small extra punch */
.partners-grid.is-revealed > .partner-logo:hover {
  transform: scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  .partners-grid > .partner-logo {
    opacity: 1;
    transform: none;
    transition: border-color .35s var(--ease), box-shadow .35s var(--ease), background .35s var(--ease);
  }
}


/* ============================================================
   04 — L'avantage G.E.T. : light section, bold 2×2 grid of
   strengths. Each card has a top accent line whose colour rotates
   through the brand palette so the four cards read as a
   distinctive lineup rather than four copies of the same block.
   ============================================================ */
.section-advantage {
  background: var(--bg);
}

.section-advantage .section-head {
  text-align: center;
  max-width: 920px;
  margin: 0 auto clamp(48px, 6vw, 80px);
}

.section-advantage .about-row-num {
  justify-content: center;
}

.section-advantage .section-title {
  font-size: clamp(36px, 4.6vw, 72px);
}

.advantage-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(20px, 2.6vw, 40px);
}

.advantage-card {
  position: relative;
  /* Horizontal layout: icon column on the left, num/title/text on the
     right. Tight column gap; vertical gap between text rows. */
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: clamp(20px, 2vw, 32px);
  row-gap: 6px;
  align-items: center;
  padding: clamp(24px, 2.8vw, 40px) clamp(24px, 2.8vw, 40px);
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition:
    transform .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease);
}

/* Top accent line — full width, 3px tall, brand colour rotation. */
.advantage-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--accent-blue);
  transform-origin: left center;
  transition: transform .4s var(--ease);
}

.advantage-grid > .advantage-card:nth-child(1)::before { background: var(--accent-blue); }
.advantage-grid > .advantage-card:nth-child(2)::before { background: var(--accent-green); }
.advantage-grid > .advantage-card:nth-child(3)::before { background: var(--accent-blue); }
.advantage-grid > .advantage-card:nth-child(4)::before { background: var(--accent-green); }

.advantage-mobile-marquee {
  display: none;
}

.advantage-card:hover {
  transform: translateY(-3px);
  border-color: var(--ink);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.08);
}

/* Icon column — spans all three rows so it sits vertically centered
   against the num + title + text stack on the right. */
.advantage-icon {
  display: block;
  grid-column: 1;
  grid-row: 1 / span 3;
  align-self: center;
  width: clamp(64px, 5vw, 88px);
  height: clamp(64px, 5vw, 88px);
  object-fit: contain;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.12));
  transition: transform .35s var(--ease);
}

.advantage-card:hover .advantage-icon {
  transform: scale(1.06) translateY(-2px);
}

.advantage-title {
  grid-column: 2;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.2vw, 34px);
  line-height: 1.05;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 10px;
}

.advantage-text {
  grid-column: 2;
  font-size: clamp(14px, 1vw, 16px);
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
  max-width: 38ch;
}

@media (max-width: 720px) {
  .advantage-grid { grid-template-columns: 1fr; }
  .advantage-card { column-gap: 18px; }
  .advantage-icon { width: 60px; height: 60px; }

  .section-advantage.has-advantage-mobile-marquee .advantage-grid {
    display: none;
  }

  .section-advantage.has-advantage-mobile-marquee .advantage-mobile-marquee {
    --advantage-mobile-gap: 14px;
    --advantage-mobile-card-w: min(82vw, 350px);
    display: grid;
    gap: 14px;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    padding-inline: 0;
    overflow: hidden;
  }

  .advantage-mobile-row {
    overflow-x: hidden;
    overflow-y: visible;
    overscroll-behavior-x: contain;
    scroll-snap-type: none;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
  }

  .advantage-mobile-row::-webkit-scrollbar {
    display: none;
  }

  .advantage-mobile-track {
    display: flex;
    width: max-content;
    will-change: transform;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    transform: translate3d(0, 0, 0);
    animation-duration: 24s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
  }

  .advantage-mobile-row--top .advantage-mobile-track {
    animation-name: advantageMobileRollRight;
  }

  .advantage-mobile-row--bottom .advantage-mobile-track {
    animation-name: advantageMobileRollLeft;
  }

  .advantage-mobile-row.is-paused .advantage-mobile-track {
    animation-play-state: paused;
  }

  .advantage-mobile-set {
    display: flex;
    flex: 0 0 auto;
    gap: var(--advantage-mobile-gap);
    padding-inline-end: var(--advantage-mobile-gap);
  }

  .advantage-mobile-track > .advantage-mobile-set > .advantage-card {
    flex: 0 0 var(--advantage-mobile-card-w);
    min-height: 172px;
    scroll-snap-align: none;
    column-gap: 18px;
    padding: 24px 22px;
  }

  .advantage-mobile-track > .advantage-mobile-set > .advantage-card:nth-child(odd)::before {
    background: var(--accent-blue);
  }

  .advantage-mobile-track > .advantage-mobile-set > .advantage-card:nth-child(even)::before {
    background: var(--accent-green);
  }

  .advantage-mobile-track > .advantage-mobile-set > .advantage-card:hover {
    transform: none;
    border-color: var(--line);
    box-shadow: none;
  }

  @keyframes advantageMobileRollLeft {
    from { transform: translate3d(0, 0, 0); }
    to { transform: translate3d(-50%, 0, 0); }
  }

  @keyframes advantageMobileRollRight {
    from { transform: translate3d(-50%, 0, 0); }
    to { transform: translate3d(0, 0, 0); }
  }

  @media (prefers-reduced-motion: reduce) {
    .advantage-mobile-track {
      animation: none;
      transform: none;
    }
  }
}

/* Tighten the seam between the Avantage section and the Projets
   section that follows: drop the bottom padding of Avantage and the
   border-top + top padding of the next section, so the two blocks
   read as a continuous flow rather than two stacked panels. */
/* The 32-px-ish bottom padding was tuned for the old tight Avantage →
   Projets seam. Now Avantage is followed by section 03 Notre mission,
   whose dark video panel + wavy clip-path top need real breathing
   room above so the white cards above don't read as crashing into
   the curve — bump the bottom padding considerably. */
.section-advantage {
  padding-bottom: clamp(120px, 13vw, 220px);
}

/* Seam projets → partners — same tight transition. */
.section#projets {
  padding-bottom: clamp(32px, 4vw, 56px);
}

/* Tighten the gap between the Projets header (05 / title / CTA) and the
   photo band below it (default .section-head margin-bottom is ~80px). */
#projets .section-head {
  margin-bottom: clamp(16px, 2.4vw, 36px);
}

.section#projets + .section-partners {
  border-top: 0;
  padding-top: clamp(32px, 4vw, 56px);
}


/* ---------- Projets ---------- */

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 15px;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 4px;
}
.link-arrow .arrow { transition: transform .3s var(--ease); }
.link-arrow:hover .arrow { transform: translateX(4px); }

/* Reference layout: 3 columns × 2 rows pinned to a 16:9 frame.
     col 1: tall image (spans both rows)
     col 2: 2 images stacked
     col 3: tall image (spans both rows)
   The container's aspect-ratio drives the overall size; minmax(0,1fr)
   rows force the cells to honour those tracks instead of being pushed
   open by the intrinsic dimensions of the project images. */
/* === Bande tournante (marquee) ==========================================
   The projets gallery auto-scrolls in an endless loop — no manual scrolling,
   the photos drift on their own. The tall/stack column pattern is unchanged;
   we lay two identical 9-photo grids side by side and translate the track by
   -50%. Each grid carries a trailing margin equal to the column gap, so the
   two halves tile perfectly and the -50% loop is seamless (no jump).
   Column widths derive from one height var (--pg-h):
     stack cell width = row-height * 16/9   (--pg-wide)
     tall  cell width = full-height * 9/16  (--pg-tall)                    */
.projects-marquee {
  --pg-h: clamp(460px, 62vh, 720px);
  --pg-gap: clamp(16px, 2vw, 28px);
  --pg-row: calc((var(--pg-h) - var(--pg-gap)) / 2);
  --pg-wide: calc(var(--pg-row) * 16 / 9);
  --pg-tall: calc(var(--pg-h) * 9 / 16);
  --marquee-duration: 60s;
  /* Full-bleed, edge to edge. --vw100 keeps the bleed viewport-aligned
     under page zoom (raw 100vw would overflow). */
  width: var(--vw100);
  margin-left: calc(50% - var(--vw100) / 2);
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.projects-marquee.is-dragging {
  cursor: grabbing;
}

.projects-marquee.is-js-marquee .projects-track {
  animation: none;
}

.projects-track {
  display: flex;
  width: max-content;
  height: var(--pg-h);
  animation: projects-marquee var(--marquee-duration) linear infinite;
  will-change: transform;
}

/* Pause when the visitor hovers, so they can look at a photo. */
.projects-marquee:hover .projects-track {
  animation-play-state: paused;
}

@keyframes projects-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.projects-grid {
  display: grid;
  grid-template-rows: var(--pg-row) var(--pg-row);
  grid-template-columns:
    var(--pg-tall) var(--pg-wide) var(--pg-tall)
    var(--pg-wide) var(--pg-tall) var(--pg-wide);
  gap: var(--pg-gap);
  height: var(--pg-h);
  width: max-content;
  flex: 0 0 auto;
  /* Trailing gap so the two grids tile seamlessly under the -50% loop. */
  margin-right: var(--pg-gap);
}

.project {
  position: relative;
  margin: 0;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--bg-alt);
  cursor: zoom-in;
}

/* Explicit placement — alternating columns that continue off-screen:
   col1 tall · col2 stack(top/bottom) · col3 tall · col4 stack · col5 tall · col6 stack */
.projects-grid > .project:nth-child(1) { grid-column: 1; grid-row: 1 / span 2; }
.projects-grid > .project:nth-child(2) { grid-column: 2; grid-row: 1; }
.projects-grid > .project:nth-child(3) { grid-column: 2; grid-row: 2; }
.projects-grid > .project:nth-child(4) { grid-column: 3; grid-row: 1 / span 2; }
.projects-grid > .project:nth-child(5) { grid-column: 4; grid-row: 1; }
.projects-grid > .project:nth-child(6) { grid-column: 4; grid-row: 2; }
.projects-grid > .project:nth-child(7) { grid-column: 5; grid-row: 1 / span 2; }
.projects-grid > .project:nth-child(8) { grid-column: 6; grid-row: 1; }
.projects-grid > .project:nth-child(9) { grid-column: 6; grid-row: 2; }

/* Cards are always visible — the band is always on screen and moving,
   and both grid copies must show, so there is no scroll-triggered pop-in. */
.projects-grid > .project {
  opacity: 1;
  transform: none;
}

/* Reduced-motion: stop the auto-scroll and let the visitor scroll the band
   by hand through a single set (the duplicate copy is hidden). */
@media (prefers-reduced-motion: reduce) {
  .projects-track {
    animation: none;
  }
  .projects-marquee {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .projects-grid[aria-hidden="true"] {
    display: none;
  }
}

/* Mobile: keep the band, just shorter so the two stacked 16:9 cells fit a
   phone screen. It auto-scrolls the same way. */
@media (max-width: 720px) {
  .projects-marquee {
    --pg-h: clamp(300px, 52vh, 430px);
    --pg-gap: 14px;
    --marquee-duration: 42s;
  }
}

.project img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s var(--ease), filter .4s var(--ease);
  filter: grayscale(15%);
}

/* These two source photos have their landscape orientation baked into the
   pixels. Swap the rendered box dimensions before rotating so they keep the
   same 9:16 crop as the other tall cards at every gallery height. */
.project img.project-image--rotate-cw {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--pg-h);
  height: var(--pg-tall);
  max-width: none;
  transform: translate(-50%, -50%) rotate(90deg);
}

.project:hover img {
  transform: scale(1.04);
  filter: grayscale(0%);
}

.project:hover img.project-image--rotate-cw {
  transform: translate(-50%, -50%) rotate(90deg) scale(1.04);
}

.project figcaption {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  background: linear-gradient(to top, rgba(0,0,0,.7), transparent);
  color: #fff;
  font-size: 13px;
}

.project-tag {
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 11px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(255,255,255,.15);
  backdrop-filter: blur(6px);
}

.project-name { opacity: 0.9; }

/* (Old 12-column responsive layout removed — superseded by the
   3-column × 2-row grid defined above with explicit nth-child
   placement.) */

.project-lightbox {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  place-items: center;
  padding: clamp(18px, 4vw, 48px);
  background: rgba(0, 0, 0, 0.86);
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms var(--ease);
}

.project-lightbox.is-open {
  opacity: 1;
  pointer-events: auto;
}

.project-lightbox__figure {
  width: min(1180px, 100%);
  max-height: 88vh;
  margin: 0;
  display: grid;
  gap: 14px;
}

.project-lightbox__image {
  display: block;
  width: 100%;
  max-height: min(78vh, 820px);
  object-fit: contain;
  background: #050505;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
}

.project-lightbox__media {
  display: grid;
  place-items: center;
  min-width: 0;
}

.project-lightbox__figure.is-rotated {
  width: min(90vw, calc(min(78vh, 820px) * 0.75));
}

.project-lightbox__figure.is-rotated .project-lightbox__media {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: #050505;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
}

.project-lightbox__figure.is-rotated .project-lightbox__image {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 133.333%;
  height: 100%;
  max-width: none;
  max-height: none;
  transform: translate(-50%, -50%) rotate(90deg);
  box-shadow: none;
}

.project-lightbox__caption {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #fff;
  font-size: 14px;
  line-height: 1.35;
}

.project-lightbox__tag {
  flex: 0 0 auto;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 11px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
}

.project-lightbox__name {
  opacity: 0.78;
}

.project-lightbox__button {
  position: absolute;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.88);
  color: #050505;
  font: 400 30px/1 var(--font-body);
  cursor: pointer;
  transition:
    transform 180ms var(--ease),
    background 180ms var(--ease);
}

.project-lightbox__button:hover {
  background: #fff;
  transform: translateY(-1px);
}

.project-lightbox__close {
  top: clamp(14px, 3vw, 28px);
  right: clamp(14px, 3vw, 28px);
  font-size: 26px;
}

.project-lightbox__prev,
.project-lightbox__next {
  top: 50%;
  transform: translateY(-50%);
}

.project-lightbox__prev:hover,
.project-lightbox__next:hover {
  transform: translateY(calc(-50% - 1px));
}

.project-lightbox__prev { left: clamp(14px, 3vw, 28px); }
.project-lightbox__next { right: clamp(14px, 3vw, 28px); }

body.project-lightbox-open {
  overflow: hidden;
}

@media (max-width: 720px) {
  .project-lightbox {
    padding: 18px;
  }

  .project-lightbox__figure {
    gap: 12px;
  }

  .project-lightbox__image {
    max-height: 72vh;
  }

  .project-lightbox__caption {
    align-items: flex-start;
    font-size: 13px;
  }

  .project-lightbox__button {
    width: 46px;
    height: 46px;
    border-radius: 8px;
    font-size: 26px;
  }

  .project-lightbox__prev,
  .project-lightbox__next {
    top: auto;
    bottom: 22px;
    transform: none;
  }

  .project-lightbox__prev:hover,
  .project-lightbox__next:hover {
    transform: translateY(-1px);
  }

  .project-lightbox__prev { left: 18px; }
  .project-lightbox__next { right: 18px; }
}

/* ---------- 03. Expertise ferroviaire ---------- */

.section-railway {
  background: var(--bg);
  /* Tighten top padding so the title sits closer to the dark
     section above (was inheriting the default .section
     clamp(80px, 11vw, 160px)). */
  padding-top: clamp(40px, 5vw, 80px);
}

.section-railway .section-head {
  text-align: center;
  max-width: 880px;
  margin: 0 auto clamp(48px, 6vw, 80px);
}

.section-railway .about-row-num {
  justify-content: center;
}

.section-railway .section-title {
  font-size: clamp(36px, 4.6vw, 72px);
}

.section-railway .section-lead {
  margin: 0 auto;
}

.railway-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
  max-width: 1180px;
  margin-inline: auto;
}

.railway-mobile-stage,
.railway-mobile-progress {
  display: contents;
}

.railway-mobile-controls,
.railway-mobile-progress {
  display: none;
}

/* ---- Top-down railway map animation ----
   Decorative SVG that spans the railway section background AND
   spills below the section seam into the top of #avantage so the
   dashed tracks visually terminate inside the next section.

   The trick to make this work without an unholy z-index war:
   1. .section-mission ~ section assigns z-index: 1 to every
      following section — creating a stacking context that would
      trap the railway-route's overflow inside #ferroviaire's
      paint area. We OPT BOTH sections OUT of that stacking
      context with `z-index: auto` (ID specificity wins over the
      `.section-mission ~ section` rule).
   2. Both sections now sit in the root stacking context, so the
      railway-route (position:absolute, z-index:1) and avantage's
      .container (position:relative, z-index:2) can be ordered
      freely — rails draw above avantage's static white bg, and
      the container's eyebrow + title still cleanly draw above
      the rails. */
.section-railway { position: relative; overflow: visible; }
.section-railway > .container { position: relative; z-index: 2; }

/* Target #avantage directly (not the +.section adjacency) so the mobile
   .marquee--mid div inserted between them does not break this rail-spill
   stacking rule on desktop. */
#ferroviaire,
#avantage { z-index: auto; }

/* Tighter vertical rhythm at the railway → avantage seam: the rails
   need room to spill down without crashing into card content above
   or eyebrow text below, but the original 160-px gap on each side
   pushed avantage too far down. Halve the inner padding on both
   ends. */
#ferroviaire {
  /* Tight padding at both ends: the section's content (eyebrow,
     title) sits close to the rail tips above and the rails extend
     freely below into avantage via the negative inset on
     .railway-route. */
  padding-top: clamp(48px, 6vw, 96px);
  padding-bottom: clamp(40px, 5vw, 80px);
  /* Hero → Ferroviaire transition: the default 1-px .section border
     reads as an out-of-place grey line above the rails — drop it so
     the railway canvas reads as continuous with the hero column above. */
  border-top: 0;
}
#avantage {
  border-top: 0;
  padding-top: clamp(40px, 5vw, 80px);
  position: relative;
}
#avantage > .container {
  position: relative;
  z-index: 3;
}

.railway-route {
  position: absolute;
  /* Negative bottom inset = the SVG canvas extends well below the
     section, into the empty top zone of avantage. With
     preserveAspectRatio="none" on the inner SVG, the viewBox
     stretches over the taller area, so the BL/BR paths naturally
     terminate deep inside the next section instead of being cut at
     the seam. z-index 1 places the rails just above the static
     section backgrounds; avantage's .container is z-index 3 so it
     always wins above the overhanging rails. */
  inset: 0 0 -360px 0;
  z-index: 1;
  pointer-events: none;
}

.railway-route-svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ---- Dashed rail fragments — short ties like a railroad track ---- */
.railway-route .route-path {
  fill: none;
  stroke: #cfd6d6;
  stroke-width: 0.8;
  stroke-linecap: butt;
  stroke-linejoin: round;
  /* "- - - -" pattern: 6 vb units visible + 5 vb units gap → looks
     like rail ties at the section's render size. */
  stroke-dasharray: 6 5;
  opacity: 0.95;
}

/* ---- Subtle coloured reflection behind each pod ----
   The visible stroke is almost transparent — we don't want a hard
   coloured line tracing the rail. What carries the effect is a
   single soft drop-shadow in the STM hue, which renders as a
   diffuse halo that "leaks" out of the moving stroke segment.
   Combined with the short trail length, the result reads as a
   light gradient reflection trailing the black pod rather than a
   neon-stripe convoy. */
.railway-route .route-trail {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-opacity: 0.18;
  filter: drop-shadow(0 0 10px currentColor);
}
.railway-route .trail-vert   { stroke: #00843d; color: #7ed957; }
.railway-route .trail-bleu   { stroke: #0083ca; color: #21abe8; }
.railway-route .trail-jaune  { stroke: #f9d70b; color: #ffd60a; }
.railway-route .trail-orange { stroke: #ee7100; color: #ff7a1a; }

/* ---- Black pods (one per corner) ----
   A single dark capsule per fragment — no wagons, no window. The
   capsule stays black; what carries the colour is the stacked
   drop-shadow halo per line below, which renders as a soft radial
   glow that "ignites" around the pod as it moves along the rail. */
.railway-route .train .pod {
  fill: #0a0a0a;
}

/* Each train is invisible until its staggered begin time. An inline
   <set attributeName="opacity" to="1" begin="Ns"> flips it on when its
   first pass starts, so trains appear in the order: vert → bleu →
   orange + jaune. */
.railway-route .train {
  opacity: 0;
}

/* Dedicated halo element drawn directly under each pod. A solid
   coloured ellipse with heavy blur is far brighter per pixel than
   stacked drop-shadows on the pod itself — and gives precise
   control over intensity via opacity. The ellipse travels with the
   train (same <g>, same animateMotion) so the halo stays glued to
   the moving capsule. */
.railway-route .train .halo {
  filter: blur(18px);
  opacity: 0.28;
}
.railway-route .train-vert   .halo { fill: #7ed957; }
.railway-route .train-bleu   .halo { fill: #21abe8; }
.railway-route .train-jaune  .halo { fill: #ffd60a; }
.railway-route .train-orange .halo { fill: #ff7a1a; }

/* Plus a softer ambient drop-shadow on the pod itself so the
   colour also bleeds OUTSIDE the ellipse's tight blur radius —
   gives the halo a two-stage falloff (bright core + soft bloom)
   without diluting the core like a single very wide blur would. */
.railway-route .train-vert   .pod { filter: drop-shadow(0 0 20px #00843d); }
.railway-route .train-bleu   .pod { filter: drop-shadow(0 0 20px #0083ca); }
.railway-route .train-jaune  .pod { filter: drop-shadow(0 0 20px #f9d70b); }
.railway-route .train-orange .pod { filter: drop-shadow(0 0 20px #ee7100); }

@media (min-width: 721px) {
  .railway-route .trail-orange { stroke: #0083ca; color: #21abe8; }
  .railway-route .trail-jaune { stroke: #00843d; color: #7ed957; }

  .railway-route .train-orange .halo { fill: #21abe8; }
  .railway-route .train-jaune .halo { fill: #7ed957; }

  .railway-route .train-orange .pod { filter: drop-shadow(0 0 20px #0083ca); }
  .railway-route .train-jaune .pod { filter: drop-shadow(0 0 20px #00843d); }
}

@media (prefers-reduced-motion: reduce) {
  .railway-route .route-train { display: none; }
}

.railway-card {
  position: relative;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition:
    transform .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease),
    background .35s var(--ease);
}

.railway-dots {
  display: none;
}

/* Top media block — image fills the top of the card, the project
   name sits over it with a soft dark gradient for legibility. */
.railway-media {
  position: relative;
  margin: 0;
  aspect-ratio: 16 / 11;
  overflow: hidden;
  background: var(--ink);
}

.railway-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease);
}

.railway-card:hover .railway-media img {
  transform: scale(1.04);
}

/* Top-anchored dark gradient — keeps the title readable in the
   top-right corner regardless of the image content underneath. */
.railway-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.72) 0%,
    rgba(0, 0, 0, 0) 55%
  );
  pointer-events: none;
}

.railway-card:hover {
  transform: translateY(-3px);
  border-color: var(--ink);
  background: #fff;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.08);
}

/* Top accent bar — blue for in-service contracts, green for upcoming. */
.railway-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--accent-blue);
}

.railway-grid > .railway-card:nth-child(4)::before,
.railway-grid > .railway-card:nth-child(5)::before {
  background: var(--accent-green);
}

.railway-status {
  display: inline-block;
  align-self: flex-start;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid;
}

.railway-status--active {
  color: var(--accent-blue);
  background: rgba(33, 171, 232, 0.08);
  border-color: rgba(33, 171, 232, 0.3);
}

.railway-status--upcoming {
  color: var(--accent-green);
  background: rgba(126, 217, 87, 0.1);
  border-color: rgba(126, 217, 87, 0.35);
}

.railway-name {
  position: absolute;
  top: clamp(14px, 1.6vw, 22px);
  left: clamp(14px, 1.6vw, 22px);
  z-index: 1;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(16px, 1.7vw, 24px);
  line-height: 0.95;
  text-align: left;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  color: #fff;
  margin: 0;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
}

.railway-desc {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
  padding: clamp(20px, 2.4vw, 32px);
}

@media (max-width: 1100px) {
  .railway-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Section 02 (Expertise ferroviaire): the mobile full-bleed swipe carousel
   (cards + dots + progress + arrows + eyebrow overlaid on the image) now also
   covers the tablet range, so it matches mobile. The dot/swipe JS isn't
   viewport-gated, so it works here automatically. Overrides the ≤1100 2-col
   grid. Mobile (≤720) is unchanged (still inside this range). */
@media (max-width: 1100px) {
  #ferroviaire {
    /* Generous, consistent breathing room around section 02 (railway) on
       mobile so the 01->02 and 02->03 seams read airy / modern. #ferroviaire
       had been trimmed well below the ~66px the other sections get. */
    padding-top: clamp(48px, 12vw, 72px);
    padding-bottom: clamp(56px, 14vw, 80px);
    overflow: hidden;
  }

  /* Seam 03 (Avantage) -> 04 (Notre mission): give the white cards room to
     breathe before the mission video panel + its wavy clip-path top. The
     global mobile `.section` padding trims the desktop 120px bottom down to
     ~66px, so restore a generous gap here (ID beats the `.section` rule). */
  #avantage {
    padding-bottom: clamp(92px, 21vw, 132px);
  }

  .section-railway {
    padding-top: 0;
    padding-bottom: clamp(46px, 12vw, 72px);
    overflow: hidden;
  }

  .section-railway > .container {
    width: 100%;
    max-width: none;
    padding-inline: 0;
  }

  /* Drop the title entirely and overlay the "02 Expertise ferroviaire"
     eyebrow (badge + label) on the TOP-LEFT of the railway image. The
     section-head is taken out of flow (absolute) so the image rises to the
     top of the section; .section-railway > .container is position:relative,
     so top/left anchor over the image. White text + shadow keep the label
     legible over the photo. */
  .section-railway .section-head {
    position: absolute;
    top: clamp(14px, 4.5vw, 22px);
    left: var(--pad);
    right: auto;
    z-index: 4;
    margin: 0;
    padding: 0;
    max-width: calc(100% - var(--pad) * 2);
    text-align: left;
  }

  .section-railway .about-row-num {
    flex: none;
    margin: 0;
    gap: 10px;
    white-space: nowrap;
    color: #fff;
  }

  /* Badge "02" — same translucent dark-section chip as the "04 Notre mission"
     eyebrow (.about-row-num.light .num-mark). Can't add the .light class in
     HTML (it would wreck the badge on the white desktop section), so the
     style is mirrored here. The green is-charged flash paints on top. */
  .section-railway .about-row-num .num-mark {
    background: rgba(255, 255, 255, 0.04);
    border-color: var(--line-dark);
    color: #fff;
  }

  /* Restore the "Expertise ferroviaire" label, now white for the photo. */
  .section-railway .row-num-label {
    display: inline;
    color: #fff;
    text-shadow: 0 1px 10px rgba(0, 0, 0, 0.65);
  }

  /* Title removed on mobile — the eyebrow on the image replaces it. */
  .section-railway .section-title {
    display: none;
  }

  .section-railway .section-lead {
    display: none;
  }

  .railway-route {
    display: none;
  }

  .railway-mobile-stage {
    --railway-mobile-image-height: clamp(390px, 62svh, 560px);
    position: relative;
    display: block;
    width: 100%;
    overflow: hidden;
  }

  .railway-grid {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 100vw;
    grid-template-columns: none;
    max-width: none;
    margin-inline: 0;
    padding: 0;
    gap: 0;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .railway-grid::-webkit-scrollbar {
    display: none;
  }

  .railway-card {
    --railway-card-accent: var(--accent-blue);
    display: grid;
    grid-template-columns: minmax(116px, 36vw) minmax(0, 1fr);
    grid-template-rows: var(--railway-mobile-image-height) auto auto;
    column-gap: clamp(14px, 4vw, 22px);
    row-gap: 5px;
    width: 100vw;
    min-width: 0;
    padding: 0 0 clamp(24px, 7vw, 38px);
    border: 0;
    border-radius: 0;
    background: var(--bg);
    box-shadow: none;
    transform: none;
    overflow: visible;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }

  .railway-card:nth-child(2) {
    --railway-card-accent: var(--accent-green);
  }

  .railway-card:nth-child(3) {
    --railway-card-accent: var(--accent-blue);
  }

  .railway-card::before {
    display: none;
  }

  .railway-card::after {
    content: attr(data-mobile-title);
    grid-column: 1;
    grid-row: 3;
    align-self: start;
    padding-left: var(--pad);
    font-family: var(--font-display);
    font-size: clamp(21px, 6vw, 28px);
    line-height: 0.95;
    letter-spacing: 0;
    text-transform: uppercase;
    color: var(--ink);
  }

  .railway-media {
    display: contents;
  }

  .railway-media::after {
    display: none;
  }

  .railway-media img {
    position: relative;
    grid-column: 1 / -1;
    grid-row: 1;
    display: block;
    width: 100%;
    height: var(--railway-mobile-image-height);
    object-fit: cover;
    transform: none;
  }

  .railway-card:hover {
    transform: none;
    border-color: transparent;
    background: var(--bg);
    box-shadow: none;
  }

  .railway-card:hover .railway-media img {
    transform: none;
  }

  .railway-name {
    position: relative;
    top: auto;
    left: auto;
    z-index: 1;
    grid-column: 1;
    grid-row: 2;
    align-self: end;
    padding-left: var(--pad);
    padding-top: clamp(18px, 5vw, 26px);
    font-size: clamp(26px, 7.6vw, 38px);
    line-height: 0.9;
    color: rgba(10, 10, 10, 0.22);
    text-shadow: none;
  }

  .railway-desc {
    grid-column: 2;
    grid-row: 2 / span 2;
    align-self: start;
    padding: clamp(18px, 5vw, 26px) var(--pad) 0 0;
    background: transparent;
    border-radius: 0;
    color: rgba(10, 10, 10, 0.58);
    font-size: clamp(13.5px, 3.65vw, 15.5px);
    line-height: 1.34;
  }

  .railway-dots {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: clamp(18px, 5.5vw, 28px);
    margin: 0 0 14px;
    padding: 0 var(--pad);
  }

  .railway-dot {
    width: auto;
    height: auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.25;
    letter-spacing: 0.1em;
    color: rgba(10, 10, 10, 0.16);
    cursor: pointer;
    transition: color 0.32s var(--ease), opacity 0.32s var(--ease);
  }

  .railway-dot.is-active {
    width: auto;
    background: transparent;
    box-shadow: none;
    color: var(--ink);
  }

  .railway-mobile-progress {
    position: relative;
    display: block;
    width: auto;
    height: 2px;
    margin: 0 var(--pad);
    overflow: hidden;
    background: rgba(10, 10, 10, 0.09);
  }

  .railway-mobile-progress-bar {
    position: absolute;
    left: 0;
    top: 0;
    width: clamp(56px, 17vw, 74px);
    height: 100%;
    border-radius: 999px;
    background: var(--ink);
    transform: translateX(var(--railway-mobile-progress-x, 0px));
    transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
  }

  .railway-mobile-controls {
    position: absolute;
    right: var(--pad);
    top: calc(var(--railway-mobile-image-height) - 68px);
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .railway-mobile-control {
    display: inline-grid;
    place-items: center;
    width: 52px;
    height: 52px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.38);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.54);
    color: rgba(10, 10, 10, 0.78);
    box-shadow: none;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    cursor: pointer;
    transition:
      transform 0.22s var(--ease),
      background 0.22s var(--ease),
      opacity 0.22s var(--ease);
  }

  .railway-mobile-control--next {
    background: rgba(255, 255, 255, 0.92);
    color: var(--ink);
  }

  .railway-mobile-control span {
    display: block;
    font-family: var(--font-body);
    font-size: 36px;
    font-weight: 400;
    line-height: 0.82;
    transform: translateY(-1px);
  }

  .railway-mobile-control:active {
    transform: scale(0.96);
  }

  .railway-mobile-control:disabled {
    opacity: 0.38;
    cursor: default;
    pointer-events: none;
  }

}

/* ---------- Contact ---------- */

.section-contact {
  /* Same --ink shade as services, footer and marquee so every dark
     surface on the page reads as one continuous tone (was --bg-dark
     #0d0d0d, now --ink #0a0a0a). */
  background: var(--ink);
  color: #fff;
  border-color: var(--line-dark);
  position: relative;
  /* Drop the default .section border-top so the wavy trapezoid
     overlay isn't underlined by a stray grey line. */
  border-top: 0;
  /* Tighten the gap to the footer (half of the default section
     padding) so contact + footer read as one continuous dark block. */
  padding-bottom: clamp(40px, 5vw, 70px);
}

/* Match the dark-section card style (.service-card): translucent
   white on a dark backdrop instead of opaque white-on-cream. */
.section-contact .contact-card,
.section-contact .contact-address {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.section-contact .contact-card:hover,
.section-contact .contact-address:hover {
  border-color: rgba(255, 255, 255, 0.25);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35);
}

/* Re-light the inside-card typography so it stays readable on the
   dark background (the default --ink / --ink-soft / --ink-mute
   colours assume a light section). */
.section-contact .contact-eyebrow,
.section-contact .contact-card:not(.contact-card--urgence) > .contact-eyebrow {
  /* Card titles ("Administration", "Informations générales") —
     pure white so they're as legible as the body. The double
     selector beats the more-specific cream rule defined further
     down the file. Urgence keeps its red accent. */
  color: #ffffff;
}
.section-contact .contact-card-list .list-label {
  color: rgba(255, 255, 255, 0.55);
}
.section-contact .contact-card-list a,
.section-contact .contact-address-line,
.section-contact .contact-address-link {
  color: #ffffff;
}
.section-contact .contact-address-link {
  border-bottom-color: rgba(255, 255, 255, 0.4);
}

/* "07" num-mark badge — match section 02 (.about-row-num.light)
   exactly: translucent white background, dark-section border,
   white digit. */
.section-contact .about-row-num .num-mark {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--line-dark);
  color: #fff;
}

/* Make every text descendant inherit the white colour, including
   the elements that have explicit colour rules elsewhere
   (titles, leads, eyebrows, etc.). */
.section-contact,
.section-contact .section-title,
.section-contact .contact-title,
.section-contact .contact-lead,
.section-contact .about-row-num,
.section-contact .num-mark,
.section-contact p,
.section-contact h1,
.section-contact h2,
.section-contact h3,
.section-contact h4,
.section-contact span,
.section-contact a {
  color: #ffffff;
}

/* Top-edge inverted-trapezoid notch — painted as a WHITE overlay
   instead of a mask so the cut-out is always solid white, even when
   the section above has a dark / video background. Sides of the
   trapezoid are mostly straight, with small quadratic fillets at
   each of the four corners (less rounded than soft cubic curves). */
.section-contact::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: clamp(22px, 2.4vw, 40px);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'><path d='M 60 0 C 200 0 260 100 380 100 L 1060 100 C 1180 100 1240 0 1380 0 L 60 0 Z' fill='%23ffffff'/></svg>");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: 0 0;
  pointer-events: none;
  z-index: 2;
}

.section-contact > .container { position: relative; z-index: 1; }

.contact-head {
  max-width: 880px;
  margin: 0 auto clamp(48px, 6vw, 80px);
  text-align: center;
}

.contact-head .about-row-num {
  justify-content: center;
}

.contact-title {
  font-size: clamp(32px, 4vw, 58px);
  line-height: 1.05;
  max-width: none;
  margin: 0 auto 24px;
}

.contact-lead {
  max-width: 620px;
  margin: 0 auto;
  color: var(--ink-soft);
  font-size: clamp(16px, 1.3vw, 19px);
  line-height: 1.55;
}

.contact-layout {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: clamp(24px, 3vw, 40px);
  align-items: stretch;
}

/* Map column */

.contact-map-col {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.8vw, 24px);
  min-width: 0;
}

.contact-map {
  position: relative;
  flex: 1 1 auto;
  min-height: 360px;
  border-radius: var(--radius);
  overflow: hidden;
  background: #1a1a1a;
  border: 1px solid var(--line-dark);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Dark-mode the Google Maps iframe via CSS filter — Google's embed
   doesn't expose a dark-mode flag, so we invert + hue-rotate to flip
   the light theme into a dark one while preserving original hues
   (the hue-rotate undoes the colour shift from invert). */
.contact-map iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  filter: invert(0.92) hue-rotate(180deg) brightness(0.95) contrast(0.9) grayscale(0.15);
}

.contact-address {
  position: relative;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(20px, 2vw, 28px) clamp(22px, 2.4vw, 32px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  overflow: hidden;
}

/* Top accent bar — green to mirror the section-04 pattern */
.contact-address::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--accent-green);
}

.contact-address-icon {
  width: clamp(40px, 4vw, 56px);
  height: clamp(40px, 4vw, 56px);
  flex-shrink: 0;
  object-fit: contain;
}

.contact-address-text { min-width: 0; }

.contact-address-line {
  margin: 8px 0 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 26px);
  line-height: 1.15;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.contact-address-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 2px;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), gap 0.2s var(--ease);
  white-space: nowrap;
}

.contact-address-link:hover {
  color: var(--accent-blue);
  border-color: var(--accent-blue);
  gap: 12px;
}

/* Info column */

.contact-info-col {
  display: grid;
  grid-template-rows: 1.25fr 1fr 1fr;
  gap: clamp(16px, 1.8vw, 24px);
  min-width: 0;
}

.contact-card {
  position: relative;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(24px, 2.8vw, 40px);
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: clamp(20px, 2.4vw, 36px);
  row-gap: 12px;
  align-items: center;
  overflow: hidden;
  transition:
    transform .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease);
}

.contact-card:hover {
  transform: translateY(-3px);
  border-color: var(--ink);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.08);
}

/* Top accent bar — mirrors the section-04 advantage cards pattern.
   Uses ::after because ::before on .contact-card--urgence is already
   taken by its radial-gradient overlay. Stack from top to bottom:
   Urgence = blue, Admin = green, Infos = blue. */
.contact-card::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--accent-blue);
  z-index: 2;
  pointer-events: none;
}

.contact-info-col > .contact-card:nth-child(2)::after { background: var(--accent-green); }
.contact-info-col > .contact-card:nth-child(3)::after { background: var(--accent-blue); }

/* Urgence — red flashing top bar, the only alert signal on the
   card's perimeter. Higher height (4px) than the other cards' 3px
   so it reads as the priority signal in the stack. */
.contact-info-col > .contact-card--urgence::after {
  height: 4px;
  background: var(--accent-red);
  box-shadow: 0 0 14px rgba(255, 59, 48, 0.8);
  animation: urgenceTopBarFlash 1.4s cubic-bezier(.4, 0, .6, 1) infinite;
}

@keyframes urgenceTopBarFlash {
  0%, 100% { opacity: 1;    box-shadow: 0 0 14px rgba(255, 59, 48, 0.85); }
  50%      { opacity: 0.35; box-shadow: 0 0 4px  rgba(255, 59, 48, 0.25); }
}

/* Icon — sits in the lower-left next to the info list. Title spans
   the full width above; icon + list form the second row. */
.contact-card-icon {
  grid-column: 1;
  grid-row: 2;
  align-self: center;
  width: clamp(64px, 5vw, 88px);
  height: clamp(64px, 5vw, 88px);
  object-fit: contain;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.12));
  transition: transform .35s var(--ease);
  pointer-events: none;
}

.contact-card:hover .contact-card-icon {
  transform: scale(1.06) translateY(-2px);
}

.contact-eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
}

/* Layout per row:
     Row 1 — title spans both columns, centered
     Row 2 — icon (col 1) + info list (col 2)
   Flex-based cards (urgence) ignore grid-column / grid-row entirely. */
.contact-card > .contact-eyebrow {
  grid-column: 1 / -1;
  grid-row: 1;
  text-align: center;
}

.contact-card > .contact-card-list {
  grid-column: 2;
  grid-row: 2;
}

/* On the standard info cards (Admin / Infos générales), the label is
   promoted from a small tracking-wide eyebrow to a full display title
   — matches the section-04 advantage card visual hierarchy. Urgence
   keeps the small eyebrow because its title comes from a separate
   .contact-card-title element below. */
.contact-card:not(.contact-card--urgence) > .contact-eyebrow {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.2vw, 34px);
  line-height: 1.05;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0 0 8px;
}

/* Urgence card — priority, dark */

.contact-card--urgence {
  background: #fff;
  color: var(--ink);
  /* Standard 1px border (matches the other contact cards) — alert
     is now expressed solely by the flashing red top bar. */
  border: 1px solid var(--line);
  position: relative;
  overflow: hidden;
  padding: clamp(24px, 2.8vw, 40px);
  /* Same grid layout as the standard cards:
       Row 1 — header (URGENCE + 24/7 pill), spans both columns
       Row 2 — icon (col 1, centered) + body (col 2, title + phone CTA) */
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: clamp(20px, 2.4vw, 36px);
  row-gap: 12px;
  align-items: center;
}

.contact-card--urgence:hover {
  transform: none;
}

/* Header (URGENCE eyebrow + 24/7 pill) — full width row 1. */
.contact-card--urgence > .contact-card-head {
  grid-column: 1 / -1;
  grid-row: 1;
}

/* Icon — col 1, row 2, vertically centered against the body. */
.contact-card--urgence > .contact-card-icon {
  grid-column: 1;
  grid-row: 2;
}

/* Body wrapper — col 2, row 2, stacks the title above the phone CTA. */
.contact-card-body {
  grid-column: 2;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  min-width: 0;
}

.contact-card--urgence::before {
  /* Radial glow removed — the flashing red top bar + URGENCE text
     already carry the alert signal; the gradient was washing out
     the otherwise clean white card. */
  content: none;
}

.contact-card--urgence > * { position: relative; }

.contact-card--urgence .contact-eyebrow {
  color: var(--accent-red);
  animation: urgenceFlashText 1.4s cubic-bezier(.4, 0, .6, 1) infinite;
}

@keyframes urgenceFlashText {
  0%, 100% { opacity: 1; text-shadow: 0 0 12px rgba(255, 59, 48, 0.6); }
  50%      { opacity: 0.5; text-shadow: 0 0 2px rgba(255, 59, 48, 0); }
}

.contact-card-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}

.contact-card-pill {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(255, 59, 48, 0.16);
  color: var(--accent-red);
  border: 1px solid rgba(255, 59, 48, 0.4);
  text-transform: uppercase;
}

.contact-card-title {
  font-family: var(--font-display);
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: 1.08;
  text-transform: uppercase;
  margin: 0;
  color: var(--ink);
  max-width: 22ch;
  text-align: left;
}

.contact-card-phone {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin: 0;
  padding: 14px 22px;
  background: var(--ink);
  color: #fff;
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: clamp(20px, 1.9vw, 26px);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  align-self: flex-start;
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}

.contact-card-phone:hover {
  background: var(--ink);
  color: #fff;
  transform: translateY(-1px);
}

/* Red ring around the urgence phone button — pulses in phase with the
   top bar ([urgenceTopBarFlash] above) so the alert reads as a single
   coordinated flash across the whole card. Same 1.4s duration + easing
   as the top bar, no animation-delay so the peaks line up exactly. */
.contact-card--urgence .contact-card-phone {
  animation: urgenceButtonFlash 1.4s cubic-bezier(.4, 0, .6, 1) infinite;
}

@keyframes urgenceButtonFlash {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255, 59, 48, 0.55), 0 0 28px rgba(255, 59, 48, 0.55); }
  50%      { box-shadow: 0 0 0 1px rgba(255, 59, 48, 0.15), 0 0 8px  rgba(255, 59, 48, 0.15); }
}

@media (prefers-reduced-motion: reduce) {
  .contact-card--urgence .contact-card-phone { animation: none; }
}

.contact-card-phone .phone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: rgba(255, 59, 48, 0.18);
  color: var(--accent-red);
  flex-shrink: 0;
}

.contact-card-phone:hover .phone-icon {
  background: rgba(255, 59, 48, 0.28);
}

.contact-card-phone .phone-arrow {
  font-size: 18px;
  margin-left: 4px;
  color: #fff;
  transition: transform 0.2s var(--ease);
}

.contact-card-phone:hover .phone-arrow {
  transform: translateX(3px);
}

/* Admin / Infos générales cards */

.contact-card-list {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.contact-card-list li {
  display: grid;
  grid-template-columns: 90px 1fr;
  align-items: baseline;
  gap: 12px;
}

.contact-card-list .list-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute, #9aa0a8);
}

.contact-card-list a {
  font-family: var(--font-display);
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.15;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--ink);
  border-bottom: 1px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
  word-break: break-word;
}

.contact-card-list a:hover {
  color: var(--accent-blue);
  border-bottom-color: var(--accent-blue);
}

/* Responsive */

@media (max-width: 960px) {
  .contact-layout {
    grid-template-columns: 1fr;
  }
  .contact-map { min-height: 320px; }
  .contact-info-col {
    grid-template-rows: none;
    grid-auto-rows: auto;
  }
}

@media (max-width: 560px) {
  .contact-address {
    flex-direction: column;
    align-items: flex-start;
  }
  .contact-card-list li {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .contact-card-phone {
    width: 100%;
    justify-content: center;
  }
}

/* ---------- Footer ---------- */

.site-footer {
  /* Same --ink as the marquee, services, and advantage so the dark
     surfaces all match exactly. */
  background: var(--ink);
  color: #fff;
  padding: clamp(40px, 5vw, 70px) 0 28px;
  /* Thin separator line between the contact section and the footer,
     so the two dark blocks remain visually distinct even with the
     reduced padding. */
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.footer-inner {
  display: grid;
  grid-template-columns: 1.2fr 2fr;
  gap: 64px;
  padding-bottom: 56px;
  border-bottom: 1px solid var(--line-dark);
}

.footer-tag {
  margin-top: 20px;
  color: rgba(255,255,255,.55);
  font-size: 14px;
  max-width: 320px;
}

.footer-cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
}

.footer-cols > div { display: flex; flex-direction: column; gap: 8px; }

.footer-cols a, .footer-cols span {
  font-size: 14px;
  color: rgba(255,255,255,.7);
  transition: color .2s var(--ease);
}

.footer-license-cmeq {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  line-height: 1.35;
}

.footer-license-cmeq img {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: .95;
}

.footer-cols a:hover { color: #fff; }

.footer-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255,255,255,.4);
  margin: 0 0 8px;
}

.footer-bottom {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 28px;
  font-size: 12.5px;
  color: rgba(255,255,255,.5);
}

.footer-credit {
  color: inherit;
  text-decoration: none;
  transition: color .2s var(--ease);
}

.footer-credit strong {
  color: rgba(255,255,255,.72);
  font-weight: 600;
}

.footer-credit:hover,
.footer-credit:focus-visible,
.footer-credit:hover strong,
.footer-credit:focus-visible strong {
  color: #fff;
}

/* ---------- Responsive ---------- */

@media (max-width: 960px) {
  .two-col { grid-template-columns: 1fr; }
  .col-left { position: static; }
  .services-grid { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; gap: 40px; }
}

@media (max-width: 720px) {
  .header-inner { height: 64px; }
  .brand-logo { height: 36px; }
  .footer-logo { height: 56px; }
  .hero-meta  { grid-template-columns: repeat(2, 1fr); }
  .footer-cols { grid-template-columns: 1fr 1fr; }
  .marquee-track {
    font-size: 22px;
    animation: marqueeMobileRight 10s linear infinite;
  }
  .marquee-track > span { margin-right: 24px; }
  .section-head.row { align-items: flex-start; }
}

@media (max-width: 480px) {
  .footer-cols { grid-template-columns: 1fr; }
}

/* ---------- Reveal-on-scroll ---------- */

.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .9s var(--ease), transform .9s var(--ease);
}

.reveal.in {
  opacity: 1;
  transform: none;
}

/* The old reduced-motion nuclear rules (animation: none !important,
   .reveal instant-show) were removed — the site's animations are all
   ambient/cinematic and the client wants them on every display. */

/* ---------- Services page : Urgence 24/7, Pourquoi G.E.T., Secteurs, CTA finale ---------- */

/* Urgent-call band sits at the top of the dark services section. Reuses the
   .service-card visual (transparent white-on-dark) so it sits naturally in
   the existing dark grid; an accent-green left bar marks it as the action
   block without introducing new colours. */
.services-urgent {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(20px, 3vw, 40px);
  padding: clamp(24px, 3vw, 36px) clamp(24px, 3vw, 44px);
  margin-bottom: clamp(32px, 4vw, 56px);
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-left: 3px solid var(--accent-green);
  border-radius: var(--radius);
}

.services-urgent-mark {
  width: 52px;
  height: 52px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.18);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent-green);
  flex-shrink: 0;
}

.services-urgent-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-green);
  margin: 0 0 8px;
}

.services-urgent-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.05;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  color: #fff;
  margin: 0 0 6px;
}

.services-urgent-lead {
  font-size: 14.5px;
  line-height: 1.5;
  color: rgba(255,255,255,.7);
  margin: 0;
}

.services-urgent-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}

.services-urgent-phone {
  font-family: var(--font-display);
  font-size: clamp(22px, 2.2vw, 28px);
  letter-spacing: 0.02em;
  color: #fff;
  white-space: nowrap;
  transition: color .2s var(--ease);
}

.services-urgent-phone:hover { color: var(--accent-green); }

@media (max-width: 860px) {
  .services-urgent {
    grid-template-columns: 1fr;
    text-align: left;
    gap: 18px;
  }
  .services-urgent-actions {
    align-items: flex-start;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 16px;
  }
}

/* Trust block — four key figures presented in the same dark grid language as
   the existing services. Uses display font for figures so they read like the
   service titles already on the page. */
.services-trust {
  margin-top: clamp(48px, 6vw, 88px);
  padding-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid rgba(255,255,255,.08);
}

.services-trust-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-blue);
  margin: 0 0 16px;
}

.services-trust-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(28px, 3.4vw, 48px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  color: #fff;
  margin: 0 0 clamp(28px, 3.5vw, 44px);
  max-width: 22ch;
}

.services-trust-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
}

.services-trust-grid li {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: clamp(20px, 2.4vw, 28px);
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius);
}

.services-trust-grid strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(28px, 3vw, 40px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  color: #fff;
}

.services-trust-grid span {
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(255,255,255,.65);
}

.services-trust-credentials {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
  margin-top: clamp(16px, 2vw, 24px);
}

.services-trust-credential {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 88px;
  padding: clamp(18px, 2.2vw, 24px);
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius);
  color: rgba(255,255,255,.68);
}

.services-trust-credential strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(24px, 2.5vw, 34px);
  line-height: 1;
  color: #fff;
  letter-spacing: 0.01em;
}

.services-trust-credential span {
  font-size: 13.5px;
  line-height: 1.45;
}

.services-trust-credential-label {
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 44px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--accent-blue);
}

.services-trust-credential--cmeq img {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: .95;
}

@media (max-width: 980px) {
  .services-trust-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
  .services-trust-grid { grid-template-columns: 1fr; }
  .services-trust-credentials { grid-template-columns: 1fr; }
}

/* Sectors served — sober label list, same eyebrow style as the trust block. */
.services-sectors {
  margin-top: clamp(48px, 6vw, 88px);
  padding-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid rgba(255,255,255,.08);
}

.services-sectors-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-green);
  margin: 0 0 16px;
}

.services-sectors-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.4vw, 30px);
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  color: #fff;
  margin: 0 0 clamp(20px, 2.5vw, 32px);
  max-width: 36ch;
}

.services-sectors-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
}

.services-sectors-list li {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 16px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255,255,255,.85);
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 999px;
}

/* Final CTA — closing block that mirrors the trust block's spacing and uses
   the existing CTA buttons from the header so we don't introduce new
   button styles. */
.services-cta-final {
  margin-top: clamp(56px, 7vw, 96px);
  padding-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid rgba(255,255,255,.08);
  text-align: center;
}

.services-cta-final-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-blue);
  margin: 0 0 16px;
}

.services-cta-final-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(36px, 4.5vw, 64px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  color: #fff;
  margin: 0 0 14px;
}

.services-cta-final-lead {
  font-size: clamp(15px, 1.3vw, 17px);
  line-height: 1.55;
  color: rgba(255,255,255,.7);
  margin: 0 auto clamp(24px, 3vw, 36px);
  max-width: 48ch;
}

.services-cta-final-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
}

/* ---------- À propos page : Terminal-style image rows, stat band, team, clients ---------- */

/* Background system — a subtle dotted grid plus animated glow blobs that
   live BEHIND each section's content. Sections opt in by including
   <div class="about-bg" aria-hidden="true"> with .about-bg-grid and one or
   more .about-bg-glow children. Position absolute / inset 0 keeps it
   covering the full section without affecting layout. */
.about-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/* Sections that host an .about-bg need to establish a positioning context
   and lift their inner container above the background layer. */
.about-hero,
.about-row,
.about-team,
.about-clients,
.about-stat-band {
  position: relative;
}

/* Bottom curve divider for the team section — same inverted entry-divider
   shape used on the mission section, so the two sections share the same
   transition language. The fill matches the next section so the curve
   reads as the team-section's bottom edge dipping up at center. */
.team-divider {
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  height: clamp(48px, 5.5vw, 88px);
  z-index: 2;
  pointer-events: none;
  line-height: 0;
}

.team-divider-bottom { bottom: 0; }

.team-divider svg {
  display: block;
  width: 100%;
  height: 100%;
}

.team-divider svg path {
  fill: var(--bg);
}

.about-hero > .container,
.about-row > .container,
.about-team > .container,
.about-clients > .container,
.about-stat-band > .container {
  position: relative;
  z-index: 1;
}

/* Dashed thin-line grid: 1px segments that form open square cells with a
   small gap at every intersection, so the corners read as discontinued
   ticks rather than full crossings. Drawn as an inline-SVG tile so the
   gaps are pixel-perfect at every viewport size. The grid is stationary
   so the travelling sparks stay aligned with the lines exactly. */
/* @property declarations make the mask centre coordinates animatable
   as smooth percentage transitions (rather than discrete string
   swaps). Moving the centre of the radial mask across the screen
   causes different corners to fall further from / closer to the
   mask centre, fading them in and out. */
@property --mask-cx { syntax: '<percentage>'; initial-value: 50%; inherits: false; }
@property --mask-cy { syntax: '<percentage>'; initial-value: 50%; inherits: false; }
@property --void-r { syntax: '<length>'; initial-value: 9px; inherits: false; }

.about-bg-grid {
  position: absolute;
  inset: 0;
  /* White-on-dark grid — same line geometry as before, but the stroke
     is now near-white at low opacity so it reads on the dark hero. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><g stroke='%23ffffff' stroke-opacity='0.10' stroke-width='1' fill='none' stroke-linecap='butt'><line x1='8' y1='0.5' x2='72' y2='0.5'/><line x1='0.5' y1='8' x2='0.5' y2='72'/></g></svg>");
  background-size: 80px 80px;
  background-position: 0 0;
  /* Single radial mask whose CENTRE travels around the hero over the
     animation cycle. The grid is most visible near the centre of the
     gradient and fades toward its edges, so when the centre moves
     away from corner C, corner C falls deeper into the transparent
     zone and its grid lines fade away. By scripting the centre to
     dwell at points DIAGONALLY OPPOSITE to a target corner (single
     fade) or on the OPPOSITE HORIZONTAL EDGE (pair fade), the
     desired 8-phase sequence emerges from a single mask layer — no
     mask-composite, no overlays, no risk of visible "ray" contours
     from layered gradients. */
  mask-image: radial-gradient(ellipse 70% 70% at var(--mask-cx) var(--mask-cy), #000 35%, transparent 85%);
  -webkit-mask-image: radial-gradient(ellipse 70% 70% at var(--mask-cx) var(--mask-cy), #000 35%, transparent 85%);
  animation: aboutGridMaskTravel 40s ease-in-out infinite;
}

.about-hero .about-bg-grid {
  --void-r: 9px;
}

.about-hero .about-bg-grid::before {
  content: '';
  position: absolute;
  inset: -80px;
  pointer-events: none;
  background-size: 80px 80px;
  background-position: 0 0;
}

/* Animated voids at each grid crossing. This dark layer sits over the
   line ends, so the empty intersection grows and shrinks without moving
   the grid or desynchronizing the travelling sparks. */
.about-hero .about-bg-grid::before {
  background-image: radial-gradient(
    circle at 0.5px 0.5px,
    var(--bg-dark) 0 var(--void-r),
    rgba(8, 11, 14, 0.86) calc(var(--void-r) + 1px),
    transparent calc(var(--void-r) + 3px)
  );
  animation: aboutGridVoidPulse 7s ease-in-out infinite;
}

/* 8-phase mask-centre travel (60s cycle, 7.5s per phase). The centre
   is pinned at the middle of each phase (6.25%, 18.75%, ... at 7.5s
   intervals). Between keyframes the centre smoothly drifts to the
   next anchor, so the corner "fades" appear and dissolve naturally.

     phase 1 (c1 single)       — centre at (65%, 65%) → top-left fades
     phase 2 (top pair)        — centre at (50%, 65%) → both top fade
     phase 3 (c3 single)       — centre at (35%, 35%) → bottom-right fades
     phase 4 (bottom pair)     — centre at (50%, 35%) → both bottom fade
     phase 5 (c2 single)       — centre at (35%, 65%) → top-right fades
     phase 6 (top pair)        — centre at (50%, 65%) → both top fade
     phase 7 (c4 single)       — centre at (65%, 35%) → bottom-left fades
     phase 8 (bottom pair)     — centre at (50%, 35%) → both bottom fade
   The centre stays within ±15% of the geometric centre (vs the prior
   ±30%) — the fade effect is gentler and the moving spotlight reads
   as a subtle drift rather than a swooping arc. */
@keyframes aboutGridMaskTravel {
  6.25%  { --mask-cx: 65%; --mask-cy: 65%; }
  18.75% { --mask-cx: 50%; --mask-cy: 65%; }
  31.25% { --mask-cx: 35%; --mask-cy: 35%; }
  43.75% { --mask-cx: 50%; --mask-cy: 35%; }
  56.25% { --mask-cx: 35%; --mask-cy: 65%; }
  68.75% { --mask-cx: 50%; --mask-cy: 65%; }
  81.25% { --mask-cx: 65%; --mask-cy: 35%; }
  93.75% { --mask-cx: 50%; --mask-cy: 35%; }
}

@keyframes aboutGridVoidPulse {
  0%, 100% { --void-r: 7px; opacity: 0.92; }
  28%      { --void-r: 13px; opacity: 1; }
  55%      { --void-r: 9px; opacity: 0.95; }
  78%      { --void-r: 15px; opacity: 1; }
}

/* Travelling spark — a short luminous segment that races along a
   single grid line, then disappears and restarts. Each spark goes in
   ONE direction (no turning at corners) — half travel horizontally
   left→right across the full width of the grid, half travel
   vertically top→bottom across the full height. Staggered delays
   give the cluster a random "rain of sparks" feel. */
/* Base spark — geometry + leading-dot only. Colour (gradient + glow)
   and direction (orientation + traversal) are added by modifier
   classes below so any colour can run on any direction. */
.about-bg-spark {
  position: absolute;
  width: 28px;
  height: 2px;
  border-radius: 1px;
  transform-origin: center center;
  pointer-events: none;
  /* Hidden by default — the JS scheduler (initAboutSparkScheduler) drives
     each spark's visibility via Web Animations API. Without JS the sparks
     stay invisible, which is the desired fallback. */
  opacity: 0;
}

/* Bright leading dot — incandescent core glued to the front of each
   streak so the spark reads as "a point of current with a tail" rather
   than a stretched gradient bar. After the 90° rotation on vertical
   sparks, this "right" edge becomes the bottom, which is still the
   leading edge — so the dot stays at the front in both directions. */
.about-bg-spark::after {
  content: '';
  position: absolute;
  right: -2px;
  top: 50%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffffff;
  transform: translateY(-50%);
}

/* ---- Colour modifiers (gradient body + halo + leading-dot glow) ---- */
.about-bg-spark.is-green {
  background: linear-gradient(to right,
    transparent 0%,
    transparent 50%,
    rgba(126, 217, 87, 0.45) 78%,
    rgba(180, 255, 150, 0.95) 92%,
    #ffffff 100%);
  filter:
    drop-shadow(0 0 1px #ffffff)
    drop-shadow(0 0 6px rgba(180, 255, 150, 1))
    drop-shadow(0 0 14px var(--accent-green))
    drop-shadow(0 0 28px rgba(126, 217, 87, 0.7));
}
.about-bg-spark.is-green::after {
  box-shadow:
    0 0 3px #ffffff,
    0 0 10px rgba(180, 255, 150, 1),
    0 0 20px var(--accent-green);
}

.about-bg-spark.is-blue {
  background: linear-gradient(to right,
    transparent 0%,
    transparent 50%,
    rgba(33, 171, 232, 0.45) 78%,
    rgba(170, 230, 255, 0.95) 92%,
    #ffffff 100%);
  filter:
    drop-shadow(0 0 1px #ffffff)
    drop-shadow(0 0 6px rgba(170, 230, 255, 1))
    drop-shadow(0 0 14px var(--accent-blue))
    drop-shadow(0 0 28px rgba(33, 171, 232, 0.7));
}
.about-bg-spark.is-blue::after {
  box-shadow:
    0 0 3px #ffffff,
    0 0 10px rgba(170, 230, 255, 1),
    0 0 20px var(--accent-blue);
}

/* ---- Direction modifiers (positioning only) -----------------------
   The actual animation (transform + opacity + flicker) is driven from
   JS via the Web Animations API by initAboutSparkScheduler. These
   classes still set the spark's STATIC starting position so the JS
   doesn't need to recompute it — but the JS overrides everything with
   inline styles anyway, so these mostly serve as a readable fallback. */
.about-bg-spark.is-h {
  left: -120px;
}

.about-bg-spark.is-v {
  top: -120px;
  /* Vertical sparks are rotated 90° around the box centre, so the
     visual centreline sits 14px (half the 28px body) to the right of
     the box's `left` value. Shift the layout box back by 14px so
     `left` directly addresses the grid column the spark should ride. */
  margin-left: -14px;
}

.about-bg-spark.is-h.is-rev {
  left: auto;
  right: -120px;
}

.about-bg-spark.is-v.is-rev {
  top: auto;
  bottom: -120px;
  /* margin-left from .is-v still applies — the 270° rotation keeps the
     centreline at x = 18px of the box, same as 90°. */
}

/* Floating glow blobs — large blurred radial gradients that drift slowly.
   Three colour variants (green / blue / mixed) so each section feels
   distinct without breaking the palette. */
.about-bg-glow {
  position: absolute;
  width: clamp(320px, 38vw, 560px);
  height: clamp(320px, 38vw, 560px);
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
  will-change: transform;
}

.about-bg-glow-1 {
  background: radial-gradient(circle, rgba(126,217,87,.55), transparent 65%);
  top: -8%;
  right: -6%;
  animation: aboutGlowFloat1 24s ease-in-out infinite;
}

.about-bg-glow-2 {
  background: radial-gradient(circle, rgba(33,171,232,.45), transparent 65%);
  bottom: -10%;
  left: -8%;
  animation: aboutGlowFloat2 32s ease-in-out infinite;
}

.about-bg-glow-3 {
  background: radial-gradient(circle, rgba(126,217,87,.4), transparent 65%);
  top: 30%;
  left: 35%;
  width: clamp(220px, 26vw, 380px);
  height: clamp(220px, 26vw, 380px);
  opacity: 0.4;
  animation: aboutGlowFloat3 28s ease-in-out infinite;
}

@keyframes aboutGlowFloat1 {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(-60px, 80px); }
}
@keyframes aboutGlowFloat2 {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(80px, -50px); }
}
@keyframes aboutGlowFloat3 {
  0%, 100% { transform: translate(0, 0); }
  33%      { transform: translate(-40px, -60px); }
  66%      { transform: translate(50px, 40px); }
}

/* Centered hero, Terminal-style: small monospace eyebrow → very large
   centered headline → tightly-set lead paragraph below. Uses the existing
   League Gothic display font, just bigger and centered. */
.about-hero {
  padding: clamp(160px, 18vw, 220px) 0 clamp(48px, 6vw, 72px);
  text-align: center;
  overflow: hidden;
  /* Dark-mode banner — matches the .section-dark surface and ensures
     the title-overlay reads as white text on a near-black backdrop. */
  background: var(--bg-dark);
  color: #fff;
}

/* White text for everything inside the dark hero (eyebrow muted, lead
   slightly dimmed, title pure white). */
.about-hero .about-hero-eyebrow { color: rgba(255, 255, 255, 0.55); }
.about-hero .about-hero-title   { color: #fff; }
.about-hero .about-hero-lead    { color: rgba(255, 255, 255, 0.8); }

.about-hero-container {
  max-width: 1100px;
}

.about-hero-eyebrow {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--ink-mute);
  margin: 0 0 clamp(24px, 3vw, 40px);
}

.about-hero-title {
  font-size: clamp(40px, 6.6vw, 110px);
  margin: 0 auto clamp(24px, 3vw, 36px);
  max-width: none;
  text-align: center;
}

/* Each manual line stays on one line — the <br/> in the heading creates
   exactly two lines, no organic wrap. */
.about-hero-title .line {
  display: block;
  white-space: nowrap;
}

.about-hero-lead {
  margin: 0 auto;
  max-width: 52ch;
  font-size: clamp(17px, 1.4vw, 21px);
  text-align: center;
  color: var(--ink-soft);
}

/* Monospace numbered marker, "01 Notre histoire" — replaces the old
   .section-eyebrow on About page rows so each numbered section reads
   like a chapter heading. */
.about-row-num {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.16em;
  color: var(--ink-mute);
  margin: 0 0 18px;
  display: flex;
  align-items: center;
  gap: 14px;
}

.about-row-num .num-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  padding: 4px 8px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--ink);
  font-weight: 600;
  letter-spacing: 0.05em;
}

.about-row-num.light { color: rgba(255,255,255,.55); }
.about-row-num.light .num-mark {
  background: rgba(255,255,255,.04);
  border-color: var(--line-dark);
  color: #fff;
}

/* Universal image placeholder. Any <figure> that uses [data-placeholder]
   draws a subtle bg + label until the inner <img> finishes loading; once
   the photo paints, it sits on top with z-index 1 and hides the label.
   This lets us ship the page with empty asset slots without flashing
   broken-image icons. */
[data-placeholder] {
  position: relative;
  background:
    linear-gradient(135deg, rgba(33,171,232,.08), rgba(126,217,87,.06)),
    var(--bg-alt);
  overflow: hidden;
  border-radius: var(--radius);
}

[data-placeholder]::before {
  content: attr(data-placeholder);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 16px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mute);
  z-index: 0;
}

[data-placeholder] img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Parallax stack — wraps the hero and the feature image so the hero
   can be sticky-pinned while the image scrolls up and climbs over it.
   Both elements are sticky at top:0 within the stack: the hero pins
   immediately, then the image catches up, lands on top (higher z-index)
   and also pins. When the stack ends, both release together and the
   following sections scroll up over them in normal flow. */
.about-hero-stack {
  position: relative;
  /* Higher than the following .about-row sections so the figure's
     bottom-right trapezoid (which now extends 32px below the standard
     21/9 area as part of the figure's clip-path) stays visible even
     when the next section scrolls up over the stack's footprint. */
  z-index: 2;
  /* Plain page background — the area below the figure's bottom edge
     (in the section's margin) reads white instead of the dark stack
     fill that was visible before. */
  background: var(--bg);
}

/* CRT "turn-off" animation on the hero title.
   JS toggles `.is-tv-off` on .about-hero-title when the feature image
   gets close to the title's vertical position. The two-stage keyframe
   mimics an old cathode-ray-tube shutdown:
     1) brief brightness flash
     2) vertical collapse to a thin horizontal line
     3) horizontal collapse to a tiny bright dot at centre
     4) dot fades to nothing
   When the class is removed (e.g. scrolling back up), the title pops
   back to its initial state — no reverse animation, the open hero
   should feel instant rather than reluctantly powering on. */
.about-hero-title {
  transform-origin: center center;
  will-change: transform, opacity, filter;
}

.about-hero.is-tv-off .about-hero-title {
  animation: tvOff 0.45s cubic-bezier(.6, 0, .35, 1) forwards;
}

/* Sibling text fades out alongside the CRT shutdown — eyebrow + lead
   share a gentle opacity fade so the whole hero "powers down" rather
   than just losing its title. */
.about-hero .about-hero-eyebrow,
.about-hero .about-hero-lead {
  transition: opacity 0.45s ease;
}

.about-hero.is-tv-off .about-hero-eyebrow,
.about-hero.is-tv-off .about-hero-lead {
  opacity: 0;
}

@keyframes tvOff {
  /* No initial expansion punch — it was reading as a glitch. Curve goes
     straight from natural size into the vertical collapse, with a soft
     brightness ramp instead of a sharp flash. */
  0% {
    transform: scale(1, 1);
    opacity: 1;
    filter: brightness(1);
  }
  40% {
    transform: scale(1, 0.01);
    filter: brightness(1.8);
    opacity: 1;
  }
  78% {
    transform: scale(0.03, 0.01);
    filter: brightness(1.4);
    opacity: 1;
  }
  100% {
    transform: scale(0, 0);
    opacity: 0;
    filter: brightness(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .about-hero.is-tv-off .about-hero-title { animation: none; opacity: 0; }
  .about-hero .about-hero-eyebrow,
  .about-hero .about-hero-lead { transition: none; }
}

.about-hero-stack .about-hero {
  position: sticky;
  top: 0;
  z-index: 0;
}

/* Full-width feature image directly under the page hero. Sticky at top
   with a higher z-index so it climbs over the hero on scroll and stays
   pinned in front of it until the stack ends. */
.about-feature-image {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0;
  margin-bottom: clamp(24px, 4vw, 48px);
  /* White element background — covers the bottom 32px strip OUTSIDE the
     trapezoid (where the figure is clipped out) so the dark hero
     sitting underneath at z:0 doesn't bleed through. The ::before
     below paints DARK over only the 21/9 main area so notches in that
     area still read dark; everything else (the strip + the margin)
     keeps this white. */
  background: var(--bg);
}

/* Dark fill ONLY behind the figure's main 21/9 area — so the top-left
   and left-side notches still expose the dark background, but anything
   below this strip (the bottom-right trapezoid surroundings + the
   margin below the figure) shows the white stack background. */
.about-feature-image::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Cover only the original 21/9 portion of the figure, NOT the extra
     32px trapezoid strip beneath it. */
  height: calc(var(--vw100) * 9 / 21);
  background: var(--bg-dark);
  z-index: 0;
  pointer-events: none;
}

.about-feature-figure {
  /* Use --vw100 so the full-bleed feature image still spans
     exactly one viewport edge-to-edge under page zoom. */
  width: var(--vw100);
  max-width: none;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  /* Custom height = 21/9 main area + a 28px strip at the bottom where
     the centred trapezoid protrudes. The image (object-fit:cover)
     naturally fills the whole box, so the trapezoid frames REAL photo
     content from the same image — no second image, no background-image
     overlay. The clip-path keeps only the trapezoid visible in that
     bottom 28px strip. */
  aspect-ratio: auto;
  height: calc(var(--vw100) * 9 / 21 + 28px);
  border-radius: 0;
  clip-path: polygon(
    0% 0%,
    /* Top-left notch. 56×28 chamfer (was 80×28) — chamfer width reduced
       30% so the diagonal is steeper. Cosine S-curve
       y = 14·(1 − cos(πx/56)). Outer corners unchanged (15% and
       15%+568px); inner flat top grows from 408px → 456px. */
    15% 0%,
    calc(15% + 4px) 0.351px, calc(15% + 8px) 1.386px, calc(15% + 12px) 3.054px, calc(15% + 16px) 5.271px, calc(15% + 20px) 7.926px, calc(15% + 24px) 10.885px, calc(15% + 28px) 14px, calc(15% + 32px) 17.115px, calc(15% + 36px) 20.074px, calc(15% + 40px) 22.729px, calc(15% + 44px) 24.946px, calc(15% + 48px) 26.614px, calc(15% + 52px) 27.649px,
    calc(15% + 56px) 28px,
    calc(15% + 512px) 28px,
    calc(15% + 516px) 27.649px, calc(15% + 520px) 26.614px, calc(15% + 524px) 24.946px, calc(15% + 528px) 22.729px, calc(15% + 532px) 20.074px, calc(15% + 536px) 17.115px, calc(15% + 540px) 14px, calc(15% + 544px) 10.885px, calc(15% + 548px) 7.926px, calc(15% + 552px) 5.271px, calc(15% + 556px) 3.054px, calc(15% + 560px) 1.386px, calc(15% + 564px) 0.351px,
    calc(15% + 568px) 0%,
    100% 0%,
    /* Right edge ends at the ORIGINAL bottom (28px above the new bottom) */
    100% calc(100% - 28px),
    /* Bottom-centre trapezoid: 56 × 28 chamfer. Outer extent unchanged
       (±568); flat bottom grows from 976px → 1024px. */
    calc(50% + 568px) calc(100% - 28px),
    calc(50% + 564px) calc(100% - 27.649px), calc(50% + 560px) calc(100% - 26.614px), calc(50% + 556px) calc(100% - 24.946px), calc(50% + 552px) calc(100% - 22.729px), calc(50% + 548px) calc(100% - 20.074px), calc(50% + 544px) calc(100% - 17.115px), calc(50% + 540px) calc(100% - 14px), calc(50% + 536px) calc(100% - 10.885px), calc(50% + 532px) calc(100% - 7.926px), calc(50% + 528px) calc(100% - 5.271px), calc(50% + 524px) calc(100% - 3.054px), calc(50% + 520px) calc(100% - 1.386px), calc(50% + 516px) calc(100% - 0.351px),
    calc(50% + 512px) 100%,
    calc(50% - 512px) 100%,
    calc(50% - 516px) calc(100% - 0.351px), calc(50% - 520px) calc(100% - 1.386px), calc(50% - 524px) calc(100% - 3.054px), calc(50% - 528px) calc(100% - 5.271px), calc(50% - 532px) calc(100% - 7.926px), calc(50% - 536px) calc(100% - 10.885px), calc(50% - 540px) calc(100% - 14px), calc(50% - 544px) calc(100% - 17.115px), calc(50% - 548px) calc(100% - 20.074px), calc(50% - 552px) calc(100% - 22.729px), calc(50% - 556px) calc(100% - 24.946px), calc(50% - 560px) calc(100% - 26.614px), calc(50% - 564px) calc(100% - 27.649px),
    calc(50% - 568px) calc(100% - 28px),
    /* Continue left along the original bottom edge to the left side */
    0% calc(100% - 28px),
    /* Left edge notch — 56 × 28 chamfer (transposed). Total notch
       height stays at 184px; inner flat middle grows from 24px → 72px. */
    0px calc(35% + 184px),
    0.351px calc(35% + 180px), 1.386px calc(35% + 176px), 3.054px calc(35% + 172px), 5.271px calc(35% + 168px), 7.926px calc(35% + 164px), 10.885px calc(35% + 160px), 14px calc(35% + 156px), 17.115px calc(35% + 152px), 20.074px calc(35% + 148px), 22.729px calc(35% + 144px), 24.946px calc(35% + 140px), 26.614px calc(35% + 136px), 27.649px calc(35% + 132px),
    28px calc(35% + 128px),
    28px calc(35% + 56px),
    27.649px calc(35% + 52px), 26.614px calc(35% + 48px), 24.946px calc(35% + 44px), 22.729px calc(35% + 40px), 20.074px calc(35% + 36px), 17.115px calc(35% + 32px), 14px calc(35% + 28px), 10.885px calc(35% + 24px), 7.926px calc(35% + 20px), 5.271px calc(35% + 16px), 3.054px calc(35% + 12px), 1.386px calc(35% + 8px), 0.351px calc(35% + 4px),
    0px 35%
  );
}

@media (max-width: 720px) {
  .about-feature-figure { 
    aspect-ratio: 4 / 3; 
    clip-path: polygon(
      0% 0%,
      15% 0%, calc(15% + 3px) 1px, calc(15% + 6px) 4px, calc(15% + 10px) 10px, calc(15% + 14px) 16px, calc(15% + 17px) 19px, calc(15% + 20px) 20px,
      calc(15% + 100px) 20px, calc(15% + 103px) 19px, calc(15% + 106px) 16px, calc(15% + 110px) 10px, calc(15% + 114px) 4px, calc(15% + 117px) 1px, calc(15% + 120px) 0%,
      100% 0%,
      100% 100%,
      0% 100%,
      0px calc(35% + 120px), 1px calc(35% + 117px), 4px calc(35% + 114px), 10px calc(35% + 110px), 16px calc(35% + 106px), 19px calc(35% + 103px), 20px calc(35% + 100px),
      20px calc(35% + 20px), 19px calc(35% + 17px), 16px calc(35% + 14px), 10px calc(35% + 10px), 4px calc(35% + 6px), 1px calc(35% + 3px), 0px 35%
    );
  }
}

/* Alternating two-column rows. Same .section padding rhythm as the rest of
   the site, but the inner grid switches order via two sibling layout
   classes — keeps the markup natural-reading while alternating visually. */
.about-row-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
}

.about-row-text-first .about-row-text   { order: 0; }
.about-row-text-first .about-row-figure {
  order: 1;
  /* `50vw - 50% + 100%` would bleed correctly to the screen edge
     in unzoomed CSS pixels; substitute --vw100 so the half-bleed
     still hits the viewport edge under page zoom. */
  width: calc(var(--vw100) / 2 - 50% + 100%);
  max-width: none;
  margin-right: calc(50% - var(--vw100) / 2);
}
.about-row-image-first .about-row-figure {
  order: 0;
  width: calc(var(--vw100) / 2 - 50% + 100%);
  max-width: none;
  margin-left: calc(50% - var(--vw100) / 2);
}

/* Bleed the text block toward the outside viewport edge while keeping a
   ~½″ margin (48px) from the screen edge so the text never gets cut
   off. --text-bleed is the leftover space between the container and the
   viewport edge minus that 48px margin; it's clamped to 0 on narrow
   viewports so the text simply respects the container's normal padding. */
.about-row-text-first .about-row-text,
.about-row-image-first .about-row-text {
  --text-bleed: max(
    0px,
    calc(((var(--vw100) - var(--max)) / 2 + var(--pad)) - 48px)
  );
  width: calc(100% + var(--text-bleed));
  max-width: none;
}

.about-row-text-first .about-row-text {
  margin-left: calc(-1 * var(--text-bleed));
  padding-right: clamp(24px, 3vw, 56px);
}

.about-row-image-first .about-row-text {
  margin-right: calc(-1 * var(--text-bleed));
  padding-left: clamp(24px, 3vw, 56px);
}

.about-row-text .section-eyebrow { margin-bottom: 18px; }
.about-row-text .section-title   { margin: 0 0 28px; }
.about-row-text .lead {
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.5;
  color: var(--ink);
  font-weight: 500;
  margin: 0 0 24px;
}
.about-row-text p {
  font-size: 16.5px;
  line-height: 1.65;
  color: var(--ink-soft);
  margin: 0 0 18px;
}
.about-row-text strong { color: var(--ink); font-weight: 600; }
.about-row-text .checklist { margin-top: 28px; }

.about-row-figure {
  width: 100%;
  aspect-ratio: 4 / 3;
  align-self: stretch;
  border-radius: 0;
  /* Text-first variant: image is on the right and bleeds to the screen
     edge, so the right corners stay SHARP. The top notch is positioned
     near the right (screen-edge) side of the image. Only the inner
     corners (top-left, bottom-left) get the 16px rounding.
     Chamfers reduced 30% to 56 × 28 (steeper diagonal). Outer notch
     extents unchanged — 360px horizontal, 320px vertical — the inner
     flat portions grow to absorb the shorter chamfers. */
  clip-path: polygon(
    16px 0%,
    calc(85% - 360px) 0%,
    calc(85% - 356px) 0.351px, calc(85% - 352px) 1.386px, calc(85% - 348px) 3.054px, calc(85% - 344px) 5.271px, calc(85% - 340px) 7.926px, calc(85% - 336px) 10.885px, calc(85% - 332px) 14px, calc(85% - 328px) 17.115px, calc(85% - 324px) 20.074px, calc(85% - 320px) 22.729px, calc(85% - 316px) 24.946px, calc(85% - 312px) 26.614px, calc(85% - 308px) 27.649px,
    calc(85% - 304px) 28px,
    calc(85% - 56px) 28px,
    calc(85% - 52px) 27.649px, calc(85% - 48px) 26.614px, calc(85% - 44px) 24.946px, calc(85% - 40px) 22.729px, calc(85% - 36px) 20.074px, calc(85% - 32px) 17.115px, calc(85% - 28px) 14px, calc(85% - 24px) 10.885px, calc(85% - 20px) 7.926px, calc(85% - 16px) 5.271px, calc(85% - 12px) 3.054px, calc(85% - 8px) 1.386px, calc(85% - 4px) 0.351px,
    85% 0%,
    100% 0%,
    100% 100%,
    16px 100%,
    10px calc(100% - 1px), 5px calc(100% - 5px), 1px calc(100% - 10px), 0px calc(100% - 16px),
    0px calc(35% + 320px),
    0.351px calc(35% + 316px), 1.386px calc(35% + 312px), 3.054px calc(35% + 308px), 5.271px calc(35% + 304px), 7.926px calc(35% + 300px), 10.885px calc(35% + 296px), 14px calc(35% + 292px), 17.115px calc(35% + 288px), 20.074px calc(35% + 284px), 22.729px calc(35% + 280px), 24.946px calc(35% + 276px), 26.614px calc(35% + 272px), 27.649px calc(35% + 268px),
    28px calc(35% + 264px),
    28px calc(35% + 56px),
    27.649px calc(35% + 52px), 26.614px calc(35% + 48px), 24.946px calc(35% + 44px), 22.729px calc(35% + 40px), 20.074px calc(35% + 36px), 17.115px calc(35% + 32px), 14px calc(35% + 28px), 10.885px calc(35% + 24px), 7.926px calc(35% + 20px), 5.271px calc(35% + 16px), 3.054px calc(35% + 12px), 1.386px calc(35% + 8px), 0.351px calc(35% + 4px),
    0px 35%,
    0px 16px,
    1px 10px, 5px 5px, 10px 1px
  );
}

.about-row-image-first .about-row-figure {
  /* Image-first variant: image is on the left and bleeds to the screen
     edge, so the left corners stay SHARP. The top notch is positioned
     near the left (screen-edge) side of the image. Only the inner
     corners (top-right, bottom-right) get the 16px rounding.
     56 × 28 chamfers (30% reduction from prior 80 × 28). Outer notch
     extents unchanged — 360px / 320px. Mirror of the text-first
     geometry. */
  clip-path: polygon(
    0px 0%,
    15% 0%,
    calc(15% + 4px) 0.351px, calc(15% + 8px) 1.386px, calc(15% + 12px) 3.054px, calc(15% + 16px) 5.271px, calc(15% + 20px) 7.926px, calc(15% + 24px) 10.885px, calc(15% + 28px) 14px, calc(15% + 32px) 17.115px, calc(15% + 36px) 20.074px, calc(15% + 40px) 22.729px, calc(15% + 44px) 24.946px, calc(15% + 48px) 26.614px, calc(15% + 52px) 27.649px,
    calc(15% + 56px) 28px,
    calc(15% + 304px) 28px,
    calc(15% + 308px) 27.649px, calc(15% + 312px) 26.614px, calc(15% + 316px) 24.946px, calc(15% + 320px) 22.729px, calc(15% + 324px) 20.074px, calc(15% + 328px) 17.115px, calc(15% + 332px) 14px, calc(15% + 336px) 10.885px, calc(15% + 340px) 7.926px, calc(15% + 344px) 5.271px, calc(15% + 348px) 3.054px, calc(15% + 352px) 1.386px, calc(15% + 356px) 0.351px,
    calc(15% + 360px) 0%,
    calc(100% - 16px) 0%,
    calc(100% - 10px) 1px, calc(100% - 5px) 5px, calc(100% - 1px) 10px, 100% 16px,
    100% 35%,
    calc(100% - 0.351px) calc(35% + 4px), calc(100% - 1.386px) calc(35% + 8px), calc(100% - 3.054px) calc(35% + 12px), calc(100% - 5.271px) calc(35% + 16px), calc(100% - 7.926px) calc(35% + 20px), calc(100% - 10.885px) calc(35% + 24px), calc(100% - 14px) calc(35% + 28px), calc(100% - 17.115px) calc(35% + 32px), calc(100% - 20.074px) calc(35% + 36px), calc(100% - 22.729px) calc(35% + 40px), calc(100% - 24.946px) calc(35% + 44px), calc(100% - 26.614px) calc(35% + 48px), calc(100% - 27.649px) calc(35% + 52px),
    calc(100% - 28px) calc(35% + 56px),
    calc(100% - 28px) calc(35% + 264px),
    calc(100% - 27.649px) calc(35% + 268px), calc(100% - 26.614px) calc(35% + 272px), calc(100% - 24.946px) calc(35% + 276px), calc(100% - 22.729px) calc(35% + 280px), calc(100% - 20.074px) calc(35% + 284px), calc(100% - 17.115px) calc(35% + 288px), calc(100% - 14px) calc(35% + 292px), calc(100% - 10.885px) calc(35% + 296px), calc(100% - 7.926px) calc(35% + 300px), calc(100% - 5.271px) calc(35% + 304px), calc(100% - 3.054px) calc(35% + 308px), calc(100% - 1.386px) calc(35% + 312px), calc(100% - 0.351px) calc(35% + 316px),
    100% calc(35% + 320px),
    100% calc(100% - 16px),
    calc(100% - 1px) calc(100% - 10px), calc(100% - 5px) calc(100% - 5px), calc(100% - 10px) calc(100% - 1px), calc(100% - 16px) 100%,
    0% 100%
  );
}

/* Old notch overrides removed */

/* Half-screen bleed: the figure spans exactly from screen centre (50vw)
   to the viewport edge on its outside side. The width of 50vw plus a
   negative margin that escapes the container's max-width + pad puts the
   inner edge at viewport centre and the outer edge flush with the
   viewport edge. The min() clamps the bleed to at least -var(--pad) on
   narrow viewports so the figure never pulls inward. */
.about-row-text-first .about-row-figure {
  width: calc(var(--vw100) / 2);
  justify-self: end;
  margin-right: min(
    calc(-1 * ((var(--vw100) - var(--max)) / 2 + var(--pad))),
    calc(-1 * var(--pad))
  );
}

.about-row-image-first .about-row-figure {
  width: calc(var(--vw100) / 2);
  justify-self: start;
  margin-left: min(
    calc(-1 * ((var(--vw100) - var(--max)) / 2 + var(--pad))),
    calc(-1 * var(--pad))
  );
}

@media (max-width: 860px) {
  .about-row-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  /* Mobile reading order: text first, image second — for every section
     (text → image, text → image …), both layout variants. */
  .about-row-text-first .about-row-figure,
  .about-row-image-first .about-row-figure {
    order: 1;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: 0;
  }
  .about-row-figure { 
    aspect-ratio: 4 / 3; 
    clip-path: polygon(
      0% 0%,
      15% 0%, calc(15% + 3px) 1px, calc(15% + 6px) 4px, calc(15% + 10px) 10px, calc(15% + 14px) 16px, calc(15% + 17px) 19px, calc(15% + 20px) 20px,
      calc(15% + 100px) 20px, calc(15% + 103px) 19px, calc(15% + 106px) 16px, calc(15% + 110px) 10px, calc(15% + 114px) 4px, calc(15% + 117px) 1px, calc(15% + 120px) 0%,
      100% 0%,
      100% 100%,
      0% 100%,
      0px calc(35% + 120px), 1px calc(35% + 117px), 4px calc(35% + 114px), 10px calc(35% + 110px), 16px calc(35% + 106px), 19px calc(35% + 103px), 20px calc(35% + 100px),
      20px calc(35% + 20px), 19px calc(35% + 17px), 16px calc(35% + 14px), 10px calc(35% + 10px), 4px calc(35% + 6px), 1px calc(35% + 3px), 0px 35%
    );
  }
  .about-row-image-first .about-row-figure {
    clip-path: polygon(
      0% 0%,
      calc(85% - 120px) 0%, calc(85% - 117px) 1px, calc(85% - 114px) 4px, calc(85% - 110px) 10px, calc(85% - 106px) 16px, calc(85% - 103px) 19px, calc(85% - 100px) 20px,
      calc(85% - 20px) 20px, calc(85% - 17px) 19px, calc(85% - 14px) 16px, calc(85% - 10px) 10px, calc(85% - 6px) 4px, calc(85% - 3px) 1px, 85% 0%,
      100% 0%,
      100% 35%,
      calc(100% - 1px) calc(35% + 3px), calc(100% - 4px) calc(35% + 6px), calc(100% - 10px) calc(35% + 10px), calc(100% - 16px) calc(35% + 14px), calc(100% - 19px) calc(35% + 17px), calc(100% - 20px) calc(35% + 20px),
      calc(100% - 20px) calc(35% + 100px),
      calc(100% - 19px) calc(35% + 103px), calc(100% - 16px) calc(35% + 106px), calc(100% - 10px) calc(35% + 110px), calc(100% - 4px) calc(35% + 114px), calc(100% - 1px) calc(35% + 117px), 100% calc(35% + 120px),
      100% 100%,
      0% 100%
    );
  }
}

/* Stat band — single big number, centered, on a dark surface that has
   wide trapezoidal notches cut from the TOP and BOTTOM edges (matching
   the row-figure notch language but applied to a full-bleed section).
   The section keeps its full width — only the top and bottom edges are
   notched. */
.about-stat-band {
  position: relative;
  text-align: center;
  padding-top: clamp(56px, 6vw, 96px) !important;
  padding-bottom: clamp(56px, 6vw, 96px) !important;
  overflow: hidden;
  /* Trapezoidal notch — slope 80px wide, depth reduced to 14px (40%
     shallower than before). The angle is now ≈10°, very gentle. Corners
     keep their light rounding. One notch top, one bottom. */
  clip-path: polygon(
    0% 0%,
    calc(10% - 8px) 0%,
    calc(10% + 2px) 1px,
    calc(10% + 12px) 2.5px,
    calc(10% + 70px) 12.5px,
    calc(10% + 78px) 13.5px,
    calc(10% + 88px) 14px,
    calc(90% - 88px) 14px,
    calc(90% - 78px) 13.5px,
    calc(90% - 70px) 12.5px,
    calc(90% - 12px) 2.5px,
    calc(90% - 2px) 1px,
    calc(90% + 8px) 0%,
    100% 0%,
    100% 100%,
    calc(90% + 8px) 100%,
    calc(90% - 2px) calc(100% - 1px),
    calc(90% - 12px) calc(100% - 2.5px),
    calc(90% - 70px) calc(100% - 12.5px),
    calc(90% - 78px) calc(100% - 13.5px),
    calc(90% - 88px) calc(100% - 14px),
    calc(10% + 88px) calc(100% - 14px),
    calc(10% + 78px) calc(100% - 13.5px),
    calc(10% + 70px) calc(100% - 12.5px),
    calc(10% + 12px) calc(100% - 2.5px),
    calc(10% + 2px) calc(100% - 1px),
    calc(10% - 8px) 100%,
    0% 100%
  );
}

/* Grid variant for the dark stat band — same dashed-line pattern as the
   hero, but using accent-green at low opacity so the marks read against
   the near-black background. A slow continuous pan animates the grid so
   the section never feels static. */
.about-bg-grid-dark {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><g stroke='%237ed957' stroke-opacity='0.16' stroke-width='1' fill='none' stroke-linecap='butt'><line x1='8' y1='0.5' x2='72' y2='0.5'/><line x1='0.5' y1='8' x2='0.5' y2='72'/></g></svg>") !important;
  mask-image: radial-gradient(ellipse at center, #000 55%, transparent 100%) !important;
  -webkit-mask-image: radial-gradient(ellipse at center, #000 55%, transparent 100%) !important;
  animation: aboutStatGridPan 30s linear infinite !important;
}

@keyframes aboutStatGridPan {
  to { background-position: 80px 80px; }
}

.about-stat-inner {
  max-width: 880px;
  margin: 0 auto;
}

.about-stat-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-green);
  margin: 0 0 32px;
}

.about-stat-figure {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(120px, 22vw, 280px);
  line-height: 0.9;
  letter-spacing: -0.02em;
  color: #fff;
  margin: 0 0 24px;
}

.about-stat-figure .about-stat-unit {
  color: var(--accent-blue);
  font-size: 0.55em;
  vertical-align: 0.15em;
  margin-left: 6px;
}

.about-stat-caption {
  font-size: clamp(17px, 1.5vw, 22px);
  line-height: 1.5;
  color: rgba(255,255,255,.78);
  margin: 0 auto;
  max-width: 60ch;
}

/* "Expertise ferroviaire" full-bleed banner section on the about page.
   The figure spans the full viewport width (via --vw100), with a clip-
   path that carves a centred trapezoid into BOTH the top and bottom
   edges — matching the same 56 × 28 cosine-S chamfer geometry used by
   the about-row figures, mirrored vertically. A semi-opaque black
   layer (::after) sits over the image so the white eyebrow + body
   text in the centre stay readable. */
.about-railway-feature {
  position: relative;
  margin: clamp(56px, 7vw, 110px) 0 0;
  padding: 0;
}

/* When a regular .section follows the banner, dramatically trim its
   top padding. !important + high-specificity dual selector so it
   beats both the base .section padding and any future overrides. */
.about-railway-feature + .section,
.about-railway-feature + .section.section-partners {
  padding-top: 0 !important;
  /* Drop the default .section top hairline at the banner -> collaborators
     seam — the grey line above "05 Nos collaborateurs". */
  border-top: 0;
}
/* Also flatten the section-head's bottom margin a bit so the title
   reaches the subtitle without an oversized gap, since we removed all
   the top padding from the section. */
.about-railway-feature + .section .section-head {
  margin-top: 0 !important;
  padding-top: 0;
}

.about-railway-banner {
  position: relative;
  width: var(--vw100);
  max-width: none;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  aspect-ratio: 21 / 8;
  background: #000;
  overflow: hidden;
  /* Top + bottom centred inward notches. Each notch is 1260px wide
     (-630 to +630 from horizontal centre — 3.5× longer than before)
     and 28px deep, with 56px cosine-S chamfers on each side and a
     1148px flat inner edge. Same chamfer profile as the about-row
     figures, just the flat middle is stretched. */
  clip-path: polygon(
    0% 0%,
    calc(50% - 630px) 0%,
    calc(50% - 626px) 0.351px, calc(50% - 622px) 1.386px, calc(50% - 618px) 3.054px, calc(50% - 614px) 5.271px, calc(50% - 610px) 7.926px, calc(50% - 606px) 10.885px, calc(50% - 602px) 14px, calc(50% - 598px) 17.115px, calc(50% - 594px) 20.074px, calc(50% - 590px) 22.729px, calc(50% - 586px) 24.946px, calc(50% - 582px) 26.614px, calc(50% - 578px) 27.649px,
    calc(50% - 574px) 28px,
    calc(50% + 574px) 28px,
    calc(50% + 578px) 27.649px, calc(50% + 582px) 26.614px, calc(50% + 586px) 24.946px, calc(50% + 590px) 22.729px, calc(50% + 594px) 20.074px, calc(50% + 598px) 17.115px, calc(50% + 602px) 14px, calc(50% + 606px) 10.885px, calc(50% + 610px) 7.926px, calc(50% + 614px) 5.271px, calc(50% + 618px) 3.054px, calc(50% + 622px) 1.386px, calc(50% + 626px) 0.351px,
    calc(50% + 630px) 0%,
    100% 0%,
    100% 100%,
    calc(50% + 630px) 100%,
    calc(50% + 626px) calc(100% - 0.351px), calc(50% + 622px) calc(100% - 1.386px), calc(50% + 618px) calc(100% - 3.054px), calc(50% + 614px) calc(100% - 5.271px), calc(50% + 610px) calc(100% - 7.926px), calc(50% + 606px) calc(100% - 10.885px), calc(50% + 602px) calc(100% - 14px), calc(50% + 598px) calc(100% - 17.115px), calc(50% + 594px) calc(100% - 20.074px), calc(50% + 590px) calc(100% - 22.729px), calc(50% + 586px) calc(100% - 24.946px), calc(50% + 582px) calc(100% - 26.614px), calc(50% + 578px) calc(100% - 27.649px),
    calc(50% + 574px) calc(100% - 28px),
    calc(50% - 574px) calc(100% - 28px),
    calc(50% - 578px) calc(100% - 27.649px), calc(50% - 582px) calc(100% - 26.614px), calc(50% - 586px) calc(100% - 24.946px), calc(50% - 590px) calc(100% - 22.729px), calc(50% - 594px) calc(100% - 20.074px), calc(50% - 598px) calc(100% - 17.115px), calc(50% - 602px) calc(100% - 14px), calc(50% - 606px) calc(100% - 10.885px), calc(50% - 610px) calc(100% - 7.926px), calc(50% - 614px) calc(100% - 5.271px), calc(50% - 618px) calc(100% - 3.054px), calc(50% - 622px) calc(100% - 1.386px), calc(50% - 626px) calc(100% - 0.351px),
    calc(50% - 630px) 100%,
    0% 100%
  );
}

.about-railway-banner img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  /* Slow ambient drift — the image gently floats inside the banner
     frame even without scroll. Scaled 1.06× so we always have a 3%
     safety buffer on each side; the ±1.5% pan stays well within it
     so the image edges never peek into view. 30s ease-in-out cycle
     makes the motion feel like a calm camera breath rather than a
     mechanical loop. */
  transform-origin: center;
  animation: aboutRailwayBannerDrift 30s ease-in-out infinite;
}

@keyframes aboutRailwayBannerDrift {
  0%, 100% { transform: scale(1.06) translate(0, 0); }
  25%      { transform: scale(1.06) translate(-1.5%, -0.5%); }
  50%      { transform: scale(1.06) translate(0, 1%); }
  75%      { transform: scale(1.06) translate(1.5%, -0.5%); }
}

@media (prefers-reduced-motion: reduce) {
  .about-railway-banner img { animation: none; transform: none; }
}

/* Light black filter over the image so the text reads cleanly without
   washing out the photo (the cadenas/disconnecteur details on the
   right side of the original photo stay legible). */
.about-railway-banner::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

.about-railway-banner-content {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 2.5vw, 32px);
  text-align: center;
  padding: clamp(40px, 6vw, 80px) clamp(24px, 4vw, 48px);
  color: #fff;
  z-index: 1;
}

.about-railway-banner-content .about-row-num {
  justify-content: center;
  color: rgba(255, 255, 255, 0.78);
  margin: 0;
}

.about-railway-banner-content .num-mark {
  /* Match the dark-pill badge style used on dark sections (.about-row-num.light)
     so the "04" reads as a translucent chip on top of the photo, then pulse
     border / text / glow between white and brand green. */
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.22);
  color: #ffffff;
  animation: aboutRailwayNumFlash 2.4s ease-in-out infinite;
}

.about-railway-banner-text {
  font-size: clamp(17px, 1.5vw, 22px);
  line-height: 1.55;
  max-width: 65ch;
  margin: 0;
  color: rgba(255, 255, 255, 0.94);
}

@keyframes aboutRailwayNumFlash {
  0%, 100% {
    color: #ffffff;
    border-color: rgba(255, 255, 255, 0.22);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  50% {
    color: var(--accent-green, #7ed957);
    border-color: rgba(126, 217, 87, 0.75);
    box-shadow: 0 0 16px rgba(126, 217, 87, 0.45);
  }
}

@media (max-width: 720px) {
  .about-railway-banner { aspect-ratio: 4 / 5; }
  .about-railway-banner::after { background: rgba(0, 0, 0, 0.55); }
}

@media (max-width: 1100px) {
  .about-railway-feature {
    margin-top: clamp(44px, 8vw, 78px);
  }

  .about-railway-banner {
    width: var(--vw100);
    max-width: none;
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    aspect-ratio: 16 / 9;
    border-radius: 0;
    overflow: hidden;
    -webkit-clip-path: polygon(
      0% 0%,
      calc(10% - 8px) 0%,
      calc(10% + 2px) 1px,
      calc(10% + 12px) 2.5px,
      calc(10% + 70px) 12.5px,
      calc(10% + 78px) 13.5px,
      calc(10% + 88px) 14px,
      calc(90% - 88px) 14px,
      calc(90% - 78px) 13.5px,
      calc(90% - 70px) 12.5px,
      calc(90% - 12px) 2.5px,
      calc(90% - 2px) 1px,
      calc(90% + 8px) 0%,
      100% 0%,
      100% 100%,
      calc(90% + 8px) 100%,
      calc(90% - 2px) calc(100% - 1px),
      calc(90% - 12px) calc(100% - 2.5px),
      calc(90% - 70px) calc(100% - 12.5px),
      calc(90% - 78px) calc(100% - 13.5px),
      calc(90% - 88px) calc(100% - 14px),
      calc(10% + 88px) calc(100% - 14px),
      calc(10% + 78px) calc(100% - 13.5px),
      calc(10% + 70px) calc(100% - 12.5px),
      calc(10% + 12px) calc(100% - 2.5px),
      calc(10% + 2px) calc(100% - 1px),
      calc(10% - 8px) 100%,
      0% 100%
    );
            clip-path: polygon(
      0% 0%,
      calc(10% - 8px) 0%,
      calc(10% + 2px) 1px,
      calc(10% + 12px) 2.5px,
      calc(10% + 70px) 12.5px,
      calc(10% + 78px) 13.5px,
      calc(10% + 88px) 14px,
      calc(90% - 88px) 14px,
      calc(90% - 78px) 13.5px,
      calc(90% - 70px) 12.5px,
      calc(90% - 12px) 2.5px,
      calc(90% - 2px) 1px,
      calc(90% + 8px) 0%,
      100% 0%,
      100% 100%,
      calc(90% + 8px) 100%,
      calc(90% - 2px) calc(100% - 1px),
      calc(90% - 12px) calc(100% - 2.5px),
      calc(90% - 70px) calc(100% - 12.5px),
      calc(90% - 78px) calc(100% - 13.5px),
      calc(90% - 88px) calc(100% - 14px),
      calc(10% + 88px) calc(100% - 14px),
      calc(10% + 78px) calc(100% - 13.5px),
      calc(10% + 70px) calc(100% - 12.5px),
      calc(10% + 12px) calc(100% - 2.5px),
      calc(10% + 2px) calc(100% - 1px),
      calc(10% - 8px) 100%,
      0% 100%
    );
  }

  .about-railway-banner-content {
    padding: clamp(58px, 9vw, 82px) clamp(22px, 6vw, 56px);
  }
}

@media (max-width: 720px) {
  .about-railway-banner {
    aspect-ratio: 4 / 5;
  }

  .about-railway-banner-content {
    gap: clamp(14px, 5vw, 22px);
    padding: clamp(70px, 17vw, 92px) clamp(18px, 6vw, 28px);
  }
}

/* ---- FAQ section (about page) ----------------------------------
   Accordion built on native <details> + <summary> — fully accessible
   and works without JS. Each row has a hairline divider; the toggle
   in the top-right is two thin bars that cross to form a "+" when
   collapsed and a single horizontal "−" when open (CSS-only). */
.section.about-faq {
  /* Remove the default 1px hairline that .section adds between
     successive sections — Siège social and FAQ belong together
     visually, so we want no visible seam between them. */
  border-top: 0;
  /* Pull the FAQ closer to Siège social — the long map column
     already leaves enough breathing room, no need for another
     80-160 px of section padding on top. */
  padding-top: clamp(24px, 3vw, 56px);
}
.about-faq .section-head {
  text-align: center;
  max-width: 720px;
  margin-inline: auto;
  margin-bottom: clamp(40px, 5vw, 64px);
}
.about-faq .about-row-num {
  justify-content: center;
}

.faq-list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 880px;
  border-top: 1px solid rgba(10, 10, 10, 0.10);
}
.faq-item {
  border-bottom: 1px solid rgba(10, 10, 10, 0.10);
}
.faq-item details {
  padding: 0;
}
.faq-item summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: clamp(20px, 2.5vw, 28px) 0;
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
  transition: color .2s ease;
}
/* Hide the default disclosure triangle in WebKit + Firefox */
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary { list-style: none; }

.faq-item summary:hover {
  color: var(--accent-green, #1f8a2e);
}

.faq-q {
  flex: 1;
}

.faq-toggle {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: currentColor;
}
.faq-toggle::before,
.faq-toggle::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: currentColor;
  border-radius: 1px;
  transition: transform .25s ease, opacity .25s ease;
}
.faq-toggle::before {
  width: 14px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}
.faq-toggle::after {
  width: 14px;
  height: 1.5px;
  transform: translate(-50%, -50%) rotate(90deg);
}
/* When open: the vertical bar rotates flat, leaving a single "−". */
.faq-item details[open] .faq-toggle::after {
  transform: translate(-50%, -50%) rotate(0deg);
  opacity: 0;
}

.faq-a {
  padding: 0 0 clamp(22px, 2.5vw, 30px);
  max-width: 75ch;
}
.faq-a p {
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.65;
  color: var(--ink-soft, rgba(10,10,10,0.72));
  margin: 0;
}
.faq-a p + p {
  margin-top: 12px;
}
.faq-a strong {
  color: var(--ink);
  font-weight: 600;
}

/* Team grid. Mirrors Terminal's leadership block with portrait cards;
   the styling stays in the existing visual language (uppercase eyebrow,
   display-font name lead-in, soft body copy). */
.about-team-head {
  margin-bottom: clamp(40px, 5vw, 64px);
  max-width: 720px;
}

.about-team-head .section-title { margin: 0 0 18px; }
.about-team-head .section-lead  { margin: 0; }

.about-team-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
}

.about-person {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.about-person-photo {
  width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0 0 6px;
  border-radius: var(--radius);
}

.about-person-name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 1.8vw, 26px);
  line-height: 1.05;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  color: var(--ink);
  margin: 0;
}

.about-person-role {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-blue);
  margin: 0;
}

.about-person-bio {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 6px 0 0;
}

@media (max-width: 980px) {
  .about-team-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  .about-team-grid { grid-template-columns: 1fr; }
}

/* Clients & partners — restrained logo strip with grayscale-by-default
   placeholders so a mix of colour and black logos stays visually unified. */
.about-clients-head {
  margin-bottom: clamp(40px, 5vw, 64px);
  max-width: 720px;
}

.about-clients-head .section-title { margin: 0; }

.about-clients-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(12px, 1.5vw, 20px);
  align-items: stretch;
}

.about-clients-grid li { display: flex; }

.about-client-logo {
  flex: 1;
  height: clamp(72px, 8vw, 110px);
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  transition: border-color .25s var(--ease);
}

.about-client-logo:hover { border-color: var(--ink-mute); }

.about-client-logo img {
  height: 100%;
  width: 100%;
  object-fit: contain;
  padding: clamp(10px, 1.4vw, 18px);
  filter: grayscale(1);
  opacity: 0.85;
  transition: filter .25s var(--ease), opacity .25s var(--ease);
}

.about-client-logo:hover img {
  filter: grayscale(0);
  opacity: 1;
}

@media (max-width: 980px) {
  .about-clients-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 520px) {
  .about-clients-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Final CTA — reuses the services CTA classes (.services-cta-final-*),
   so this rule only handles the surrounding section. */
.about-cta-final {
  text-align: center;
}

/* Tighten vertical rhythm on the À propos page. The base .section rule
   uses clamp(80px, 11vw, 160px) of vertical padding, which leaves too
   much air between alternating image/text rows. Override with about half
   that for the row sections specifically — the eye still gets clear
   section separation thanks to the background grid colour shift, but
   adjacent rows feel like a single composition rather than disconnected
   blocks. */
.section.about-row,
.section.about-team,
.section.about-clients,
.section.about-stat-band {
  padding: clamp(12px, 1.5vw, 22px) 0;
}

/* Remove the 1px hairline that the base .section rule draws between
   adjacent rows so the three about-row blocks read as a single
   continuous composition instead of three separated panels. */
.section.about-row,
.section.section-about {
  border-top: 0;
}

/* Tighten the gap between the dark stat band and the address/map
   section right below it — they belong together visually. */
.section.section-about {
  padding-top: clamp(40px, 5vw, 72px);
}

/* Section-level numbered marker (04 — Siège social) — sits above the
   about-grid with a bit of breathing room. Reuses the same .num-mark
   look as the row variants and inherits the flash-electrique animation
   from the global initNumFlash observer. */
.about-section-num {
  margin-bottom: clamp(20px, 3vw, 36px);
}

.about-feature-image {
  margin-bottom: clamp(6px, 0.8vw, 12px);
}

.about-team-head,
.about-clients-head {
  margin-bottom: clamp(20px, 2.5vw, 32px);
}

/* Respect reduced-motion: stop the slow grid pan and glow drift, keep the
   visuals (no jarring snap-back on subsequent visits). */
@media (prefers-reduced-motion: reduce) {
  .about-bg-grid,
  .about-bg-grid-dark,
  .about-bg-glow,
  .about-bg-spark {
    animation: none !important;
  }

  .about-hero .about-bg-grid::before {
    animation: none !important;
  }
}

/* ============================================================
   À propos page — scroll-triggered animations
   ============================================================
   All visual states tied to JS-added classes:
     .is-charged   → on .num-mark (flash electrique)
     .is-complete  → on .about-stat-figure (count-up pulse)
     .is-revealed  → on .about-row-figure (wipe reveal)
     .stagger-item / .in → on team/clients/checklist items
     .is-typing    → on .js-typewriter (terminal caret)
*/

/* A. Count-up pulse — when the 100% number reaches its target the
   figure gets .is-complete and a brief glow + scale pulse fires. */
.about-stat-figure.is-complete {
  animation: aboutStatPulse 700ms cubic-bezier(.2, .7, .2, 1) 1;
}

@keyframes aboutStatPulse {
  0%   { text-shadow: 0 0 0 rgba(126, 217, 87, 0);
         transform: scale(1); }
  35%  { text-shadow:
           0 0 12px rgba(255, 255, 255, 0.9),
           0 0 28px rgba(126, 217, 87, 0.85),
           0 0 64px rgba(126, 217, 87, 0.55);
         transform: scale(1.025); }
  100% { text-shadow: 0 0 0 rgba(126, 217, 87, 0);
         transform: scale(1); }
}

/* D. Flash électrique — the numbered chapter mark flickers continuously
   while the section is in view. The cycle spends most of its time in the
   normal "off" state with brief, irregular flicker bursts so it feels
   like a faulty fuse rather than a smooth pulse. JS toggles .is-charged
   on enter/exit of the viewport. */
.about-row-num .num-mark.is-charged {
  animation: numFuseFlicker 6s steps(20, end) infinite;
}

.about-row-num.light .num-mark.is-charged {
  animation: numFuseFlickerDark 6s steps(20, end) infinite;
}

@keyframes numFuseFlickerDark {
  0%, 18% {
    color: #fff;
    border-color: var(--line-dark);
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  19% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.32);
    box-shadow: 0 0 18px rgba(126, 217, 87, 0.9),
                0 0 36px rgba(126, 217, 87, 0.45);
  }
  20% {
    color: #fff;
    border-color: var(--line-dark);
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  22% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.18);
    box-shadow: 0 0 12px rgba(126, 217, 87, 0.7);
  }
  24%, 47% {
    color: #fff;
    border-color: var(--line-dark);
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  48% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.4);
    box-shadow: 0 0 22px rgba(126, 217, 87, 1),
                0 0 48px rgba(126, 217, 87, 0.5);
  }
  50% {
    color: var(--accent-green);
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.12);
    box-shadow: 0 0 10px rgba(126, 217, 87, 0.5);
  }
  52%, 78% {
    color: #fff;
    border-color: var(--line-dark);
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  79% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.22);
    box-shadow: 0 0 14px rgba(126, 217, 87, 0.75);
  }
  80%, 100% {
    color: #fff;
    border-color: var(--line-dark);
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
}

@keyframes numFuseFlicker {
  /* Long stretch of "off" state */
  0%, 18% {
    color: var(--ink);
    border-color: var(--line);
    background: var(--bg-alt);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  /* First flicker burst: short, bright */
  19% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.32);
    box-shadow: 0 0 18px rgba(126, 217, 87, 0.9),
                0 0 36px rgba(126, 217, 87, 0.45);
  }
  20% {
    color: var(--ink);
    border-color: var(--line);
    background: var(--bg-alt);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  22% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.18);
    box-shadow: 0 0 12px rgba(126, 217, 87, 0.7);
  }
  24%, 47% {
    color: var(--ink);
    border-color: var(--line);
    background: var(--bg-alt);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  /* Second burst: stronger glow + lingering green */
  48% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.4);
    box-shadow: 0 0 22px rgba(126, 217, 87, 1),
                0 0 48px rgba(126, 217, 87, 0.5);
  }
  50% {
    color: var(--accent-green);
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.12);
    box-shadow: 0 0 10px rgba(126, 217, 87, 0.5);
  }
  52%, 78% {
    color: var(--ink);
    border-color: var(--line);
    background: var(--bg-alt);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
  /* Third burst: tiny tease */
  79% {
    color: #fff;
    border-color: var(--accent-green);
    background: rgba(126, 217, 87, 0.22);
    box-shadow: 0 0 14px rgba(126, 217, 87, 0.75);
  }
  80%, 100% {
    color: var(--ink);
    border-color: var(--line);
    background: var(--bg-alt);
    box-shadow: 0 0 0 rgba(126, 217, 87, 0);
  }
}

/* E + F + H. Generic stagger-item — opacity/transform initial state,
   .in class flips to final state. JS sets per-item transition-delay. */
.stagger-item {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 600ms cubic-bezier(.2, .7, .2, 1),
              transform 600ms cubic-bezier(.2, .7, .2, 1);
}
.stagger-item.in {
  opacity: 1;
  transform: translateY(0);
}

/* F. Clients logos — scale-in feels more "pop" than translate. */
.about-clients-grid li.stagger-item {
  transform: scale(0.88);
}
.about-clients-grid li.stagger-item.in {
  transform: scale(1);
}

/* H. Checklist — slide-in from the left to mirror the reading direction. */
.about-row .checklist li.stagger-item {
  transform: translateX(-10px);
}
.about-row .checklist li.stagger-item.in {
  transform: translateX(0);
}

/* E. Team photo hover — subtle scale + soft glow. */
.about-person-photo {
  overflow: hidden;
  transition: transform 320ms cubic-bezier(.2, .7, .2, 1);
}
.about-person-photo img {
  transition: transform 500ms cubic-bezier(.2, .7, .2, 1);
}
.about-person:hover .about-person-photo img {
  transform: scale(1.04);
}

/* G. CTA finale — continuous subtle electric pulse on the primary
   button. Box-shadow breathes between a soft inner glow and a wider
   diffuse halo, with a barely-visible scale. */
.about-cta-final .cta-primary {
  animation: ctaElectricPulse 3.6s ease-in-out infinite;
  will-change: box-shadow, transform;
}

@keyframes ctaElectricPulse {
  0%, 100% {
    box-shadow:
      0 0 0 0 rgba(126, 217, 87, 0),
      0 0 8px rgba(126, 217, 87, 0.25);
    transform: scale(1);
  }
  50% {
    box-shadow:
      0 0 0 6px rgba(126, 217, 87, 0.12),
      0 0 24px rgba(126, 217, 87, 0.55);
    transform: scale(1.015);
  }
}

/* I. Typewriter — blinking caret while typing. */
.js-typewriter.is-typing::after {
  content: '';
  display: inline-block;
  width: 0.55em;
  height: 1em;
  margin-left: 2px;
  background: var(--accent-blue);
  vertical-align: -0.12em;
  animation: typewriterCaret 0.6s steps(2, end) infinite;
}

@keyframes typewriterCaret {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* J. Parallax interne — the image is scaled slightly so the runtime
   translateY (applied inline by JS) doesn't expose empty space at the
   edges of the figure. */
.about-feature-figure img {
  will-change: transform;
  /* 1.5× scale gives 25% overshoot on each side — generous cropped
     buffer so the JS-driven translateY never exposes the figure
     background behind the bottom edge. */
  transform: scale(1.5);
  transform-origin: center center;
  /* Anchor the image toward its bottom so the foreground (people in
     this photo) is the dominant content visible from the moment the
     banner enters the viewport. */
  object-position: 50% 75%;
}

/* Override the global [data-placeholder] background tint for the feature
   figure — once the photo loads we don't need the green/blue placeholder
   gradient. Transparent so any clipped-out region (bottom 32px strip
   outside the trapezoid, notches, etc.) reveals the chosen background
   layer underneath (.about-feature-image::before for the dark notches,
   .about-hero-stack white bg for the bottom strip and margin). */
.about-feature-figure {
  background: transparent;
}
.about-feature-figure::before { display: none; }

/* Inner-row parallax — image pre-scaled so the JS-driven translateY
   never reveals the cropped edges of the figure. The translation itself
   is applied as inline style by initAboutRowParallax in script.js. */
.about-row-figure img {
  will-change: transform;
  transform: scale(1.3);
  transform-origin: center center;
}

@media (prefers-reduced-motion: reduce) {
  .about-row-figure img { transform: none; }
}

/* Reduced motion — kill the new animations. .is-* classes still get
   added (so static end state is reached), but the transitions/keyframes
   are neutralised. */
@media (prefers-reduced-motion: reduce) {
  .about-stat-figure.is-complete,
  .about-row-num .num-mark.is-charged,
  .about-cta-final .cta-primary {
    animation: none !important;
  }
  .stagger-item,
  .about-person-photo img {
    transition: none !important;
  }
  .stagger-item {
    opacity: 1;
    transform: none;
  }
}

/* =============================================================
   CONNEXION PAGE
   Client portal sign-in. Centred card layout with the same
   typographic system as the rest of the site, but on a slightly
   warmer background so the page reads as a utility view rather
   than marketing content.
   ============================================================= */
.page-login {
  background: var(--bg-alt);
}

.login-section {
  min-height: calc(100vh - 80px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(80px, 12vw, 160px) 0;
}

.login-container {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
}

.login-intro {
  text-align: center;
  margin-bottom: clamp(32px, 4vw, 48px);
}
.login-intro .about-row-num {
  justify-content: center;
  margin-bottom: 24px;
}
.login-title {
  font-size: clamp(48px, 6vw, 88px);
  line-height: 1;
  margin: 0 0 16px;
}
.login-lead {
  font-size: clamp(15px, 1.3vw, 17px);
  line-height: 1.55;
  color: var(--ink-soft, rgba(10,10,10,0.7));
  max-width: 40ch;
  margin: 0 auto;
}

.login-form {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: clamp(28px, 4vw, 40px);
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 6px 24px rgba(0,0,0,0.05);
}

.login-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.login-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.login-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute, rgba(10,10,10,0.55));
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.login-forgot {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease, color .2s ease;
}
.login-forgot:hover {
  color: var(--accent-green, #1f8a2e);
  border-bottom-color: currentColor;
}

.login-input {
  font: inherit;
  font-size: 16px;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 14px 16px;
  width: 100%;
  transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
}
.login-input::placeholder {
  color: rgba(10,10,10,0.35);
}
.login-input:hover {
  border-color: rgba(10,10,10,0.30);
}
.login-input:focus {
  outline: none;
  border-color: var(--ink);
  background: #fff;
  box-shadow: 0 0 0 4px rgba(10,10,10,0.06);
}

.login-remember {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--ink-soft, rgba(10,10,10,0.7));
  cursor: pointer;
  user-select: none;
  margin-top: 4px;
}
.login-remember input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent-green, #7ed957);
  margin: 0;
  cursor: pointer;
}

.login-submit {
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--accent-green, #7ed957);
  border: none;
  border-radius: 999px;
  padding: 16px 28px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 8px;
  transition: background .2s ease, transform .15s ease, box-shadow .2s ease;
}
.login-submit .arrow {
  display: inline-block;
  transition: transform .25s ease;
}
.login-submit:hover {
  background: #5ec73a;
  box-shadow: 0 6px 18px rgba(126, 217, 87, 0.35);
}
.login-submit:hover .arrow {
  transform: translateX(4px);
}
.login-submit:active {
  transform: translateY(1px);
}

.login-help {
  text-align: center;
  font-size: 14px;
  color: var(--ink-soft, rgba(10,10,10,0.7));
  margin: 16px 0 0;
}
.login-help-link {
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  transition: color .2s ease;
}
.login-help-link:hover {
  color: var(--accent-green, #1f8a2e);
}

/* ============================================================
   Responsive safety layer
   Desktop keeps the large cinematic/full-bleed layouts. Tablet and mobile
   switch complex grids/bleeds back into bounded, stable flows.
   ============================================================ */
/* Tablet (721–1100px) now inherits the FULL desktop banner — pinned sticky +
   scroll-scrub until the end, then the page continues. These static-banner
   overrides therefore apply to MOBILE (≤720) only, where the canvas fixed-pin
   drives the scrub instead. (Layout/partners-grid rules below stay at ≤1100.) */
@media (max-width: 720px) {
  .entry {
    height: auto !important;
    --p-intro: 1;
    --p: 1;
  }

  .entry-stage {
    position: relative;
    top: auto;
    overflow: hidden;
  }

  .entry-video-wrap {
    aspect-ratio: 16 / 9;
  }

  .entry-text,
  .entry-grid,
  .entry-scenes {
    display: none;
  }

  .entry-meta {
    right: 16px;
    top: 16px;
    max-width: calc(100% - 32px);
    opacity: 1;
    transform: none;
  }

  .control-panel {
    position: absolute;
  }

  .entry-console {
    transform: none;
    opacity: 1;
  }

  .cp-inner {
    padding-inline: var(--pad);
  }

  .cp-row-top {
    display: none;
  }

  .cp-row-bottom {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .cp-cell:nth-child(n+4) {
    display: none;
  }
}

/* Grid overflow safety + tablet partners grid — apply across the whole
   ≤1100 range (these are not banner-specific). */
@media (max-width: 1100px) {
  .container,
  .hero-grid,
  .entry-inner,
  .section-head,
  .projects-grid,
  .partners-grid,
  .home-services-grid,
  .advantage-grid,
  .railway-grid,
  .contact-layout,
  .footer-inner {
    min-width: 0;
  }

  .partners-grid {
    width: 100%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-rows: minmax(118px, auto);
  }

  .partners-grid > .partner-logo:nth-child(n) {
    grid-column: auto;
  }

  .partners-grid::before {
    width: 100%;
    background-size: 33.333% 118px;
  }
}

@media (min-width: 721px) and (max-width: 1100px) {
  #partenaires .partners-grid {
    --partner-tablet-row: clamp(118px, 12.8vw, 142px);
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-rows: var(--partner-tablet-row);
  }

  #partenaires .partners-grid::before {
    background-size: 33.333333% var(--partner-tablet-row);
    background-position: 0 40px;
  }

  #partenaires .partner-logo {
    min-height: var(--partner-tablet-row);
    padding: clamp(18px, 2.2vw, 24px);
    display: flex;
    align-items: center;
    justify-content: center;
  }

  #partenaires .partner-logo img {
    display: block;
    margin: auto;
    max-width: min(88%, 210px);
    max-height: clamp(58px, 6.8vw, 84px);
    object-fit: contain;
    object-position: center;
  }

  #partenaires .partners-blip {
    left: var(--tablet-x);
    top: var(--tablet-y);
    background: rgba(10, 10, 10, 0.58);
    z-index: 2;
  }

  #partenaires .partners-grid > .partners-blip:nth-of-type(1)  { --tablet-x: 0%;          --tablet-y: 0; }
  #partenaires .partners-grid > .partners-blip:nth-of-type(2)  { --tablet-x: 33.333333%;  --tablet-y: 0; }
  #partenaires .partners-grid > .partners-blip:nth-of-type(3)  { --tablet-x: 66.666667%;  --tablet-y: 0; }
  #partenaires .partners-grid > .partners-blip:nth-of-type(4)  { --tablet-x: 100%;        --tablet-y: 0; }
  #partenaires .partners-grid > .partners-blip:nth-of-type(5)  { --tablet-x: 0%;          --tablet-y: calc(var(--partner-tablet-row) * 1); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(6)  { --tablet-x: 33.333333%;  --tablet-y: calc(var(--partner-tablet-row) * 1); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(7)  { --tablet-x: 66.666667%;  --tablet-y: calc(var(--partner-tablet-row) * 1); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(8)  { --tablet-x: 100%;        --tablet-y: calc(var(--partner-tablet-row) * 1); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(9)  { --tablet-x: 0%;          --tablet-y: calc(var(--partner-tablet-row) * 2); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(10) { --tablet-x: 33.333333%;  --tablet-y: calc(var(--partner-tablet-row) * 2); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(11) { --tablet-x: 66.666667%;  --tablet-y: calc(var(--partner-tablet-row) * 2); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(12) { --tablet-x: 100%;        --tablet-y: calc(var(--partner-tablet-row) * 2); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(13) { --tablet-x: 0%;          --tablet-y: calc(var(--partner-tablet-row) * 3); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(14) { --tablet-x: 33.333333%;  --tablet-y: calc(var(--partner-tablet-row) * 3); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(15) { --tablet-x: 66.666667%;  --tablet-y: calc(var(--partner-tablet-row) * 3); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(16) { --tablet-x: 100%;        --tablet-y: calc(var(--partner-tablet-row) * 3); }
  #partenaires .partners-grid > .partners-blip:nth-of-type(n+17) { display: none; }
}

@media (max-width: 960px) {
  .col-left,
  .about-map {
    position: static;
    top: auto;
  }
  /* .map-frame MUST stay positioned: it is the containing block for its
     absolutely-positioned <iframe>. Making it `static` let the iframe escape
     and float to the top of the page over the other sections. */
  .map-frame {
    position: relative;
  }

  .about-feature-image::before {
    display: none;
  }

  /* Mobile: the team feature image is full-bleed (edge to edge, no rounded
     corners) and KEEPS the sticky parallax (from the base CSS) so it climbs
     up over the dark hero grid on scroll while the hero title fades out (CRT)
     just before — exactly like desktop. overflow:hidden clips it to a clean
     rectangle. */
  .about-feature-figure {
    width: var(--vw100);
    max-width: none;
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    height: auto;
    aspect-ratio: 4 / 3;
    clip-path: none;
    border-radius: 0;
    overflow: hidden;
  }

  /* Section 04 railway banner keeps its full-bleed trapezoid treatment from
     the dedicated responsive rules above. */
}

@media (max-width: 860px) {
  /* Mobile: section images are full-bleed (edge to edge), no rounded
     corners, and keep the scroll parallax (scale 1.3 img translated by JS).
     overflow:hidden clips the over-scanned image to a clean rectangle. */
  .about-row-text-first .about-row-figure,
  .about-row-image-first .about-row-figure {
    width: 100vw;
    max-width: none;
    /* justify-self start for both variants so the 100vw bleed anchors to the
       viewport's left edge — text-first defaults to `end`, offsetting it ~21px. */
    justify-self: start;
    margin-left: calc(50% - 50vw);
    margin-right: 0;
    clip-path: none;
    border-radius: 0;
    overflow: hidden;
  }

  .about-row-text-first .about-row-text,
  .about-row-image-first .about-row-text {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
  }
}

@media (min-width: 721px) and (max-width: 960px) {
  .section-mission,
  #services {
    --section-notch-y: clamp(40px, 6.4vw, 62px);
    -webkit-clip-path: polygon(
      0 var(--section-notch-y),
      13% var(--section-notch-y),
      18% calc(var(--section-notch-y) - 4px),
      24% calc(var(--section-notch-y) - 15px),
      31% calc(var(--section-notch-y) - 30px),
      39% 0,
      61% 0,
      69% calc(var(--section-notch-y) - 30px),
      76% calc(var(--section-notch-y) - 15px),
      82% calc(var(--section-notch-y) - 4px),
      87% var(--section-notch-y),
      100% var(--section-notch-y),
      100% 100%,
      0 100%
    );
            clip-path: polygon(
      0 var(--section-notch-y),
      13% var(--section-notch-y),
      18% calc(var(--section-notch-y) - 4px),
      24% calc(var(--section-notch-y) - 15px),
      31% calc(var(--section-notch-y) - 30px),
      39% 0,
      61% 0,
      69% calc(var(--section-notch-y) - 30px),
      76% calc(var(--section-notch-y) - 15px),
      82% calc(var(--section-notch-y) - 4px),
      87% var(--section-notch-y),
      100% var(--section-notch-y),
      100% 100%,
      0 100%
    );
  }
}

@media (min-width: 721px) and (max-width: 1100px) {
  .section-mission,
  #services {
    --section-tablet-curve: clamp(34px, 4.2vw, 44px);
  }

  .section-mission {
    overflow: hidden;
    -webkit-clip-path: url(#sectionTopNotch);
            clip-path: url(#sectionTopNotch);
  }

  #services {
    overflow: visible;
    -webkit-clip-path: none;
            clip-path: none;
  }

  #services::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: calc(-1 * var(--section-tablet-curve) + 1px);
    z-index: 0;
    height: calc(var(--section-tablet-curve) + 2px);
    background: var(--ink);
    pointer-events: none;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 44' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 44V18C67 18 101 0 210 0C319 0 353 18 420 18V44H0Z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 44' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 44V18C67 18 101 0 210 0C319 0 353 18 420 18V44H0Z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
  }

  #services > .container {
    position: relative;
    z-index: 1;
  }

  .section-mission {
    height: min(calc(0.52 * var(--vw100)), calc(0.62 * var(--vh100)));
    min-height: clamp(500px, 52vw, 570px);
  }

  .mission-content {
    transform: translateY(clamp(-42px, -3.2vw, -24px));
  }

  .mission-title {
    margin: clamp(14px, 1.8vw, 20px) auto clamp(18px, 2.2vw, 24px);
    font-size: clamp(54px, 7.1vw, 78px);
    line-height: 0.98;
  }

  .mission-lead {
    max-width: 66ch;
    font-size: clamp(14px, 1.55vw, 17px);
    line-height: 1.5;
  }

  #services {
    margin-top: calc(-1 * clamp(86px, 10vw, 124px));
    padding-top: clamp(106px, 12vw, 148px);
  }
}

@media (max-width: 720px) {
  .entry-divider {
    height: 82px;
    background: var(--bg);
    margin-top: -1px;
    margin-bottom: -8px;
    border: 0;
    box-shadow: none;
    outline: 0;
  }

  .entry-divider::before {
    display: none;
  }

  .entry-divider::after {
    display: none;
  }

  .entry-divider-svg {
    height: 68px;
    margin-top: -1px;
    border: 0;
    box-shadow: none;
    outline: 0;
  }

  .entry-divider-path--desktop {
    display: none;
  }

  .entry-divider-path--mobile {
    display: inline;
  }

  .home-services-grid,
  .advantage-grid {
    grid-template-columns: 1fr;
  }

  .partners-grid {
    --partner-mobile-row: clamp(172px, 50vw, 224px);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: var(--partner-mobile-row);
  }

  .partners-grid::before {
    background-size: 50% var(--partner-mobile-row);
  }

  .home-service,
  .advantage-card,
  .contact-card,
  .contact-address {
    padding: clamp(22px, 7vw, 34px);
  }

  .cp-row-bottom {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cp-cell:nth-child(3) {
    display: none;
  }
}

@media (max-width: 480px) {
  .site-header,
  .nav-menu {
    max-width: calc(100vw - 20px);
  }

  .section-title,
  .about-hero-title,
  .contact-title {
    overflow-wrap: anywhere;
  }
}

/* ============================================================
   Terminal-style mobile pass
   Mobile gets its own stable flow: a large visual opener, bounded
   floating navigation, and no desktop sticky section collisions.
   ============================================================ */
@media (max-width: 720px) {
  .site-header {
    top: max(14px, env(safe-area-inset-top));
    width: calc(100vw - 40px);
    max-width: 720px;
  }

  .header-inner {
    height: 64px;
    padding: 6px 6px 6px 16px;
    border-radius: 18px;
  }

  .brand-logo {
    height: 34px;
    width: 58px;
    object-fit: contain;
  }

  .header-actions {
    gap: 8px;
    flex: 0 0 auto;
  }

  .header-actions .cta-primary {
    display: none;
  }

  .icon-btn,
  .menu-toggle {
    width: 40px;
    height: 40px;
    flex: 0 0 40px;
  }

  .nav-menu {
    top: calc(max(14px, env(safe-area-inset-top)) + 76px);
    left: 20px;
    right: 20px;
    bottom: 20px;
    width: auto;
    max-width: none;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 20px;
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.45);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .nav-menu-inner {
    padding: clamp(24px, 7vw, 36px) clamp(18px, 6vw, 28px);
    gap: clamp(24px, 8vw, 40px);
  }

  .nav-menu-row {
    gap: 16px;
    padding: clamp(16px, 5vw, 24px) 0;
    font-size: clamp(34px, 11vw, 56px);
    line-height: 0.95;
  }

  /* Keep sub-menu pills compact at the small breakpoint. */
  .nav-menu-sub a {
    padding: 12px 12px;
  }

  .nav-menu-meta {
    gap: 20px;
  }

  .entry-video-wrap {
    /* Grow to fill the space between the top padding and the KPI band so the
       video + KPI bar + console + closing curve all fit and adapt to any
       screen height/width (object-fit:cover reframes the crop). */
    flex: 1 1 auto;
    aspect-ratio: auto;
    height: auto;
    min-height: 0;
    overflow: visible;
    margin-bottom: 50px;
  }

  .entry {
    /* Spacer for the fixed-pin: full-screen banner height + scrub runway.
       Exact px set by JS (syncMobileSection); this calc is the pre-JS value. */
    height: calc(100svh + var(--entry-mobile-scrub, 1200px)) !important;
    --p-intro: 1;
    --p: 1;
    background: var(--bg);
  }

  /* Terminal-style FIXED pin (replaces position:sticky). `sticky` is recalced
     by the browser and lagged the smooth scroll, so the banner jumped and its
     dark bottom edge flashed a seam. Here JS toggles .entry-pinned / .entry-
     after at the scrub boundaries, synced to Lenis. The banner fills the
     viewport, so there is no gap and no lag-prone reveal. */
  .entry-stage {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    min-height: 100svh;
    z-index: 3;
    overflow: hidden;
    padding-top: calc(max(14px, env(safe-area-inset-top)) + 82px);
    background: #050505;
    box-shadow: none;
  }

  .entry-stage.entry-pinned {
    position: fixed;
    top: 0;
  }

  .entry-stage.entry-after {
    position: absolute;
    top: auto;
    bottom: 0;
  }

  .entry.entry-scroll-driven .entry-stage,
  .entry.entry-scroll-driven .entry-stage.entry-pinned,
  .entry.entry-scroll-driven .entry-stage.entry-after {
    position: relative;
    top: auto;
    bottom: auto;
    left: auto;
    right: auto;
  }

  /* Closing curve sits at the very bottom; the video now grows to fill the
     space above the KPI band, so the divider stays at the bottom without an
     auto-margin (which would otherwise eat the free space and stop the video
     from growing). */
  .entry-divider {
    margin-top: 0;
  }

  .entry + .hero-split {
    border-top: 0 !important;
    box-shadow: none;
    outline: 0;
    /* Hero just flows after the pinned banner — no negative-margin overlap and
       no JS padding reveal (that reveal was the jump + seam source). */
    margin-top: -1px;
    padding-top: clamp(6px, 2vw, 14px);
    transform: none;
    background: var(--bg);
    position: relative;
    z-index: 1;
  }

  .entry-video-wrap .control-panel {
    top: 100%;
    bottom: auto;
    height: 50px;
    background:
      linear-gradient(180deg, rgba(8, 12, 15, 0.82) 0%, rgba(4, 4, 4, 0.92) 100%);
    border-top: 1px solid rgba(255, 255, 255, 0.10);
    backdrop-filter: blur(16px) saturate(140%);
    -webkit-backdrop-filter: blur(16px) saturate(140%);
  }

  .control-panel .cp-strip {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
  }

  .entry-video-wrap .control-panel .cp-inner {
    display: flex;
    height: 100%;
    max-width: none;
    padding: 10px max(20px, var(--pad)) 8px;
    gap: 0;
  }

  .entry-video-wrap .cp-row-top {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: space-between;
    gap: 10px;
    font-size: 9px;
    letter-spacing: 0.14em;
  }

  .entry-video-wrap .cp-status {
    min-width: 0;
    gap: 7px;
  }

  .entry-video-wrap .cp-led {
    width: 7px;
    height: 7px;
    flex: 0 0 7px;
  }

  .entry-video-wrap .cp-status .cp-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .entry-video-wrap .cp-meta {
    display: none;
  }

  .entry-video-wrap .cp-progress {
    flex: 0 0 clamp(126px, 36vw, 150px);
    min-width: 0;
    gap: 7px;
  }

  .entry-video-wrap .cp-bar {
    height: 3px;
  }

  .entry-video-wrap .cp-pct {
    min-width: 28px;
  }

  .entry-still,
  .entry-video {
    transform: none;
    object-fit: cover;
    object-position: 50% 50%;
    filter: none;
  }

  .entry-video { display: block; }
  .entry-seq { display: block; }

  /* Scene captions type over the banner video on mobile too (hidden by the
     ≤1100 rule above; re-enabled here). Centered in the cinematic strip and
     scaled down to fit; the typing is driven by updateScenes() in rAF. */
  .entry-scenes {
    display: grid;
    /* Lower third of the tall portrait video — hero-style, like the desktop. */
    place-items: end center;
    padding: 0 clamp(16px, 6vw, 28px) clamp(26px, 9vw, 52px);
    opacity: 1;
  }

  .entry-scene {
    font-size: clamp(30px, 9.6vw, 46px);
    line-height: 1.12;
    letter-spacing: 0.01em;
    color: #fff;
    -webkit-text-fill-color: #fff;
    max-width: 15ch;
    /* Identical to desktop: soft dark shadow only — no darkening, no glow. */
    text-shadow: 0 2px 28px rgba(0, 0, 0, 0.45);
  }

  .entry-scene.is-active,
  .entry-scene.is-active .es-char,
  .entry-scene.is-active .es-char.is-shown {
    color: #fff;
    -webkit-text-fill-color: #fff;
  }

  .entry-scenes,
  .entry-scene,
  .entry-scene .es-char {
    mix-blend-mode: normal;
    filter: none;
  }

  /* No green halo on the active line — desktop only changes opacity. */
  .entry-scene.is-active {
    text-shadow: 0 2px 28px rgba(0, 0, 0, 0.45);
  }

  /* Leading "typed" char: plain green like desktop, no green glow. */
  .entry-scene.is-active .es-char.is-cursor {
    color: var(--accent-green);
    -webkit-text-fill-color: var(--accent-green);
    text-shadow: 0 2px 28px rgba(0, 0, 0, 0.45);
  }

  @supports (-webkit-touch-callout: none) {
    .entry-scene.is-active .es-char,
    .entry-scene.is-active .es-char.is-shown {
      color: #fff;
      -webkit-text-fill-color: #fff;
      opacity: 1;
    }

    .entry-scene {
      opacity: 0;
    }

    .entry-scene.is-active {
      opacity: 1;
    }

    .entry-scene .es-char:not(.is-shown) {
      opacity: 0;
    }

    .entry-scene.is-active .es-char.is-cursor {
      color: var(--accent-green);
      -webkit-text-fill-color: var(--accent-green);
    }
  }

  .entry-meta {
    display: none;
  }

  .entry-console {
    padding-block: 0;
  }

  .entry-console .cp-inner {
    padding: 16px max(14px, var(--pad)) 18px;
  }

  .cp-row-bottom {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0;
    padding-top: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
  }

  .cp-cell {
    display: flex;
    min-width: 0;
    min-height: 52px;
    justify-content: center;
    padding: 8px 5px 7px 9px;
  }

  .cp-cell:nth-child(3),
  .cp-cell:nth-child(4) {
    display: flex;
  }

  .cp-cell:nth-child(n+5) {
    display: none;
  }

  .cp-cell:nth-child(n+2) {
    border-left: 1px solid rgba(255, 255, 255, 0.08);
    padding-left: 10px;
  }

  .cp-cell:nth-child(n+3) {
    border-top: 0;
  }

  .cp-cell::before {
    top: 11px;
    bottom: 11px;
    width: 2px;
  }

  .cp-label {
    font-size: clamp(6.4px, 1.65vw, 7.6px);
    letter-spacing: 0.12em;
    white-space: nowrap;
  }

  .cp-value {
    font-size: clamp(18px, 4.8vw, 21px);
    white-space: nowrap;
  }

  .cp-value small {
    margin-left: 2px;
    font-size: 0.38em;
  }

  .section-mission {
    position: sticky;
    top: 0;
    z-index: 0;
    height: auto;
    min-height: 560px;
    padding: clamp(108px, 24vw, 150px) 0 clamp(72px, 18vw, 104px);
    -webkit-clip-path: url(#sectionTopNotch);
            clip-path: url(#sectionTopNotch);
  }

  #services {
    position: relative;
    z-index: 2;
    margin-top: calc(-1 * clamp(44px, 11vw, 72px));
    border-top: 0;
    padding-top: clamp(104px, 24vw, 144px);
  }

  .section-mission,
  #services {
    --section-trap-height: clamp(24px, 6.2vw, 32px);
    overflow: hidden;
    -webkit-clip-path: none;
            clip-path: none;
  }

  .section-mission::before,
  #services::before {
    content: '';
    position: absolute;
    top: -1px;
    left: 0;
    right: 0;
    z-index: 4;
    height: calc(var(--section-trap-height) + 2px);
    background: var(--bg);
    pointer-events: none;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 34' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 0H62C85 0 103 5 122 17L136 25C146 31 159 33 174 33H246C261 33 274 31 284 25L298 17C317 5 335 0 358 0H420V0H0Z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 34' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 0H62C85 0 103 5 122 17L136 25C146 31 159 33 174 33H246C261 33 274 31 284 25L298 17C317 5 335 0 358 0H420V0H0Z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
  }

  #services::before {
    display: none;
  }

  #services::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    z-index: 4;
    height: calc(var(--section-trap-height) + 2px);
    background: var(--bg);
    pointer-events: none;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 34' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 34V32H58C87 32 108 27 132 16C145 10 159 8 176 8H244C261 8 275 10 288 16C312 27 333 32 362 32H420V34H0Z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 34' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M0 34V32H58C87 32 108 27 132 16C145 10 159 8 176 8H244C261 8 275 10 288 16C312 27 333 32 362 32H420V34H0Z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
  }

  #services {
    -webkit-clip-path: polygon(
      0 0,
      15% 0,
      20% 2px,
      25% 7px,
      29.5% 14px,
      33.2% 22px,
      38.3% var(--section-trap-height),
      61.7% var(--section-trap-height),
      66.8% 22px,
      70.5% 14px,
      75% 7px,
      80% 2px,
      85% 0,
      100% 0,
      100% 100%,
      0 100%
    );
            clip-path: polygon(
      0 0,
      15% 0,
      20% 2px,
      25% 7px,
      29.5% 14px,
      33.2% 22px,
      38.3% var(--section-trap-height),
      61.7% var(--section-trap-height),
      66.8% 22px,
      70.5% 14px,
      75% 7px,
      80% 2px,
      85% 0,
      100% 0,
      100% 100%,
      0 100%
    );
  }

  .mission-content {
    padding-inline: var(--pad);
  }

  .mission-title {
    margin: 18px auto 24px;
    font-size: clamp(42px, 14vw, 70px);
    max-width: 10ch;
  }

  .mission-lead {
    font-size: 15px;
    line-height: 1.5;
    max-width: 31ch;
  }

  .section {
    padding-block: clamp(64px, 16vw, 96px);
  }

  .section-title {
    font-size: clamp(44px, 13vw, 72px);
    line-height: 0.98;
  }

  .section-lead,
  .partners-subtitle,
  .contact-lead {
    font-size: 17px;
    line-height: 1.58;
  }

  .home-service-title-row {
    align-items: flex-start;
  }

  .home-service-title-row .home-service-title {
    white-space: nowrap;
    font-size: clamp(22px, 6.4vw, 30px);
    line-height: 0.95;
    letter-spacing: 0;
    transform: translateY(10px);
  }

  .home-service-title-row .home-service-title br {
    display: none;
  }

  .projects-marquee {
    --pg-gap: 14px;
  }

  .project {
    border-radius: 8px;
  }

  .project figcaption {
    align-items: flex-start;
    gap: 10px;
  }

  .project-name {
    line-height: 1.2;
  }

  .section-partners {
    padding-bottom: clamp(56px, 14vw, 84px);
  }

  .partners-grid {
    --partner-mobile-row: clamp(172px, 50vw, 224px);
    --partner-mobile-line: rgba(10, 10, 10, 0.11);
    width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: var(--partner-mobile-row);
    background: transparent;
  }

  .partners-grid::before {
    display: block;
    top: -40px;
    bottom: -40px;
    left: 0;
    width: 100%;
    max-width: none;
    transform: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='140'><g stroke='%230a0a0a' stroke-opacity='0.10' stroke-width='1' fill='none' stroke-linecap='butt'><line x1='8' y1='0.5' x2='212' y2='0.5'/><line x1='0.5' y1='8' x2='0.5' y2='132'/></g></svg>");
    background-size: 50% var(--partner-mobile-row);
    background-position: 0 40px;
    background-repeat: repeat;
    -webkit-mask-image:
      linear-gradient(to bottom, transparent 0%, #000 7%, #000 93%, transparent 100%),
      linear-gradient(to right,  transparent 0%, #000 2%, #000 98%, transparent 100%);
    -webkit-mask-composite: source-in;
            mask-image:
      linear-gradient(to bottom, transparent 0%, #000 7%, #000 93%, transparent 100%),
      linear-gradient(to right,  transparent 0%, #000 2%, #000 98%, transparent 100%);
            mask-composite: intersect;
  }

  .partners-grid::after {
    display: none;
  }

  .partner-logo {
    min-height: var(--partner-mobile-row);
    padding: clamp(22px, 7vw, 34px) clamp(16px, 5vw, 28px);
    border: 0;
    overflow: hidden;
    background: transparent;
  }

  .partner-logo img {
    width: min(118%, 220px);
    max-width: none;
    max-height: none;
    height: auto;
  }

  #partenaires .partners-grid.is-mobile-rotating > .partner-logo:nth-child(n+5) {
    display: none;
  }

  #partenaires .partners-grid.is-mobile-rotating.is-revealed > .partner-logo > img {
    animation: none;
  }

  #partenaires .partners-grid.is-mobile-rotating .partner-logo img {
    opacity: 1;
    transition:
      opacity 820ms cubic-bezier(0.22, 1, 0.36, 1),
      transform 960ms cubic-bezier(0.16, 1.18, 0.32, 1),
      filter 820ms cubic-bezier(0.22, 1, 0.36, 1);
    filter: brightness(0) opacity(0.7);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    will-change: opacity, transform, filter;
  }

  #partenaires .partners-grid.is-mobile-rotating .partner-logo.is-logo-switching img {
    opacity: 0;
    transform: translateY(7px) scale(0.94);
    filter: brightness(0) opacity(0.18) blur(4px);
  }

  @supports (-webkit-touch-callout: none) {
    #partenaires .partners-grid.is-mobile-rotating .partner-logo.is-logo-switching img {
      filter: brightness(0) opacity(0.18);
    }
  }

  .hero-partners-track-wrap {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
  }

  .hero-partners-track,
  .hero-partners-track img,
  .partner-logo,
  .partner-logo img,
  .footer-logo {
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
  }

  .hero-partners-track img {
    filter: brightness(0);
    opacity: 1;
    transform: translateZ(0);
    will-change: transform;
  }

  .partner-logo img {
    filter: brightness(0) opacity(0.7);
    opacity: 1;
    transform: translateZ(0);
    will-change: opacity, transform, filter;
  }

  .footer-logo {
    filter: none;
    transform: translateZ(0);
  }

  .partners-blip {
    display: block;
    left: var(--mobile-x);
    top: var(--mobile-y);
    background: rgba(10, 10, 10, 0.58);
    z-index: 2;
  }

  .partners-grid > .partners-blip:nth-of-type(1)  { --mobile-x: 0%;   --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(2)  { --mobile-x: 50%;  --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(3)  { --mobile-x: 100%; --mobile-y: 0; }
  .partners-grid > .partners-blip:nth-of-type(4)  { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(5)  { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(6)  { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 1); }
  .partners-grid > .partners-blip:nth-of-type(7)  { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(8)  { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(9)  { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 2); }
  .partners-grid > .partners-blip:nth-of-type(10) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(11) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(12) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 3); }
  .partners-grid > .partners-blip:nth-of-type(13) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(14) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(15) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 4); }
  .partners-grid > .partners-blip:nth-of-type(16) { --mobile-x: 0%;   --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(17) { --mobile-x: 50%;  --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(18) { --mobile-x: 100%; --mobile-y: calc(var(--partner-mobile-row) * 5); }
  .partners-grid > .partners-blip:nth-of-type(n+19) { display: none; }
  #partenaires .partners-grid.is-mobile-rotating > .partners-blip:nth-of-type(n+10) { display: none; }

  .contact-map {
    min-height: 360px;
  }
}

@media (max-width: 380px) {
  .site-header {
    top: max(12px, env(safe-area-inset-top));
    width: calc(100vw - 24px);
  }

  .header-inner {
    padding-left: 14px;
  }

  .brand-logo {
    height: 30px;
  }

  .nav-menu {
    top: calc(max(12px, env(safe-area-inset-top)) + 72px);
    left: 12px;
    right: 12px;
    bottom: 12px;
  }
}

/* ---- Banner console + KPIs on tablet (721–1100px) ----
   The ≤1100 / ≤960 rules trim the console to 3 KPIs and hide the status row.
   Restore the full desktop console on tablet: status row + all 5 KPI cells. */
@media (min-width: 721px) and (max-width: 1100px) {
  .cp-row-top { display: flex; }
  .cp-row-bottom { grid-template-columns: repeat(5, 1fr); }
  .cp-cell:nth-child(n+4) { display: flex; }

  /* Fill the viewport with the dark stage (no white gap below) + keep the black
     padding above the video. Kept on the PC sticky pin + scrub. */
  .entry-stage {
    min-height: 100svh;
    padding-top: calc(max(14px, env(safe-area-inset-top)) + 88px);
    /* Same flat near-black as the mobile banner (instead of the desktop navy
       gradient) so the top padding above the video matches mobile. */
    background: #050505;
  }

  /* The video grows to fill the space between the top padding and the console,
     so the video + KPI bar + console + closing curve (bavette) all fit on
     screen together. aspect-ratio:auto + object-fit:cover lets the video adapt
     to any screen ratio — tablet portrait or landscape — with no dark gap. */
  .entry-video-wrap {
    flex: 1 1 auto;
    aspect-ratio: auto;
    height: auto;
    min-height: 0;
    width: 100%;
    margin-left: 0;
  }

  /* Caption text a bit larger on tablet (the PC base size is kept on PC). */
  .entry-scene {
    font-size: clamp(42px, 6.4vw, 78px);
  }
}

/* ---- Section 01 hero caption carousel: tablet sizing (721–1100px) ----
   The shared carousel block uses phone-sized slides (min(76vw, 310px) / 20px).
   Widen + enlarge them for tablet. The JS translates the track by each slide's
   real offset, so wider slides work without any JS change. */
@media (min-width: 721px) and (max-width: 1100px) {
  .hero-caption {
    flex: 0 0 min(62vw, 560px);
    width: min(62vw, 560px);
    min-height: clamp(84px, 11vw, 128px);
    font-size: clamp(22px, 3.2vw, 32px);
  }
}

/* ---- Sections 01-02: enlarge phone-sized type on tablet (721–1100px) ----
   The mobile layouts read too small on tablet. Tablet-only bumps. */
@media (min-width: 721px) and (max-width: 1100px) {
  /* Section 01 — step-nav numerators (01 02 03 04 05) */
  .hero-mobile-step-nav {
    font-size: 17px;
    gap: clamp(20px, 3vw, 34px);
  }

  /* Section 01 — "Nos collaborateurs" label + partner logos */
  .hero-split .hero-partners-label {
    font-size: 14px;
  }
  .hero-split .hero-partners-track img {
    height: clamp(56px, 7vw, 78px);
  }

  /* Section 02 — railway dot numerators (01 02 03) */
  .railway-dot {
    font-size: 16px;
  }

  /* Section 02 — railway card descriptions */
  .railway-desc {
    font-size: clamp(16px, 2vw, 19px);
    line-height: 1.4;
  }
}
