/* ---- LA PAGE GRAPHEN : qui nous sommes ----

   Une page CRAIE, sans la scene d'aurore : le banner de la reference —
   deux lignes geantes en quinconce, deux paragraphes satellites, deux
   grandes formes d'eau en surimpression sur le logotype. Les formes sont des BLOBS D'EAU
   (graphen-blobs.js) ; sans WebGPU, les ronds gris de la reference
   restent, et la composition tient telle quelle.

   Toutes les valeurs viennent de la suite (Fibonacci + Lucas) ou de la
   section d'or (19.1 / 38.2 / 61.8), la regle de la maison. */

body[data-page="graphen"] {
  background-color: var(--degrade-craie);
}

/* ---- LE BANNER ---- */

.banniere {
  position: relative;
  height: 100vh;
  min-height: var(--f-610);
  overflow: clip;
  /* Le diametre des deux formes : ~la moitie de la hauteur d'ecran,
     borne en largeur pour les fenetres etroites. */
  --bulle-d: min(38.2vh, 21vw);
  /* LE FOND DE L'ACCUEIL. Le canvas WebGPU peint le vrai (fond() de
     verre-statue, porte dans graphen-blobs.js) ; ce degrade CSS est son
     REPLI — les memes quatre teintes petrole/cuivre, les deux foyers aux
     positions de repos (28/33 et 72/63), comme le repli de hero-eau.css. */
  background:
    radial-gradient(circle 80vh at 72% 63%, rgba(247, 204, 168, 0.75), rgba(247, 204, 168, 0) 100%),
    radial-gradient(circle 130vh at 28% 33%, rgba(66, 121, 119, 0) 38%, rgba(66, 121, 119, 0.6) 100%),
    radial-gradient(circle 90vh at 28% 33%, rgb(199, 223, 221), rgb(124, 190, 184) 100%);
}

/* Les deux ronds de la reference. Ils sont le REPLI (sans WebGPU) et la
   POSITION de reference des blobs : le script lit leurs rects puis les
   cache — visibility et non display, pour que les rects restent
   mesurables. */
.banniere-cercle {
  position: absolute;
  width: var(--bulle-d);
  height: var(--bulle-d);
  border-radius: 50%;
  /* Sur le degrade, les ronds du repli sont des voiles d'eau, pas du gris. */
  background: rgba(255, 255, 255, 0.21);
  transform: translate(-50%, -50%);
  z-index: 0;
}
.bc-1 { left: 38.2%; top: 48%; }
.bc-2 { left: 61.8%; top: 48%; }
/* Des que WebGPU est present (html.eau-attente, posee par le script inline
   du head AVANT la premiere image), les ronds ne paraissent JAMAIS : on
   demarre directement dans l'eau. Ils ne servent qu'au vrai repli (pas de
   WebGPU, device perdu — repli() retire la classe). */
html.eau-attente .banniere-cercle,
.banniere.eau-active .banniere-cercle { visibility: hidden; }

/* ---- LA SEQUENCE D'ENTREE ----
   L'eau d'abord, puis le grand texte, puis les petits (demande de Quentin).
   Tout est cale sur .eau-active (la premiere image des blobs) : l'eau fond
   sur le degrade CSS (610), le titre parait (610 -> 1597), les paragraphes
   et « Scroll » suivent. Sous .eau-attente seulement : sans WebGPU la classe
   n'est jamais posee et le texte est la des la premiere image. */
.banniere-eau { opacity: 0; transition: opacity 610ms cubic-bezier(0.22, 1, 0.36, 1); }
html.eau-attente .banniere-titre,
html.eau-attente .banniere-para,
html.eau-attente .banniere-scroll { opacity: 0; }
.qui:has(.banniere.eau-active) .banniere-eau { opacity: 1; }
/* Les delais couvrent l'ENTREE des blobs (la goutte qui se divise puis les
   jumeaux qui grossissent, ~3.4 s dans graphen-blobs.js) : le grand texte
   parait quand les jumeaux se posent, les petits ensuite. */
.banniere.eau-active .banniere-titre {
  opacity: 1;
  transition: opacity 987ms cubic-bezier(0.22, 1, 0.36, 1) 3571ms;
}
.banniere.eau-active .banniere-para {
  opacity: 1;
  transition: opacity 987ms cubic-bezier(0.22, 1, 0.36, 1) 4558ms;
}
.banniere.eau-active .banniere-scroll {
  opacity: 1;
  transition: opacity 987ms cubic-bezier(0.22, 1, 0.36, 1) 5545ms;
}
@media (prefers-reduced-motion: reduce) {
  .banniere-eau,
  .banniere.eau-active .banniere-titre,
  .banniere.eau-active .banniere-para,
  .banniere.eau-active .banniere-scroll { transition: none; }
  .banniere-titre .bt-1,
  .banniere-titre .bt-2 { animation: none; }
}

/* Le canvas des blobs : au-dessus du logotype (z 2), sous les satellites
   (z 4). Il peint le fond ET l'eau ; le texte reste du DOM pour le repli.
   pointer-events none : il ne vole aucun clic (regle de la maison). */
.banniere-eau {
  /* LA VITRE (sequence) : FIXE plein ecran — les cards et le texte du flux
     defilent DERRIERE le mesh — au-dessus du flux, sous les stations
     collees (z 5), l'en-tete (z 6) et la barre (z 8). Transparente hors de
     l'eau (fsAffiche premultiplie l'alpha) : le degrade et le contenu
     vivent au travers. Elle ne capte aucun geste. */
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
}

/* Le logotype geant reprend exactement les parametres du logo de l'accueil
   (TAILLE_LOGO, graisse et interlettrage de verre-statue.js), derriere l'eau. */
.banniere-titre {
  position: absolute;
  inset: 0;
  margin: 0;
  z-index: 2;   /* derriere la vitre d'eau (3), comme le logo de l'accueil */
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  color: #FFFFFF;
  font-family: var(--font-main);
  font-size: 120px;
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1;
}
.bt-ligne {
  position: static;
  display: inline;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  white-space: nowrap;
}
.banniere-titre .logo-graphen { color: #FFFFFF; }
.banniere-titre .logo-studio { color: #000000; }
/* Le logotype reste centre dans la scene, comme celui peint par l'accueil.
   L'ecart entre les deux mots reprend ECART_MOTS de verre-statue.js. */
.bt-1,
.bt-2 { left: auto; top: auto; }

/* L'accroche courte reste au-dessus de l'eau (z 4) — elle n'est pas dans la
   texture refractee et passe DEVANT les blobs. */
.banniere-para {
  position: absolute;
  z-index: 4;
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  max-width: var(--l-322);
  color: #FFFFFF;
  /* la meme assurance que le texte de l'accueil : une ombre tres douce qui
     tient les lettres sur les zones claires du degrade */
  text-shadow: 0 1px 13px rgba(15, 19, 26, 0.34);
}
/* L'accroche est petite et centrée sous le logotype, sans changer les
   paramètres du logo lui-même. */
.banniere { --bt-corps: 120px; }
.banniere-accroche {
  left: 50%;
  top: calc(50% + 10rem);
  max-width: none;
  transform: translateX(-50%);
  white-space: nowrap;
  text-align: center;
}

.banniere-scroll {
  position: absolute;
  left: 50%;
  bottom: var(--f-34);
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--lh-sm);
  color: #FFFFFF;
  text-shadow: 0 1px 13px rgba(15, 19, 26, 0.34);
}

/* ---- LA SUITE : les volets du contenu ---- */

.qui-suite {
  position: relative;
  z-index: 5;
  background: var(--degrade-craie);
  padding: 0 var(--canvas-inset) var(--l-199);
}

.volet {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--f-89) 0;
  border-top: var(--border-hair) solid var(--color-line);
  display: grid;
  grid-template-columns: var(--f-233) 1fr;
  gap: var(--f-55);
  align-items: start;
}

.volet-sur {
  margin: 0;
  /* h2 depuis le passage en titres : on annule le gras du navigateur pour
     garder exactement le rendu de l'ancien <p>. */
  font-weight: var(--fw-light);
  font-size: var(--text-sm);
  line-height: var(--lh-sm);
  color: var(--logo-encre);
}

.volet-corps {
  max-width: var(--l-843);
}
.volet-corps > p { margin: 0; }
.volet-corps > p + p { margin-top: var(--f-21); }

/* Les prestations : une regle par ligne, le numero, le titre, le texte. */
.prestations-intro { margin: 0 0 var(--f-55); }

.prestations {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: presta;
}
.prestation {
  display: grid;
  grid-template-columns: var(--f-55) var(--f-233) 1fr;
  gap: var(--f-34);
  padding: var(--f-34) 0;
  border-top: var(--border-hair) solid var(--color-line);
}
.prestation::before {
  counter-increment: presta;
  content: "(" counter(presta, decimal-leading-zero) ")";
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  font-variant-numeric: tabular-nums;
}
.prestation h3 {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-snug);
  color: var(--logo-encre);
}
.prestation p {
  margin: 0;
  max-width: var(--l-521);
}

/* LES ENUMERATIONS. Elles suivent le passage, dans la meme colonne que lui.
   Une liste et non des titres : le referentiel SEO/GEO du studio note que
   listes et tableaux sont les formats que les moteurs generatifs decoupent
   et extraient le plus facilement — et vingt titres de plus auraient mis la
   hierarchie de la page a plat. */
.prestation-liste {
  grid-column: 3;
  margin: var(--f-13) 0 0;
  padding: 0;
  list-style: none;
  max-width: var(--l-521);
  color: var(--color-brand-studio);
}
.prestation-liste li { padding-left: var(--f-13); text-indent: calc(-1 * var(--f-13)); }
.prestation-liste li::before { content: "— "; }

/* Le contact : les lieux, puis les entrees. */
.contact-lieux {
  display: grid;
  gap: var(--f-8);
  margin-top: var(--f-34) !important;
}
.contact-lieux b {
  font-weight: var(--fw-medium);
  color: var(--logo-encre);
}
.contact-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--f-21);
  margin-top: var(--f-55) !important;
}
.contact-actions .etiquette { min-width: var(--etiquette-min); }
.contact-lien {
  color: var(--logo-encre);
  text-decoration: none;
}
.contact-lien:hover { text-decoration: underline; }

/* ---- Paliers ---- */

@media (max-width: 987px) {
  .banniere { --bulle-d: min(34vh, 47vw); }
  .banniere { --bt-corps: min(11vw, 120px); }
  .banniere-titre { font-size: min(11vw, 120px); }
  .bt-ligne { font-size: inherit; }
  .volet { grid-template-columns: 1fr; gap: var(--f-21); padding: var(--f-55) 0; }
  .prestation { grid-template-columns: var(--f-34) 1fr; }
  .prestation p,
  .prestation-liste { grid-column: 2; }
}

@media (max-width: 610px) {
  /* En mobile, plus d'absolu : une colonne centree, en FLUX — les blocs ne
     peuvent pas se marcher dessus, quelle que soit la hauteur d'ecran.
     Seuls les ronds (et l'eau) restent des calques. */
  .banniere {
    --bulle-d: min(29vh, 76vw);
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 var(--container-pad);
  }
  .banniere-titre { position: relative; }
  .bt-ligne {
    position: static;
    font-size: inherit;
    white-space: normal;
  }
  .banniere-titre { gap: 0.3em; }
  .banniere-para {
    position: static;
    max-width: var(--l-322);
    margin-top: var(--f-34);
  }
  .banniere-accroche {
    max-width: 100%;
    margin-top: var(--f-21);
    transform: none;
    white-space: normal;
    text-align: center;
  }
  /* Plus petits et bien ecartes : en portrait le monde est etroit — a 29vh
     les deux blobs se rejoignaient en cacahuete sur le titre. */
  .banniere { --bulle-d: min(21vh, 47vw); }
  .bc-1 { left: 72%; top: 26%; }
  .bc-2 { left: 26%; top: 55%; }
}

/* ======================================================================
   LA SEQUENCE (27/08) — eau -> buste -> card, sans quitter le banner.

   La page devient une SCENE de 1000vh : le banner se colle, et le
   defilement pilote tout. L'eau fusionne et devient le buste (WebGPU,
   graphen-blobs.js — SORTIE_BUSTE passe a 2.0 quand la scene est la),
   puis le buste cede la place a une CARD embossee AU CENTRE de l'ecran :
   leger cadre clair, creux extrude en negatif, et les photos des
   prestations qui crantent dedans. Les textes se relaient en STATIONS
   autour de la card — titre a gauche cale sur son bord haut, passage a
   droite pose sur son bord bas. La grille place, elle ne se montre pas.

   Le decoupage de la course (contain 0-100 %) :
     0-7      le titre du banner cede
     0-22     fusion des gouttes puis buste (WebGPU, fonction de scrollY)
     8-26     l'intro parait pendant que le buste se forme

   En repli (mobile <= 987, mouvement reduit, navigateur sans timelines),
   la scene redevient une page posee : banner normal, stations empilees
   avec leurs images, card absente. Tout le texte reste le meme — les
   moteurs ne voient aucune difference.
   ====================================================================== */

.scene {
  view-timeline-name: --sequence; view-timeline-axis: block;
  --carte-l: clamp(var(--l-322), 23vw, var(--f-377));
  --carte-h: calc(var(--carte-l) * 5 / 4);
}

/* Le pilote JS prend la main sur desktop : la progression est calculee sur
   la course reelle de la scene, ce qui reste stable avec les grands deltas de
   trackpad. Les replis mobile et mouvement reduit gardent le flux naturel. */
@media (min-width: 988px) and (prefers-reduced-motion: no-preference) {
  .scene[data-sequence-js]::before,
  .scene[data-sequence-js] .banniere-scroll,
  .scene[data-sequence-js] .banniere-para,
  .scene[data-sequence-js] .banniere-titre .bt-1,
  .scene[data-sequence-js] .banniere-titre .bt-2,
  .scene[data-sequence-js] .banniere-cercle,
  .scene[data-sequence-js] .station-intro,
  .scene[data-sequence-js] .station-intro .cale-appui,
  .scene[data-sequence-js] .station-intro .cale-corps {
    animation: none !important;
  }

  .scene[data-sequence-js]::before {
    opacity: var(--sequence-scrim, 0);
  }

  .scene[data-sequence-js] .banniere-titre .bt-1,
  .scene[data-sequence-js] .banniere-titre .bt-2 {
    translate: none;
  }
}
@supports (animation-timeline: view()) {
  @media (min-width: 988px) and (prefers-reduced-motion: no-preference) {
    .scene { height: 360vh; }   /* davantage de temps de lecture pour la philosophie */
    
    /* Voile de métamorphose : assombrissement monochrome jusqu'au noir plein,
       régulier et sans halo circulaire. */
    .scene::before {
      content: "";
      position: fixed; inset: 0; z-index: -1;
      background: #000000;
      opacity: 0;
      animation: sq-assombrir linear both;
      animation-timeline: --sequence;
      animation-range: contain 0% contain 100%;
    }

    /* l'aurore fixe (ci-dessous) porte le fond depuis la premiere image :
       le degrade du banner ferait une couture en defilant par-dessus */
    .banniere { background: none; }
    /* l'aurore, FIXE sous toute la page : le banner finit par defiler,
       son degrade reste — le meme, au pixel */
    .qui::before {
      content: "";
      position: fixed; inset: 0; z-index: -2;
      background:
        radial-gradient(circle 80vh at 72% 63%, rgba(247, 204, 168, 0.75), rgba(247, 204, 168, 0) 100%),
        radial-gradient(circle 130vh at 28% 33%, rgba(66, 121, 119, 0) 38%, rgba(66, 121, 119, 0.6) 100%),
        radial-gradient(circle 90vh at 28% 33%, rgb(199, 223, 221), rgb(124, 190, 184) 100%);
    }
    /* z-index 4 : la banniere COLLEE passe DEVANT la vitre d'eau (3).
       Sticky cree son propre contexte d'empilement, donc le z 4 du titre
       ne comptait pas face au canvas — les blobs couvraient les lettres.
       C'est la banniere entiere qu'il faut monter : titre et paragraphes
       nets DEVANT les gouttes, la composition de la reference. Le
       surbanner (philosophie, z 2) reste, lui, DERRIERE le buste. */
    .banniere { position: sticky; top: 0; z-index: 4; }
    /* sous WebGPU la card DOM se tait : sa peinture vit dans le plan du
       fond, DERRIERE l'objet 3D, refractee par lui. La figure DOM reste
       mesurable (visibility, pas display) : c'est elle qui donne le rect. */
    html.eau-attente .surbanner .station-carte { visibility: hidden; }

    /* LA SORTIE LATERALE DE LA PHILOSOPHIE : pas un fondu de defilement —
       le bloc de droite GLISSE vers la gauche, celui de gauche vers la
       droite, et ils passent REELLEMENT derriere le buste (le calque est
       sous la vitre) en s'eteignant. Disparition totale avant la fin de
       la scene. */
    .station-intro .cale-corps {
      animation: sq-croiser-d linear both;
      animation-timeline: --sequence;
      animation-range: contain 25% contain 100%;
    }
    .station-intro .cale-appui {
      animation: sq-croiser-g linear both;
      animation-timeline: --sequence;
      animation-range: contain 25% contain 100%;
    }

    /* LE FLUX A DEUX VITESSES : chaque card lit SA traversee de l'ecran
       (view-timeline) et prend de l'avance sur la page ; le texte traine
       legerement en sens inverse. Les deux couches ne defilent pas a la
       meme vitesse — c'est la profondeur qu'on sent sous la molette, et
       Lenis lisse le tout puisque les timelines lisent son defilement. */
    .prestations-flux .station-carte {
      animation: flux-carte linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }


    /* L'ALTERNANCE : une station sur deux pose sa card a DROITE, le PIN
       de texte a gauche — le zigzag qui evite la colonne monotone. */
    .prestations-flux .pin { grid-column: 3; grid-row: 1 / span 3; }
    .prestations-flux .station:nth-of-type(even) .station-carte { grid-column: 3; justify-self: end; }
    .prestations-flux .station:nth-of-type(even) .pin { grid-column: 1; }

    /* LES TEXTES EPINGLES (demande de Quentin) : le texte ne defile pas —
       il S'EPINGLE au tiers haut pendant que sa station traverse (et que
       la card, elle, defile a travers lui), puis SORT SUR LE COTE — a
       droite quand il vit a droite, a gauche quand il vit a gauche — pour
       laisser place au suivant. La station est haute (144vh) : c'est elle
       qui donne le temps d'epinglage. */
    /* UNE SEULE RANGEE, haute comme la station : c'est elle le conteneur
       du sticky — sans ca, la zone de grille fait la taille du texte et le
       pin n'a aucune marge pour coller */
    /* .qui en prefixe : la regle de BASE du flux vient plus loin dans le
       fichier et gagnerait a specificite egale — le min-height sautait,
       et le pin n'avait aucune course pour coller */
    .qui .prestations-flux .station {
      min-height: 144vh;
      view-timeline-name: --station;
      grid-template-rows: 1fr;
      align-content: stretch;
    }
    .prestations-flux .station-carte { grid-row: 1; align-self: center; }
    .prestations-flux .pin { grid-row: 1; }
    .prestations-flux .pin {
      position: sticky; top: 21vh;
      align-self: start;
      animation: flux-pin-droite linear both;
      animation-timeline: --station;
      animation-range: cover 0% cover 100%;
    }
    .prestations-flux .station:nth-of-type(even) .pin { animation-name: flux-pin-gauche; }

    /* LA STATION FINALE GARDE SA COMPOSITION (card a gauche, texte a
       droite) meme quand la fusion des sections la fait passer au rang
       pair : la main, le blob et le texte ont ete regles pour elle.
       Meme specificite que les regles d'alternance — ces lignes viennent
       APRES, elles gagnent. */
    .prestations-flux .station.station-fin .station-carte { grid-column: 1; justify-self: start; }
    .prestations-flux .station.station-fin .pin { grid-column: 3; animation-name: flux-pin-droite; }

    /* LA SORTIE CROISEE DE LA PHILOSOPHIE (demande de Quentin) : en
       quittant la section, le bloc de droite glisse vers la GAUCHE et
       celui de gauche vers la DROITE — ils se referment sur le buste
       pendant que la station s'efface. La propriete `translate` compose
       avec les transforms existants, rien ne se marche dessus. */
    /* .station.station-intro : DEUX classes — la specificite bat la
       regle generique .station. La philosophie parait avec le buste puis
       RESTE : elle quitte l'ecran avec le banner, en defilant. */
    .station.station-intro {
      animation-name: sq-relever-final;
      animation-range: contain 25% contain 100%;
    }

    /* LE WEBDESIGN ARRIVE PAR LE BAS : la card et le texte montent
       ensemble pendant que la station s'allume. */

  }
}


/* -- SORTIE DU BANNER (demande de Quentin).
      Le titre sur deux lignes cede tot dans la course : « decouvrir » part
      vers la droite, « le studio » vers la gauche. L'accroche et les textes
      de la philosophie gardent leurs propres fenetres d'apparition. -- */
.banniere-scroll {
  animation: sq-ceder linear both;
  animation-timeline: --sequence;
  animation-range: contain 0% contain 6%;
}
.banniere-para {
  animation: sq-ceder linear both;
  animation-timeline: --sequence;
  animation-range: contain 2% contain 8%;
}
.banniere-titre .bt-1,
.banniere-titre .bt-2 {
  animation-timing-function: linear;
  animation-fill-mode: both;
  animation-timeline: --sequence;
  animation-range: contain 2% contain 8%;
}
.banniere-titre .bt-1 {
  translate: -1em 0;
  animation-name: sq-titre-droite;
}
.banniere-titre .bt-2 {
  translate: 1em 0;
  animation-name: sq-titre-gauche;
}

@keyframes sq-assombrir { to { opacity: 1; } }
@keyframes sq-ceder { to { opacity: 0; } }
@keyframes sq-titre-droite {
  0%   { translate: -1em 0; opacity: 1; }
  100% { translate: calc(-1em + 8vw) 0; opacity: 0; }
}
@keyframes sq-titre-gauche {
  0%   { translate: 1em 0; opacity: 1; }
  100% { translate: calc(1em - 8vw) 0; opacity: 0; }
}
/* la philosophie se referme sur le buste : glissement + extinction */
@keyframes sq-croiser-d {
  0%, 80% { translate: 0 0; opacity: 1; }
  100%    { translate: calc(-1 * var(--l-322)) 0; opacity: 0; }
}
@keyframes sq-croiser-g {
  0%, 80% { translate: 0 0; opacity: 1; }
  100%    { translate: var(--l-322) 0; opacity: 0; }
}
/* la card defile et mene de 144 px : c'est elle qui traverse le texte
   epingle — le contraste des deux mondes */
@keyframes flux-carte { from { translate: 0 var(--f-144); } to { translate: 0 calc(-1 * var(--f-144)); } }
/* LE TEXTE NE MONTE JAMAIS AVEC LA PAGE (demande de Quentin) : il reste
   MASQUE (clip integral) pendant que son pin voyage vers le point de
   collage — atteint vers 30 % de la traversee — puis, une fois immobile,
   le defilement le REVELE sur place par un balayage gauche -> droite.
   Il tient, et sort ensuite sur son cote, comme avant. */
@keyframes flux-pin-droite {
  0%, 30% { clip-path: inset(0 100% 0 0); translate: 0 0; opacity: 1; }
  46%     { clip-path: inset(0 0 0 0); }
  60%     { clip-path: inset(0 0 0 0); translate: 0 0; opacity: 1; }
  78%     { translate: var(--l-322) 0; opacity: 0; }
  100%    { translate: var(--l-322) 0; opacity: 0; }
}
@keyframes flux-pin-gauche {
  0%, 30% { clip-path: inset(0 100% 0 0); translate: 0 0; opacity: 1; }
  46%     { clip-path: inset(0 0 0 0); }
  60%     { clip-path: inset(0 0 0 0); translate: 0 0; opacity: 1; }
  78%     { translate: calc(-1 * var(--l-322)) 0; opacity: 0; }
  100%    { translate: calc(-1 * var(--l-322)) 0; opacity: 0; }
}

/* -- seuls les ronds du repli sans WebGPU cedent : l'eau, elle, reste —
      le buste demeure et le cadre pousse derriere lui (cadreF) -- */
.banniere-cercle {
  animation: sq-eau-ceder linear both;
  animation-timeline: --sequence;
  animation-range: contain 19% contain 27%;
}
@keyframes sq-eau-ceder { to { opacity: 0; } }

/* ---- LE CALQUE COLLE DES STATIONS ---- */
.surbanner {
  position: sticky; top: 0; height: 100vh;
  margin-top: -100vh;
  /* SOUS la vitre (z 3) : les textes de la philosophie sont occultes par
     le buste — pour de vrai — quand ils glissent vers lui a la sortie */
  z-index: 2;
  pointer-events: none;
}

.station {
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 38vw) 1fr minmax(0, var(--l-521));
  align-content: center;
  padding: 0 7vw;
  opacity: 0;
  animation: sq-relever linear both;
  animation-timeline: --sequence;
}

/* cinq stations depuis 30% : un cran de 14% chacune. La derniere porte
   .station-fin — elle reste posee, la sequence se termine sur elle. */
.s1 { animation-range: contain 30% contain 44%; }
.s2 { animation-range: contain 44% contain 58%; }
.s3 { animation-range: contain 58% contain 72%; }
.s4 { animation-range: contain 72% contain 86%; }
.s5 { animation-range: contain 86% contain 100%; }
.station-fin { animation-name: sq-relever-final; }
/* plein pendant le palier, efface seulement pendant le cran */
@keyframes sq-relever {
  0%   { opacity: 0; transform: translateY(var(--f-13)); }
  14%  { opacity: 1; transform: translateY(0); }
  86%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(calc(-1 * var(--f-13))); }
}
/* la derniere reste posee : la sequence se termine sur elle */
@keyframes sq-relever-final {
  0%   { opacity: 0; transform: translateY(var(--f-13)); }
  14%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 1; transform: translateY(0); }
}

/* LES CALES : hautes comme la card. Le titre part de son bord HAUT a
   gauche, le passage se pose sur son bord BAS a droite — la diagonale
   qui tend la composition, sans un trait pour la souligner. */
.cale { display: flex; flex-direction: column; }
.cale-titre { grid-column: 3; }
.cale-corps { grid-column: 3; margin-top: var(--f-34); }
/* ---- L'INTRO SUR LA GRILLE NATIVE DU .STATION ----
   La philosophie occupe les DEUX flancs de la statue :
   – cale-appui (gauche, col 1) : titre + deux premiers §
   – cale-corps (droite, col 3) : troisième §, après le buste

   ALIGNEMENT GAUCHE :
   .minimal-story-paragraph est à 14.5rem du viewport
   (2.5rem de padding .minimal-story + 12rem de padding-left .minimal-story-copy).
   .station a padding: 0 7vw → décalage interne = 14.5rem − 7vw */
.station-intro { grid-template-columns: minmax(0, 38vw) 1fr minmax(0, var(--l-521)); column-gap: 0; }

/* FLANC GAUCHE : commence exactement sur le même axe que .minimal-story-paragraph */
.station-intro .cale-appui {
  grid-column: 1;
  margin-left: calc(14.5rem - 7vw);
  margin-top: 0;
  transform: none;
  gap: 1.5rem;
  align-self: center;
  max-width: 30ch;
}

/* FLANC DROIT : H1 gros + paragraphe, ancré en haut de la section */
.station-intro .cale-corps {
  grid-column: 3;
  margin-top: 0;
  padding-top: 3rem;   /* ancre le H1 près du haut de la station */
  transform: none;
  gap: 2rem;
  align-self: start;   /* colle au bord haut, pas centré */
  max-width: var(--l-521);
}

/* LE H1 EN HAUT À DROITE */
.station-intro-titre {
  margin: 0;
  max-width: 18ch;
  --philo-title-y: 0em;
  transform: translate(2rem, calc(-1.75rem + var(--philo-title-y)));
  will-change: transform, opacity;
  font-size: clamp(1.2rem, 2.2vw, 2.2rem);
  font-weight: var(--fw-medium);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: #ffffff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.4);
}



.station-sur {
  display: flex;
  align-items: center;
  gap: 0.5em;
  margin: 0;
  color: #9a9a9a;
  font-size: 0.8rem;
  line-height: var(--lh-body);
  font-weight: 300;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.station-sur::before {
  content: "▸";
  margin-right: 0.5em;
  font-size: 0.7em;
}
.station-titre {
  margin: 0; max-width: var(--l-521);
  text-align: left;
  font-size: min(2.9vw, var(--l-47)); line-height: 1.15;
  font-weight: var(--fw-extralight); letter-spacing: var(--ls-tight);
  color: #FFFFFF;
  text-shadow: 0 1px 13px rgba(15, 19, 26, 0.34);
  text-box: trim-both cap alphabetic;
}
.station-role {
  margin: var(--f-21) 0 0;
  font-size: var(--text-caption); line-height: var(--lh-caption);
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.station-texte {
  margin: 0; max-width: var(--f-377);
  color: rgba(255, 255, 255, 0.89);
  text-shadow: 0 1px 8px rgba(15, 19, 26, 0.21);
  text-box: trim-both cap alphabetic;
}
.station-intro .station-texte {
  max-width: 30ch;
  font-size: 0.9rem;
  line-height: 1.6;
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-normal);
}

/* Le troisième paragraphe de la philosophie reprend exactement la distance
   entre le bord gauche de la fenêtre et le paragraphe SEO/GEO. La formule
   reprend l'axe de la seconde colonne de .minimal-story-bottom. */
@media (min-width: 988px) {
  .station-intro .cale-corps > p.station-texte {
    margin-left: calc(521px + 13.5rem - 43vw);
  }
}

/* Apparition ligne par ligne : chaque ligne est masquée dans sa propre
   fenêtre et son contenu arrive depuis le néant. Le script ne modifie que
   transform et opacity pendant le défilement. */
.philo-line {
  display: block;
  overflow: hidden;
}
.philo-line-inner {
  display: block;
  opacity: 0;
  transform: translate3d(0, 1.1em, 0);
  will-change: transform, opacity;
}
.station-liste {
  margin: var(--f-21) 0 0; padding: 0; list-style: none;
  font-size: var(--text-sm); line-height: var(--lh-sm);
  color: rgba(255, 255, 255, 0.78);
}
.station-liste li { padding-left: var(--f-13); text-indent: calc(-1 * var(--f-13)); }
.station-liste li::before { content: "\2014\00a0"; }

/* les images de repli ne vivent qu'en repli */

/* ---- LES CARDS PHOTO : une par station, classiques ----
   La card vit DANS sa station : elle parait et s'efface avec son texte
   (meme animation, aucun mecanisme de plus). Paysage — le format d'un
   ecran — pour la creation de site et l'assistant IA ; portrait pour la
   video, la photo, le SEO et l'hebergement. Leger cadre clair, angles
   vifs : la card classique du site. */
.station-carte {
  grid-column: 1;
  grid-row: 1 / span 3;
  align-self: center;
  justify-self: start;
  margin: 0;
  overflow: hidden;
  border: var(--border-hair) solid rgba(255, 255, 255, 0.62);
  border-radius: var(--radius-xs);
  background: var(--color-line);
}
.station-carte img { width: 100%; height: 100%; object-fit: cover; display: block; }
.station-carte.paysage { width: min(38vw, var(--l-843)); aspect-ratio: 16 / 10; }
.station-carte.portrait { width: min(23vw, var(--f-377)); aspect-ratio: 4 / 5; }

/* ---- LE FLUX CLASSIQUE DES PRESTATIONS : apres la scene, les cinq
   stations DEROULENT. Meme grille — card a gauche, texte a droite, le
   vide au centre — mais posee dans le flux, sans une seule animation :
   c'est le defilement qui fait tout. Texte encre sur craie. ---- */
.prestations-flux .station {
  position: static; inset: auto;
  opacity: 1; animation: none;
  min-height: 76vh;
  align-content: center;
  padding: var(--f-89) 7vw;
}
/* sur l'aurore, les stations gardent leurs habits blancs de base ;
   seul le bord des cards s'assoit un peu pour tenir sur les zones claires */
.prestations-flux .station-carte { border-color: rgba(255, 255, 255, 0.62); }
/* la securite d'empilement : tout le flux vit sous la vitre (z 3), meme
   une fois les cards transformees par la parallaxe */
.prestations-flux { position: relative; z-index: 0; }
/* les cards debordent vers le CENTRE : leur bord interieur croise le mesh
   pendant la traversee — on les voit passer derriere lui, franchement */
.prestations-flux .station-carte.paysage { width: min(46vw, var(--l-843)); }
.prestations-flux .station-carte.portrait { width: min(30vw, var(--l-521)); }

/* ---- LES REPLIS : la scene redevient une page posee. Trois portes
   (sans timelines, mouvement reduit, mobile), le meme habit. ---- */
@supports not (animation-timeline: view()) {
  .surbanner { position: static; height: auto; margin-top: 0; pointer-events: auto; }
  .station { position: static; opacity: 1; animation: none; grid-template-columns: 1fr; row-gap: var(--f-21); padding: var(--f-55) var(--f-34); align-items: start; }
  .cale { height: auto; align-items: flex-start; justify-content: flex-start; }
  .station-sur { color: var(--logo-encre); }
  .station-titre { text-align: left; font-size: var(--text-h3); line-height: var(--lh-h3); color: var(--logo-encre); text-shadow: none; }
  .station-role { color: var(--color-brand-studio); }
  .station-texte { color: var(--color-brand-studio); text-shadow: none; max-width: var(--l-521); }
  .station-liste { color: var(--color-brand-studio); }
  .station-carte, .station-carte.paysage, .station-carte.portrait { width: 100%; max-width: var(--l-322); aspect-ratio: auto; grid-row: auto; border-color: var(--color-line); }
}
@media (prefers-reduced-motion: reduce) {
  .surbanner { position: static; height: auto; margin-top: 0; pointer-events: auto; }
  .station { position: static; opacity: 1 !important; animation: none !important; grid-template-columns: 1fr; row-gap: var(--f-21); padding: var(--f-55) var(--f-34); align-items: start; }
  .cale { height: auto; align-items: flex-start; justify-content: flex-start; }
  .station-sur { color: var(--logo-encre); }
  .station-titre { text-align: left; font-size: var(--text-h3); line-height: var(--lh-h3); color: var(--logo-encre); text-shadow: none; }
  .station-role { color: var(--color-brand-studio); }
  .station-texte { color: var(--color-brand-studio); text-shadow: none; max-width: var(--l-521); }
  .station-liste { color: var(--color-brand-studio); }
  .station-carte, .station-carte.paysage, .station-carte.portrait { width: 100%; max-width: var(--l-322); aspect-ratio: auto; grid-row: auto; border-color: var(--color-line); }
}
@media (max-width: 987px) {
  .surbanner { position: static; height: auto; margin-top: 0; pointer-events: auto; }
  .station { position: static; opacity: 1; animation: none; grid-template-columns: 1fr; row-gap: var(--f-21); padding: var(--f-55) var(--f-21); align-items: start; }
  .cale { height: auto; align-items: flex-start; justify-content: flex-start; }
  .station-sur { color: var(--logo-encre); }
  .station-titre { text-align: left; font-size: var(--text-h3); line-height: var(--lh-h3); color: var(--logo-encre); text-shadow: none; }
  .station-role { color: var(--color-brand-studio); }
  .station-texte { color: var(--color-brand-studio); text-shadow: none; max-width: var(--l-521); }
  .station-liste { color: var(--color-brand-studio); }
  .station-carte, .station-carte.paysage, .station-carte.portrait { width: 100%; max-width: var(--l-322); aspect-ratio: auto; grid-row: auto; border-color: var(--color-line); }
}
