/* =========================================================================
   ALTIPUR — hero : préchargeur, canvas, cues et mobilier

   Le film est le sujet, le mobilier est son cadre. Quatre éléments visibles
   au maximum, et rien du tout pendant les trois séquences de traitement.
   ========================================================================= */

:root {
  /* La colonne de gauche : cues, indicateur d'acte et logotype s'y alignent.
     Le max() empêche la gouttière de se réduire à rien sur un petit écran. */
  --colonne: max(1.25rem, 8vw);
}

/* =========================================================================
   1. LE PRÉCHARGEUR
   ========================================================================= */

.loader {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  place-content: center;
  gap: clamp(1.5rem, 4vh, 2.75rem);
  gap: clamp(1.5rem, 4svh, 2.75rem);
  background: var(--fond);
  text-align: center;
  /* 900 ms exactement : js/hero.js pose `loader.hidden = true` à 900 ms et
     ce fichier-là ne bouge pas. Les deux valeurs doivent rester égales. */
  transition:
    opacity var(--d-reveal) var(--ease-expressive),
    visibility var(--d-reveal) var(--ease-expressive);
}

.loader.est-parti {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.loader__mot {
  position: relative;
  justify-self: center;
  font-family: var(--logotype);
  font-size: clamp(2rem, 7vw, 5rem);
  letter-spacing: 0.3em;
  text-indent: 0.3em; /* compense la chasse ajoutée après la dernière lettre */
  line-height: 1;
  text-transform: uppercase;
}

.loader__couche {
  display: block;
  color: var(--creme);
  white-space: nowrap;
}

/* La couche or se dévoile de gauche à droite au rythme du préchargement
   réel. Un seul clip-path piloté depuis majLoader(), aucun JS par lettre. */
.loader__couche--or {
  position: absolute;
  inset: 0;
  color: var(--or);
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--d-state) linear;
}

.loader__piste {
  width: 240px;
  max-width: 60vw;
  height: 1px;
  margin-inline: auto;
  background: rgba(200, 169, 107, 0.2);
  overflow: hidden;
}

.loader__barre {
  width: 100%;
  height: 100%;
  background: var(--or);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-state) linear;
}

.loader__chiffre {
  font-family: var(--mono);
  /* 12 px dès qu'il y a la place ; en dessous la ligne passerait à deux. */
  font-size: clamp(10px, 2.9vw, 12px);
  letter-spacing: 0.18em;
  color: var(--gris);
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

/* Le mot quitte le préchargeur pour rejoindre sa position de logotype.
   Il est sorti du .loader pour survivre au fondu de celui-ci : pas de
   disparition puis réapparition, une seule animation continue. */
.loader__mot--vol {
  z-index: 130;
  pointer-events: none;
}

/* =========================================================================
   2. LE HERO
   ========================================================================= */

.hero {
  position: relative;
  z-index: 2;
  height: 100svh;
  overflow: hidden;
  background: var(--fond);
}

.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  will-change: transform; /* seul élément animé en continu avec le marquee */
}

/* Vignettage : le texte crème doit tenir sur un ciel clair. */
.hero__voile {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      to top,
      rgba(8, 12, 20, 0.9) 0%,
      rgba(8, 12, 20, 0.78) 26%,
      rgba(8, 12, 20, 0) 62%
    ),
    linear-gradient(to bottom, rgba(8, 12, 20, 0.72) 0%, rgba(8, 12, 20, 0) 30%),
    radial-gradient(120% 90% at 50% 50%, rgba(8, 12, 20, 0) 42%, rgba(8, 12, 20, 0.5) 100%);
}

/* =========================================================================
   3. LES CUES NARRATIFS
   Alignés à gauche sur la colonne, jamais centrés : c'est ce qui distingue
   une composition d'un gabarit.
   ========================================================================= */

.hero__scene {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.cue {
  position: absolute;
  z-index: 2; /* au-dessus du voile permanent de la colonne */
  left: var(--colonne);
  right: var(--colonne);
  bottom: clamp(5.5rem, 16vh, 10rem);
  bottom: clamp(5.5rem, 16svh, 10rem);
  opacity: 0;
  visibility: hidden;
  /* Sortie : fondu et remontée de 20 px. L'entrée, elle, est portée par le
     line-reveal des lignes, pas par ce déplacement. */
  transform: translate3d(0, -20px, 0);
  /* Sortie : 0,75 × l'entrée, sur une courbe qui accélère. C'est la règle
     d'asymétrie du site — la transition de sortie vit sur la règle de base,
     celle d'entrée sur la règle d'état. */
  transition:
    opacity calc(var(--d-state) * 0.75) var(--ease-sortie),
    transform calc(var(--d-state) * 0.75) var(--ease-sortie),
    visibility calc(var(--d-state) * 0.75) var(--ease-sortie);
}

.cue.est-actif {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity var(--d-state) var(--ease-expressive),
    transform var(--d-state) var(--ease-expressive),
    visibility var(--d-state) var(--ease-expressive);
}

/* LE VOILE PERMANENT DE LA COLONNE DE TEXTE.

   Mesure faite frame par frame, une sur dix, sur les 363 frames des deux
   jeux : luminance moyenne relevée SOUS la boîte du cue, pas sur la frame
   entière, et pas seulement sur la première — la séquence va du canyon de
   nuages sombre au ciel clair du final.

     · pire frame  : f_281, crème #F2F0EB à 1,42:1 en desktop, 1,33:1 en mobile
     · 18 frames sur 37 échantillonnées passent sous 4,5:1 sans voile
     · alpha minimal pour tenir 4,5:1 partout : 0,489 (desktop) / 0,505 (mobile)
     · alpha minimal pour tenir 7:1, niveau AAA : 0,640 / 0,652

   Ce voile-ci est PERMANENT et non plus accroché au cue : accroché au cue, il
   partageait son opacité, et pendant le fondu le texte se retrouvait à moitié
   posé sur un voile à moitié absent. Il est plus clair que l'ancien (0,72 au
   lieu de 0,90 au centre) : composé avec `.hero__voile`, qui vaut déjà 0,30 à
   0,84 dans cette bande, le total dépasse 0,80 au centre du texte, donc au-delà
   du seuil AAA. Le hero est donc à la fois conforme et plus clair qu'avant.

   Les rayons restent sous 50 % de la boîte : au-delà, le dégradé est encore
   opaque quand il atteint le bord et laisse une arête franche en travers de
   l'image. */
.hero__voile-texte {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: 0;
  height: min(52vh, 34rem);
  height: min(52svh, 34rem);
  pointer-events: none;
  background: radial-gradient(
    58% 62% at 26% 62%,
    rgba(8, 12, 20, 0.72) 0%,
    rgba(8, 12, 20, 0.52) 48%,
    rgba(8, 12, 20, 0) 100%
  );
}

.cue__display {
  font-family: var(--display);
  font-size: var(--t-xl);
  line-height: 0.98;
  letter-spacing: var(--tr-xl);
  text-transform: none;
  color: var(--creme);
  max-width: 20ch;
  text-wrap: balance;
  text-shadow: 0 2px 40px rgba(8, 12, 20, 0.75);
}

/* Line-reveal masqué : overflow hidden, y 100% → 0, stagger 70 ms. */

.ligne {
  display: block;
  overflow: hidden;
  padding-bottom: 0.06em; /* les jambages ne doivent pas être rognés */
}

.ligne__i {
  display: block;
  transform: translate3d(0, 105%, 0);
  transition: transform var(--d-reveal) var(--ease-expressive);
}

.cue.est-actif .ligne__i {
  transform: none;
}

.ligne:nth-child(2) .ligne__i {
  transition-delay: var(--stagger);
}
.ligne:nth-child(3) .ligne__i {
  transition-delay: calc(var(--stagger) * 2);
}

.cue__mono {
  margin-top: clamp(0.9rem, 2.2vh, 1.6rem);
  margin-top: clamp(0.9rem, 2.2svh, 1.6rem);
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--or-lien);
  opacity: 0;
  transition: opacity var(--d-reveal) var(--ease-expressive) var(--d-state);
}

.cue.est-actif .cue__mono {
  opacity: 1;
}

/* =========================================================================
   4. LE MOBILIER
   Couche ornementale posée sur le canvas. En absolu dans le hero et non en
   fixed : pendant le pin le hero est lui-même fixe, le rendu est identique,
   et une fois le pin relâché le mobilier s'en va avec lui au lieu de rester
   collé à l'écran.
   ========================================================================= */

.hero__ui {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

/* Chaque élément de mobilier partage la même mécanique d'apparition. */
[data-mob],
.marque {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity calc(var(--d-state) * 0.75) var(--ease-sortie),
    visibility calc(var(--d-state) * 0.75) var(--ease-sortie);
}

[data-mob].est-visible,
.marque.est-visible {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--d-state) var(--ease-expressive),
    visibility var(--d-state) var(--ease-expressive);
}

/* --- 4.1 Logotype, haut gauche ------------------------------------------ */

.marque {
  position: absolute;
  top: clamp(1.4rem, 4vh, 2.4rem);
  top: clamp(1.4rem, 4svh, 2.4rem);
  left: var(--colonne);
  font-family: var(--logotype);
  font-size: 18px;
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  color: var(--creme);
  text-transform: uppercase;
  line-height: 1;
}

/* --- 4.2 Altimètre, rail droit ------------------------------------------ */

.altimetre {
  position: absolute;
  right: clamp(1.1rem, 2.8vw, 2.4rem);
  top: 50%;
  translate: 0 -50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.8rem, 2vh, 1.4rem);
  gap: clamp(0.8rem, 2svh, 1.4rem);
}

.altimetre__lecture {
  writing-mode: vertical-rl;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gris);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.altimetre__lecture b {
  font-weight: 500;
  color: var(--or-lien);
}

.altimetre__piste {
  width: 1px;
  height: 180px;
  background: rgba(200, 169, 107, 0.2);
  overflow: hidden;
}

/* Le JS ne pose qu'une variable : c'est la CSS qui décide de l'axe, vertical
   sur le rail de droite, horizontal quand l'altimètre repasse en bas. */
.altimetre__jauge {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--or);
  transform: scaleY(var(--p, 0));
  transform-origin: top;
}

/* --- 4.3 Indicateur d'acte, bas gauche ---------------------------------- */

.acte {
  position: absolute;
  left: var(--colonne);
  bottom: clamp(1.6rem, 4.5vh, 2.8rem);
  bottom: clamp(1.6rem, 4.5svh, 2.8rem);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gris);
  font-variant-numeric: tabular-nums;
}

.acte__masque {
  display: block;
  overflow: hidden;
  padding-bottom: 0.25em;
}

.acte__i {
  display: block;
  white-space: nowrap;
}

/* --- 4.4 Le grand mot, entrée et sortie --------------------------------- */

.grandmot {
  position: absolute;
  left: var(--colonne);
  right: var(--colonne);
  top: 50%;
  translate: 0 -50%;
  text-align: center;
}

.grandmot__mot {
  display: flex;
  justify-content: center;
  gap: 0.02em;
  font-family: var(--logotype);
  font-size: clamp(4rem, 16vw, 14rem);
  line-height: 0.86;
  text-transform: uppercase;
}

.grandmot__m {
  display: block;
  overflow: hidden;
  padding-bottom: 0.1em; /* la dislocation ne doit pas rogner les jambages */
}

/* Contour or qui se remplit de gauche à droite. Le remplissage est une
   variable CSS animée par GSAP : une seule propriété, pas de calque en plus. */
.grandmot__l {
  display: block;
  color: transparent;
  -webkit-text-stroke: 1px var(--or);
  background-image: linear-gradient(to right, var(--or), var(--or));
  background-repeat: no-repeat;
  background-size: var(--remplissage, 0%) 100%;
  -webkit-background-clip: text;
  background-clip: text;
}

.grandmot--plein .grandmot__l {
  -webkit-text-stroke: 0;
  color: var(--or);
  background-image: none;
}

.grandmot__sous {
  margin-top: clamp(0.9rem, 2.4vh, 1.8rem);
  margin-top: clamp(0.9rem, 2.4svh, 1.8rem);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: var(--gris);
}

/* --- 4.5 Indicateur de scroll, bas centré ------------------------------- */

.invite {
  position: absolute;
  left: 50%;
  bottom: clamp(1.6rem, 4.5vh, 2.8rem);
  bottom: clamp(1.6rem, 4.5svh, 2.8rem);
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: 0.75rem;
}

.invite__mot {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: var(--gris);
}

.invite__piste {
  position: relative;
  display: block;
  width: 1px;
  height: 48px;
  background: rgba(242, 240, 235, 0.15);
  overflow: hidden;
}

.invite__seg {
  position: absolute;
  inset: 0 0 auto 0;
  display: block;
  height: 16px;
  background: var(--or);
  /* Boucle infinie : c'est l'une des deux seules durées littérales
     autorisées du dossier, avec le badge rotatif. */
  animation: descente 1.8s var(--ease-expressive) infinite;
}

@keyframes descente {
  from {
    transform: translateY(-16px);
  }
  to {
    transform: translateY(48px);
  }
}

/* --- 4.6 Le bouton devis ------------------------------------------------ */

.devis-rond {
  position: absolute;
  z-index: 4;
  left: 50%;
  top: calc(50% + clamp(7rem, 17vw, 15rem));
  translate: -50% 0;
  width: 132px;
  height: 132px;
  display: grid;
  place-items: center;
  color: var(--or);
  pointer-events: auto;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity calc(var(--d-state) * 0.75) var(--ease-sortie),
    visibility calc(var(--d-state) * 0.75) var(--ease-sortie);
}

.devis-rond.est-visible {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--d-state) var(--ease-expressive),
    visibility var(--d-state) var(--ease-expressive);
}

.devis-rond svg {
  position: absolute;
  inset: 0;
  rotate: -90deg; /* le trait part du haut */
}

/* Circonférence de 2π × 65,5 ≈ 412 : le trait se dessine en 0,9 s. */
.devis-rond__trait {
  stroke-dasharray: 412;
  stroke-dashoffset: 412;
  transition: stroke-dashoffset var(--d-reveal) var(--ease-expressive);
}

.devis-rond.est-visible .devis-rond__trait {
  stroke-dashoffset: 0;
}

.devis-rond span {
  position: relative;
  max-width: 9ch;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--or-lien);
  text-align: center;
  line-height: 1.6;
}

.devis-rond:hover span,
.devis-rond:focus-visible span {
  color: var(--creme);
}

/* =========================================================================
   5. PORTE DE SORTIE : MOUVEMENT RÉDUIT
   ========================================================================= */

.hero--statique {
  height: auto;
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: clamp(6rem, 18vh, 12rem);
  padding-block: clamp(6rem, 18svh, 12rem);
}

.hero--statique .hero__canvas {
  position: absolute;
}

/* En mode statique tout le texte du hero est empilé sur une seule image :
   le dégradé du bas ne couvre plus rien. On passe à un voile plat. */
.hero--statique .hero__voile {
  background: rgba(8, 12, 20, 0.74);
}

/* En mode statique, le voile plat ci-dessus couvre déjà toute l'image et
   le texte est empilé sur toute la hauteur : le voile de colonne ne ferait
   plus qu'une tache sombre au milieu d'un bloc déjà lisible. */
.hero--statique .hero__voile-texte {
  display: none;
}

.hero--statique .hero__scene {
  position: relative;
  display: grid;
  gap: clamp(2rem, 6vh, 4rem);
  gap: clamp(2rem, 6svh, 4rem);
  padding-inline: var(--colonne);
}

.hero--statique .cue {
  position: relative;
  inset: auto;
  transform: none;
  text-align: left;
}

/* Sans scrub, il n'y a ni acte à annoncer ni invitation à défiler qui ait
   du sens : on garde le logotype, l'altimètre et le bouton devis. */
.hero--statique .invite,
.hero--statique .acte,
.hero--statique .grandmot {
  display: none;
}

.hero--statique .devis-rond {
  position: relative;
  top: auto;
  left: auto;
  translate: none;
  margin: clamp(2.5rem, 7vh, 4rem) auto 0;
  margin: clamp(2.5rem, 7svh, 4rem) auto 0;
}

@media (prefers-reduced-motion: reduce) {
  .invite__seg {
    animation: none;
    transform: translateY(16px);
  }

  .cue {
    transform: none;
    transition:
      opacity var(--d-state) linear,
      visibility var(--d-state) linear;
  }

  .ligne__i {
    transform: none;
    transition: none;
  }

  .devis-rond__trait {
    transition: none;
    stroke-dashoffset: 0;
  }
}

/* =========================================================================
   6. MOBILE
   L'altimètre repasse à l'horizontale en bas à droite, le badge disparaît
   (traité dans scenes.css), l'indicateur d'acte reste.
   ========================================================================= */

@media (max-width: 767px) {
  .cue {
    bottom: clamp(7rem, 22vh, 13rem);
    bottom: clamp(7rem, 22svh, 13rem);
  }

  .cue__display {
    max-width: 14ch;
  }

  /* Le logotype ferait doublon avec la marque de la pilule de nav, qui est
     déjà à l'écran en permanence sur cette largeur. */
  .marque {
    display: none;
  }

  .altimetre {
    right: var(--colonne);
    top: auto;
    bottom: clamp(1.6rem, 4.5vh, 2.8rem);
    bottom: clamp(1.6rem, 4.5svh, 2.8rem);
    translate: none;
    flex-direction: row;
    align-items: center;
    gap: 0.7rem;
  }

  .altimetre__lecture {
    writing-mode: horizontal-tb;
    letter-spacing: 0.16em;
  }

  .altimetre__piste {
    width: 64px;
    height: 1px;
  }

  .altimetre__jauge {
    transform: scaleX(var(--p, 0));
    transform-origin: left;
  }

  .acte {
    font-size: 10px;
    letter-spacing: 0.14em;
  }

  /* L'invite et l'altimètre se disputeraient le bas de l'écran. */
  .invite {
    bottom: clamp(4.5rem, 11vh, 6.5rem);
    bottom: clamp(4.5rem, 11svh, 6.5rem);
  }

  .grandmot__sous {
    font-size: 11px;
  }

  .devis-rond {
    width: 116px;
    height: 116px;
  }

  .devis-rond svg {
    width: 116px;
    height: 116px;
  }
}
