/* =========================================================
   NEXALY — Couche d'élévation visuelle (page d'accueil)

   Se superpose à styles.css sans le modifier. Chargée uniquement
   par index.html : 404.html et les pages légales partagent
   styles.css et ne sont pas affectées.

   Réutilise les tokens existants (--accent, --grad, --ease-out…).
   ========================================================= */

/* ---------------- Barre de progression de lecture ---------------- */

.scroll-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 120;
  pointer-events: none;
  background: var(--line-soft);
}

.scroll-progress span {
  display: block;
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--grad);
  box-shadow: 0 0 12px rgba(69, 224, 255, .55);
}

/* ---------------- Hero : trame WebGL ---------------- */

/* Trame ambiante : fixe, couvre le viewport, derrière tout le contenu.
   Elle accompagne le lecteur sur toute la page au lieu de s'arrêter au
   hero ; son intensité est modulée section par section dans le shader. */
.ambient {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s var(--ease-out);
}

/* hero-gl.js pose .has-gl une fois le contexte WebGL2 obtenu.
   Sans WebGL2, le canvas reste à 0 : les fonds CSS existants suffisent. */
.has-gl .ambient {
  opacity: 1;
}

/* Le contenu passe au-dessus de la couche ambiante. Le header porte
   déjà z-index 100 dans styles.css. */
main,
.footer {
  position: relative;
  z-index: 1;
}

/* La trame porte déjà le motif : on allège la grille CSS du hero. */
.has-gl .hero__grid {
  opacity: .35;
}

.has-gl .blob {
  filter: blur(70px);
  opacity: .55;
}

/* ---------------- Hero : typographie ---------------- */

.hero__title {
  font-size: clamp(2.6rem, 7.2vw, 5.2rem);
  letter-spacing: -.035em;
}

.hero__sub {
  max-width: 60ch;
}

/* Le dégradé de marque respire au lieu d'être figé */
.grad {
  background: var(--grad);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: gradSweep 9s var(--ease) infinite alternate;
}

@keyframes gradSweep {
  from { background-position:   0% 50%; }
  to   { background-position: 100% 50%; }
}

/* ---------------- Parallaxe : curseur + scroll ---------------- */

/* Deux sources de déplacement, deux variables : main.js écrit --px/--py
   depuis le curseur et --sy depuis le scroll, sans jamais se marcher
   dessus. C'est le CSS qui fait la somme, donc l'ordre d'écriture n'a
   aucune importance.

   La longhand `translate` est délibérée : les blobs animent déjà
   `transform` (drift + blobMorph), les deux se composent.

   Seules des couches décoratives (aria-hidden) portent ces attributs.
   Aucun bloc de texte n'est déplacé. */
[data-parallax],
[data-scroll-parallax] {
  translate: var(--px, 0px) calc(var(--py, 0px) + var(--sy, 0px));
}

[data-scroll-parallax] {
  will-change: transform, translate;
}

/* Couche la plus lointaine : l'aurore est en position fixe, elle ne
   défile pas. On la déplace donc sur la progression de lecture plutôt
   que sur la position d'un élément — d'où une variable à part. */
body::before {
  translate: 0 var(--sy-aurora, 0px);
}

/* ---------------- Cartes : élévation au survol ---------------- */

.card {
  transition:
    transform .5s var(--ease-out),
    border-color .5s var(--ease-out),
    box-shadow .5s var(--ease-out);
}

@media (hover: hover) {
  .card:hover {
    transform: translateY(-6px);
    border-color: rgba(69, 224, 255, .28);
    box-shadow: var(--shadow-md), 0 0 40px rgba(69, 224, 255, .10);
  }
}

/* Liseré dégradé qui apparaît au survol, sans décaler la mise en page */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--grad);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  transition: opacity .5s var(--ease-out);
  pointer-events: none;
}

@media (hover: hover) {
  .card:hover::after { opacity: .55; }
}

/* ---------------- Boutons magnétiques ---------------- */

/* main.js écrit la position cible sur la longhand `translate`, une fois
   par frame. C'est cette transition qui fait l'amortissement — courte
   pendant le survol pour rester collé au curseur, et le retour à zéro
   au pointerleave emprunte la même courbe. */
.btn--lg,
.nav__cta {
  transition:
    translate .35s var(--ease-out),
    background-color .3s var(--ease),
    border-color .3s var(--ease),
    box-shadow .3s var(--ease),
    color .3s var(--ease);
}

.btn--primary {
  position: relative;
  overflow: hidden;
}

/* Reflet qui balaie le bouton au survol */
.btn--primary::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    rgba(255, 255, 255, .28) 50%,
    transparent 80%
  );
  transform: translateX(-100%);
  transition: transform .8s var(--ease-out);
  pointer-events: none;
}

@media (hover: hover) {
  .btn--primary:hover::before { transform: translateX(100%); }
}

/* ---------------- Titres de section ---------------- */

.section__title {
  font-size: clamp(1.9rem, 4.4vw, 3.1rem);
  letter-spacing: -.03em;
}

/* ---------------- Transitions entre sections ---------------- */

/* Filet dégradé au-dessus de chaque section, discret. Il se trace depuis
   le centre quand la section prend le relais de la précédente : c'est la
   couture entre deux blocs, pas un effet d'entrée de plus. */
.section + .section {
  position: relative;
}

.section + .section::before {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(69, 224, 255, .22) 30%,
    rgba(124, 107, 255, .22) 70%,
    transparent
  );
  transform: scaleX(0);
  transform-origin: 50% 50%;
  transition: transform 1.2s var(--ease-out);
}

.section + .section.is-lit::before {
  transform: scaleX(1);
}

/* Halo d'entrée : la section « s'allume » depuis son bord haut. Posé en
   z-index négatif comme .cta__aura et .waitlist__glow — derrière le
   contenu, jamais au-dessus. La mise en page n'est pas touchée. */
.section::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    56% 40% at 50% 0%,
    rgba(69, 224, 255, .07),
    transparent 72%
  );
  opacity: 0;
  transition: opacity 1.4s var(--ease-out);
}

.section.is-lit::after {
  opacity: 1;
}

/* ---------------- Révélations en cascade par groupe ---------------- */

/* Le décalage lui-même vient de styles.css (--d × 90 ms). Ce qui change
   ici, c'est que main.js recalcule --d au moment de la révélation, par
   vague : les membres d'un groupe qui franchissent le seuil ensemble se
   décalent les uns par rapport aux autres, et celui qui arrive seul —
   colonne étroite, mobile — repart de zéro au lieu d'hériter du rang
   qu'il occupe dans le HTML.

   Le geste reste vertical, comme le .reveal générique : un retrait
   d'échelle en plus, pour que la carte « arrive » au lieu de monter.
   L'état d'arrivée est l'état au repos — aucun décalage de mise en page. */
.cards > .reveal,
.scores > .reveal {
  transform: translateY(30px) scale(.985);
  transition-duration: .9s;
}

.cards > .reveal.is-visible,
.scores > .reveal.is-visible {
  transform: none;
}

/* Les étapes sont une colonne : le glissement est un peu plus ample,
   sans composante horizontale qui ferait ramer le texte latéralement. */
.steps > .reveal {
  transform: translateY(34px);
  transition-duration: .85s;
}

.steps > .reveal.is-visible {
  transform: none;
}

/* ---------------- Jauges de score ---------------- */

.score__bar span {
  transition: width 1.4s var(--ease-out) .2s;
  box-shadow: 0 0 14px rgba(69, 224, 255, .35);
}

/* ---------------- Respect de prefers-reduced-motion ---------------- */

/* ---------------- Couche aurora ---------------- */

/* Signature Aurora UI : maillage de dégradés iridescent en blend mode,
   posé sur le fond du hero. Pseudo-élément : aucun ajout de balise.
   Les blobs existants portent déjà des dérives lentes (22-30 s) ; cette
   couche ajoute le morphing rapide et la fusion des couleurs. */
body::before {
  content: "";
  position: fixed;
  inset: -20%;
  z-index: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: .55;
  filter: blur(60px);
  background:
    radial-gradient(38% 44% at 22% 28%, rgba(69, 224, 255, .40), transparent 70%),
    radial-gradient(34% 40% at 78% 22%, rgba(124, 107, 255, .38), transparent 70%),
    radial-gradient(32% 38% at 62% 74%, rgba(255, 107, 214, .28), transparent 70%),
    radial-gradient(30% 36% at 30% 78%, rgba(69, 224, 255, .22), transparent 70%);
  background-repeat: no-repeat;
  animation: auroraMorph 14s var(--ease) infinite alternate;
  will-change: transform, filter;
}

/* Déformation lente de l'ensemble du maillage : les masses se déplacent
   les unes par rapport aux autres, ce qui fait varier les zones de
   recouvrement — c'est ce recouvrement qui produit l'iridescence. */
@keyframes auroraMorph {
  0% {
    transform: translate3d(0, 0, 0) scale(1) rotate(0deg);
    filter: blur(60px) hue-rotate(0deg);
  }
  50% {
    transform: translate3d(2%, -3%, 0) scale(1.12) rotate(4deg);
    filter: blur(74px) hue-rotate(-8deg);
  }
  100% {
    transform: translate3d(-3%, 2%, 0) scale(1.06) rotate(-3deg);
    filter: blur(66px) hue-rotate(6deg);
  }
}

/* Morphing de forme des blobs : ils cessent d'être des cercles parfaits
   et respirent. `transform` seulement — la parallaxe de main.js écrit
   sur la longhand `translate`, les deux se composent sans conflit. */
.blob--1 { animation: drift1 22s var(--ease) infinite alternate, blobMorph 11s var(--ease) infinite alternate; }
.blob--2 { animation: drift2 26s var(--ease) infinite alternate, blobMorph 13s var(--ease) infinite alternate -3s; }
.blob--3 { animation: drift3 30s var(--ease) infinite alternate, blobMorph 15s var(--ease) infinite alternate -6s; }

@keyframes blobMorph {
  0%   { border-radius: 50% 50% 50% 50%; transform: scale(1)    rotate(0deg); }
  33%  { border-radius: 62% 38% 44% 56%; transform: scale(1.08) rotate(3deg); }
  66%  { border-radius: 40% 60% 58% 42%; transform: scale(.95)  rotate(-2deg); }
  100% { border-radius: 55% 45% 38% 62%; transform: scale(1.05) rotate(2deg); }
}

/* On supprime le MOUVEMENT, pas la richesse visuelle. Ce qui disparaît :
   les animations autonomes (dégradé qui balaie, reflet des boutons).
   Ce qui reste : la trame de points (figée par hero-gl.js), la barre de
   progression (elle suit le scroll, elle n'anime rien d'elle-même) et
   les états de survol, qui répondent à une action de l'utilisateur. */
@media (prefers-reduced-motion: reduce) {

  /* styles.css masque le canvas sous reduced-motion ; on le rétablit,
     hero-gl.js le rend désormais en une seule image fixe. */
  .ambient {
    display: block;
    opacity: 0;
  }

  .has-gl .ambient { opacity: 1; }

  /* Barre de progression conservée : pilotée par le scroll, sans
     transition propre, donc sans mouvement involontaire. */
  .scroll-progress { display: block; }
  .scroll-progress span { transition: none; }

  /* Animations autonomes coupées. La couche aurora reste VISIBLE mais
     figée sur sa première image : on garde la profondeur de couleur,
     on retire le mouvement. */
  body::before { animation: none; }
  .blob--1, .blob--2, .blob--3 { animation: none; }
  .grad { animation: none; }
  .btn--primary::before { display: none; }

  /* Survols conservés mais instantanés */
  .card,
  .card::after,
  .score__bar span { transition: none; }

  /* Parallaxe : aucune couche ne bouge, ni au curseur ni au scroll.
     main.js n'écrit déjà plus --px/--py/--sy dans ce mode ; la règle
     ci-dessous couvre le cas où la préférence change après le chargement,
     sans attendre un rechargement de la page. */
  [data-parallax],
  [data-scroll-parallax],
  body::before { translate: none; }

  [data-scroll-parallax] { will-change: auto; }

  /* Révélations : plus de retrait d'échelle ni de glissement, les
     éléments sont posés à leur place définitive. */
  .cards > .reveal,
  .scores > .reveal,
  .steps > .reveal { transform: none; transition: none; }

  /* Transitions de section : l'état allumé est l'état de départ. Le
     filet et le halo restent VISIBLES — on retire la mise en place
     progressive, pas la matière. main.js pose .is-lit d'emblée. */
  .section + .section::before {
    transform: none;
    transition: none;
  }

  .section::after { transition: none; }
}
