/* ==========================================================================
   VTJ — Couche d'animations
   --------------------------------------------------------------------------
   Fichier SÉPARÉ de style.css volontairement : si vous voulez un site plus
   sobre, il suffit de retirer la ligne <link> de ce fichier dans les pages,
   le site reste parfaitement fonctionnel et lisible.

   Sommaire :
   A. Réglages & préférences de mouvement
   B. Barre de progression de lecture
   C. Halo qui suit la souris
   D. Header intelligent (se cache / réapparaît)
   E. Fond animé du hero (aurores + grille + parallaxe)
   F. Titres : révélation mot à mot + mot rotatif
   G. Maquette navigateur animée
   H. Bandeau défilant (marquee)
   I. Cartes : inclinaison 3D + halo
   J. Boutons magnétiques + onde au clic
   K. Étapes : ligne de progression au scroll
   L. Compteurs, tableau de prix, listes en cascade
   M. Bouton retour en haut
   N. Variantes de révélation au scroll
   O. Transition entre les pages
   ========================================================================== */


/* ==========================================================================
   A. RÉGLAGES
   ========================================================================== */
:root {
  --anim-slow: 900ms;
  --anim-med: 600ms;
  --anim-fast: 300ms;
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Le JS ajoute .js-anim sur <html> : les effets ne s'activent que si le JS
   tourne, sinon tout reste visible et statique (aucun contenu masqué). */


/* ==========================================================================
   B. BARRE DE PROGRESSION DE LECTURE (injectée par le JS)
   ========================================================================== */
.scroll-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 200;
  background: linear-gradient(90deg, var(--color-accent), #7aa2ff, var(--color-accent));
  transform: scaleX(0);
  transform-origin: 0 50%;
  will-change: transform;
  pointer-events: none;
}


/* ==========================================================================
   C. HALO QUI SUIT LA SOURIS (souris précise uniquement, jamais sur mobile)
   ========================================================================== */
.cursor-glow {
  position: fixed;
  top: 0; left: 0;
  width: 420px; height: 420px;
  margin: -210px 0 0 -210px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(42, 98, 240, 0.14) 0%, rgba(42, 98, 240, 0) 65%);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  transition: opacity 400ms ease, width 300ms ease, height 300ms ease;
  will-change: transform;
}
.cursor-glow.is-active { opacity: 1; }
/* Le halo grossit au survol d'un élément cliquable */
.cursor-glow.is-hot { width: 620px; height: 620px; margin: -310px 0 0 -310px; }


/* ==========================================================================
   D. HEADER INTELLIGENT
   ========================================================================== */
.site-header {
  transition: box-shadow var(--transition), border-color var(--transition),
              transform 420ms var(--ease-out-expo), background-color var(--transition);
}
/* Descente : le header s'efface. Remontée : il revient. */
.site-header.is-hidden { transform: translateY(-100%); }
.site-header.is-scrolled::before { background: var(--color-header-bg-solid); }

/* Soulignement animé des liens de menu (desktop) */
@media (min-width: 1000px) {
  .nav__link { position: relative; overflow: hidden; }
  .nav__link::after {
    content: '';
    position: absolute; left: 0; bottom: 0;
    width: 100%; height: 2px;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: 100% 50%;
    transition: transform 420ms var(--ease-out-expo);
  }
  .nav__link:hover::after { transform: scaleX(1); transform-origin: 0 50%; }
  .nav__link.is-active::after { transform: scaleX(1); }
}

/* Entrée en cascade des liens du menu mobile */
@media (max-width: 999px) {
  .js-anim .nav .nav__list li,
  .js-anim .nav .nav__cta {
    opacity: 0;
    transform: translateX(26px);
    transition: opacity 420ms ease, transform 480ms var(--ease-out-expo);
  }
  .js-anim .nav.is-open .nav__list li,
  .js-anim .nav.is-open .nav__cta { opacity: 1; transform: none; }
  .js-anim .nav.is-open .nav__list li:nth-child(1) { transition-delay: 80ms; }
  .js-anim .nav.is-open .nav__list li:nth-child(2) { transition-delay: 130ms; }
  .js-anim .nav.is-open .nav__list li:nth-child(3) { transition-delay: 180ms; }
  .js-anim .nav.is-open .nav__list li:nth-child(4) { transition-delay: 230ms; }
  .js-anim .nav.is-open .nav__list li:nth-child(5) { transition-delay: 280ms; }
  .js-anim .nav.is-open .nav__cta { transition-delay: 340ms; }
}


/* ==========================================================================
   E. FOND ANIMÉ DU HERO
   ========================================================================== */
.hero__bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/* --- Aurores : trois taches de couleur qui dérivent lentement --- */
.aurora {
  position: absolute;
  border-radius: 50%;
  filter: blur(52px);
  opacity: 0.5;
  /* pas de will-change : ces couches tournent en continu, les garder
     promues en permanence coûte plus cher que ça ne rapporte */
}
.aurora--1 {
  width: 520px; height: 520px; top: -160px; right: -120px;
  background: radial-gradient(circle, #2a62f0 0%, rgba(42, 98, 240, 0) 70%);
  animation: drift-1 22s ease-in-out infinite;
}
.aurora--2 {
  width: 460px; height: 460px; bottom: -200px; left: -140px;
  background: radial-gradient(circle, #00c2ff 0%, rgba(0, 194, 255, 0) 70%);
  animation: drift-2 28s ease-in-out infinite;
  opacity: 0.38;
}
.aurora--3 {
  width: 380px; height: 380px; top: 40%; left: 45%;
  background: radial-gradient(circle, #7b5cff 0%, rgba(123, 92, 255, 0) 70%);
  animation: drift-3 25s ease-in-out infinite;
  opacity: 0.32;
}
@keyframes drift-1 {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33%      { transform: translate3d(-60px, 50px, 0) scale(1.12); }
  66%      { transform: translate3d(40px, 90px, 0) scale(0.94); }
}
@keyframes drift-2 {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(90px, -70px, 0) scale(1.18); }
}
@keyframes drift-3 {
  0%, 100% { transform: translate3d(0, 0, 0) scale(0.9); }
  40%      { transform: translate3d(-100px, -40px, 0) scale(1.1); }
  75%      { transform: translate3d(70px, 40px, 0) scale(1); }
}

/* --- Grille technique en fond, très discrète --- */
.hero__grid {
  position: absolute;
  inset: -10% -10% -10% -10%;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(ellipse 80% 70% at 50% 40%, #000 30%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 80% 70% at 50% 40%, #000 30%, transparent 75%);
  animation: grid-slide 30s linear infinite;
}
@keyframes grid-slide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(56px, 56px, 0); }
}

/* --- Particules qui montent lentement --- */
.hero__particles { position: absolute; inset: 0; }
.particle {
  position: absolute;
  bottom: -10px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: rgba(143, 176, 255, 0.75);
  animation: rise linear infinite;
}
@keyframes rise {
  0%   { transform: translate3d(0, 0, 0) scale(0.6); opacity: 0; }
  10%  { opacity: 0.9; }
  90%  { opacity: 0.5; }
  100% { transform: translate3d(24px, -640px, 0) scale(1.3); opacity: 0; }
}

/* La parallaxe souris est appliquée par le JS (attribut data-depth) */
.hero__parallax { position: relative; will-change: transform; }


/* ==========================================================================
   F. TITRES : RÉVÉLATION MOT À MOT + MOT ROTATIF
   ========================================================================== */
/* Chaque mot est enveloppé par le JS dans .word > .word__inner */
.word { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: 0.08em; }
.word__inner {
  display: inline-block;
  transform: translateY(110%) rotate(4deg);
  opacity: 0;
  transition: transform 800ms var(--ease-out-expo), opacity 600ms ease;
}
.is-visible .word__inner,
.split-in .word__inner { transform: none; opacity: 1; }

/* Filet de sécurité : posé par le JS 2,5 s après le chargement. Si la
   transition n a pas pu se jouer (onglet en arrière-plan, rendu suspendu),
   le titre s affiche d un coup plutôt que de rester masqué. */
.split-done .word__inner { transition: none; transform: none; opacity: 1; }

/* --- Mot qui change (« fait décoller » / « convertit » / ...) --- */
.rotator {
  display: inline-grid;
  /* Même alignement que les mots découpés (.word) : sans cela le mot rotatif
     décroche de la ligne du titre. */
  vertical-align: top;
  overflow: hidden;
  text-align: left;
  padding-bottom: 0.08em;
  line-height: inherit;
  /* La largeur est pilotée par le JS, qui mesure chaque mot et l applique au
     moment du changement. Elle s anime donc en même temps que le glissement :
     la phrase se resserre en douceur au lieu de sauter d un mot à l autre.
     Sans JS, aucune largeur n est posée et le conteneur reprend sa taille
     naturelle — le titre reste correct. */
  transition: width 700ms var(--ease-out-expo);
}
.rotator__item {
  grid-area: 1 / 1;
  /* Chaque mot garde SA largeur naturelle au lieu de s étirer sur la colonne :
     c est ce qui permet au JS de les mesurer un par un. */
  justify-self: start;
  background: linear-gradient(100deg, #6f9bff, #2a62f0);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  transform: translateY(100%);
  opacity: 0;
  transition: transform 700ms var(--ease-out-expo), opacity 400ms ease;
  white-space: nowrap;
}
.rotator__item.is-current { transform: none; opacity: 1; }
.rotator__item.is-leaving { transform: translateY(-100%); opacity: 0; }


/* ==========================================================================
   G. MAQUETTE NAVIGATEUR ANIMÉE
   ========================================================================== */
.browser-mock { transition: transform 500ms var(--ease-out-expo); }
.browser-mock__bar span:nth-child(1) { background: #ff5f57; }
.browser-mock__bar span:nth-child(2) { background: #febc2e; }
.browser-mock__bar span:nth-child(3) { background: #28c840; }

/* Barre d'adresse qui se remplit */
.browser-mock__url {
  flex: 1;
  height: 16px;
  margin-left: 10px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.12);
  position: relative;
  overflow: hidden;
}
.browser-mock__url::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(42, 98, 240,0.6), rgba(42, 98, 240,0));
  transform: scaleX(0);
  transform-origin: 0 50%;
  animation: url-load 6s ease-in-out infinite;
}
@keyframes url-load {
  0%, 8%   { transform: scaleX(0); }
  35%, 70% { transform: scaleX(1); }
  85%, 100%{ transform: scaleX(0); }
}

/* Les barres du faux site se construisent en boucle */
.js-anim .browser-mock__screen .skeleton {
  transform-origin: 0 50%;
  animation: build-line 6s var(--ease-out-expo) infinite;
}
.js-anim .browser-mock__screen .skeleton:nth-child(1) { animation-delay: 0.15s; }
.js-anim .browser-mock__screen .skeleton:nth-child(2) { animation-delay: 0.4s; }
.js-anim .browser-mock__screen .skeleton:nth-child(3) { animation-delay: 0.6s; }
.js-anim .browser-mock__screen .skeleton:nth-child(4) { animation-delay: 0.85s; }
@keyframes build-line {
  0%       { transform: scaleX(0); opacity: 0; }
  12%, 78% { transform: scaleX(1); opacity: 1; }
  92%, 100%{ transform: scaleX(0); opacity: 0; }
}

/* Reflet qui balaie l'écran */
.browser-mock__screen { position: relative; overflow: hidden; }
.browser-mock__screen::after {
  content: '';
  position: absolute; top: 0; left: -60%;
  width: 45%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.65), transparent);
  transform: skewX(-18deg);
  animation: sheen 6s ease-in-out infinite;
}
@keyframes sheen {
  0%, 55%  { left: -60%; }
  80%, 100%{ left: 130%; }
}

/* Étiquettes flottantes autour de la maquette */
.float-badge {
  position: absolute;
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(255, 255, 255, 0.97);
  color: var(--color-primary);
  font-family: var(--font-heading);
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
  white-space: nowrap;
  animation: float-y 5s ease-in-out infinite;
  z-index: 2;
}
.float-badge__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--color-success); animation: pulse-dot 2s ease-in-out infinite; }
.float-badge--1 { top: 8%; left: -14px; animation-delay: 0s; }
.float-badge--2 { bottom: 16%; right: -10px; animation-delay: 1.4s; }
.float-badge--3 { bottom: -14px; left: 18%; animation-delay: 2.6s; }
@keyframes float-y {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-12px); }
}
@keyframes pulse-dot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(12, 122, 91, 0.55); }
  70%      { box-shadow: 0 0 0 8px rgba(12, 122, 91, 0); }
}

/* --- Allègement mobile : sur petit écran on garde la couleur mais on coupe
   les effets coûteux (flous plein écran, grille, particules). Le hero reste
   identique visuellement à 95 %, pour une fraction du coût. --- */
@media (max-width: 719px) {
  .aurora { filter: blur(38px); animation: none; }
  .aurora--3 { display: none; }
  .hero__grid, .hero__particles { display: none; }
}

/* Sur petit écran, les étiquettes restent dans le cadre */
@media (max-width: 719px) {
  .float-badge--1 { left: 0; }
  .float-badge--2 { right: 0; }
  .float-badge--3 { display: none; }
}


/* ==========================================================================
   H. BANDEAU DÉFILANT (MARQUEE)
   ========================================================================== */
.marquee {
  overflow: hidden;
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
  padding: var(--space-sm) 0;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee 38s linear infinite;
  will-change: transform;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0 var(--space-md);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: var(--fs-xl);
  color: var(--color-heading);
  opacity: 0.75;
  white-space: nowrap;
}
.marquee__item::after {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
}
@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}


/* ==========================================================================
   I. CARTES : INCLINAISON 3D + HALO QUI SUIT LE CURSEUR
   ========================================================================== */
[data-tilt] {
  transform-style: preserve-3d;
  position: relative;
  will-change: transform;
}
/* Halo local : positionné par le JS via --mx / --my */
[data-tilt]::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%),
              rgba(42, 98, 240, 0.13), transparent 62%);
  opacity: 0;
  transition: opacity 320ms ease;
  pointer-events: none;
  z-index: 0;
}
[data-tilt]:hover::before { opacity: 1; }
[data-tilt] > * { position: relative; z-index: 1; }

/* Bordure lumineuse au survol */
.card, .service-card, .testimonial { position: relative; }
.card::after, .service-card::after {
  content: '';
  position: absolute; inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg, rgba(42, 98, 240,0.7), rgba(42, 98, 240,0));
  -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 320ms ease;
  pointer-events: none;
}
.card:hover::after, .service-card:hover::after { opacity: 1; }

/* L'icône de carte réagit au survol */
.card__icon { transition: transform 480ms var(--ease-spring), background-color var(--transition); }
.card:hover .card__icon { transform: translateY(-3px) rotate(-8deg) scale(1.08); }


/* ==========================================================================
   J. BOUTONS : EFFET MAGNÉTIQUE + ONDE AU CLIC + BRILLANCE
   ========================================================================== */
.btn { position: relative; overflow: hidden; }

/* Brillance qui traverse le bouton au survol */
.btn::before {
  content: '';
  position: absolute; top: 0; left: -80%;
  width: 55%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.35), transparent);
  transform: skewX(-20deg);
  transition: left 650ms var(--ease-out-expo);
}
.btn:hover::before { left: 130%; }

/* Onde circulaire créée par le JS au clic */
.ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  transform: scale(0);
  animation: ripple-out 620ms var(--ease-out-expo) forwards;
  pointer-events: none;
}
.btn--outline .ripple, .btn--light .ripple { background: rgba(42, 98, 240, 0.28); }
@keyframes ripple-out {
  to { transform: scale(2.6); opacity: 0; }
}

[data-magnetic] { will-change: transform; }

/* Flèche des liens de carte */
.card__link svg, .card__link { transition: gap 300ms var(--ease-out-expo), transform 300ms var(--ease-out-expo); }


/* ==========================================================================
   K. ÉTAPES : LIGNE DE PROGRESSION QUI SE REMPLIT AU SCROLL
   ========================================================================== */
.steps { position: relative; }
/* Les deux lignes sont des pseudo-éléments du CONTENEUR : sans z-index elles
   seraient peintes après les .step et passeraient par-dessus les numéros.
   On les envoie derrière, et on remonte les étapes au-dessus. */
.steps::before, .steps::after { z-index: 0; }
.step { z-index: 1; }
.steps::before {
  content: '';
  position: absolute;
  left: 21px; top: 10px; bottom: 10px;
  width: 2px;
  background: var(--color-border);
}
.steps::after {
  content: '';
  position: absolute;
  left: 21px; top: 10px;
  width: 2px;
  height: calc(var(--steps-progress, 0) * (100% - 20px));
  background: linear-gradient(180deg, var(--color-accent), #7aa2ff);
  transition: height 200ms linear;
}
.step::before { transition: transform 520ms var(--ease-spring), box-shadow 400ms ease; transform: scale(0.6); }
.step.is-visible::before { transform: scale(1); box-shadow: 0 0 0 6px rgba(42, 98, 240, 0.14); }

/* En desktop la ligne verticale est masquée (grille sur 2 colonnes) */
@media (min-width: 1000px) {
  .steps::before, .steps::after { display: none; }
}


/* ==========================================================================
   L. COMPTEURS, TABLEAU DE PRIX, LISTES EN CASCADE
   ========================================================================== */
.stat__value, .example-total strong { font-variant-numeric: tabular-nums; }

/* Barre d'accent qui glisse sur la ligne survolée */
.price-table tbody tr { position: relative; }
.price-table tbody tr::after {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--color-accent);
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform 380ms var(--ease-out-expo);
}
.price-table tbody tr:hover::after { transform: scaleY(1); }
.price-table .price-value { transition: transform 320ms var(--ease-spring); display: inline-block; }
.price-table tbody tr:hover .price-value { transform: translateX(4px) scale(1.05); }

/* Les puces cochées apparaissent une à une */
.js-anim .check-list li {
  opacity: 0;
  transform: translateX(-10px);
  transition: opacity 420ms ease, transform 480ms var(--ease-out-expo);
}
.js-anim .check-list.is-visible li,
.js-anim .is-visible .check-list li { opacity: 1; transform: none; }
.js-anim .check-list li:nth-child(1) { transition-delay: 60ms; }
.js-anim .check-list li:nth-child(2) { transition-delay: 120ms; }
.js-anim .check-list li:nth-child(3) { transition-delay: 180ms; }
.js-anim .check-list li:nth-child(4) { transition-delay: 240ms; }
.js-anim .check-list li:nth-child(5) { transition-delay: 300ms; }
.js-anim .check-list li:nth-child(6) { transition-delay: 360ms; }

/* Scintillement des zones placeholder */
.placeholder { overflow: hidden; }
.placeholder::after {
  content: '';
  position: absolute; top: 0; left: -50%;
  width: 40%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.75), transparent);
  animation: shimmer 3.4s ease-in-out infinite;
}
@keyframes shimmer {
  0%   { left: -50%; }
  60%, 100% { left: 130%; }
}

/* Avis clients : légère respiration */
.testimonial { transition: transform 480ms var(--ease-out-expo), box-shadow 480ms ease; }
.testimonial:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); }
.testimonial__stars span { display: inline-block; animation: star-pop 500ms var(--ease-spring) backwards; }

@keyframes star-pop {
  from { transform: scale(0) rotate(-40deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* FAQ : le contenu se déplie en douceur */
.faq__item p { animation: fade-slide 420ms var(--ease-out-expo); }
@keyframes fade-slide {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}

/* Champs de formulaire : léger soulèvement au focus */
.field input, .field select, .field textarea { transition: border-color var(--transition), background var(--transition), box-shadow var(--transition), transform 260ms var(--ease-out-expo); }
.field input:focus, .field select:focus, .field textarea:focus { transform: translateY(-2px); }

/* Bouton d'envoi en cours de traitement */
.btn.is-loading { pointer-events: none; opacity: 0.85; }
.btn.is-loading::after {
  content: '';
  width: 16px; height: 16px;
  margin-left: 10px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Message de confirmation qui apparaît */
.form-feedback.is-visible { animation: pop-in 520ms var(--ease-spring); }
@keyframes pop-in {
  from { opacity: 0; transform: scale(0.94) translateY(-8px); }
  to   { opacity: 1; transform: none; }
}


/* ==========================================================================
   M. BOUTON RETOUR EN HAUT (injecté par le JS)
   ========================================================================== */
.to-top {
  position: fixed;
  right: 20px; bottom: 20px;
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(20px) scale(0.8);
  transition: opacity 380ms ease, transform 420ms var(--ease-spring), background-color var(--transition);
  z-index: 90;
}
.to-top.is-visible { opacity: 1; transform: none; }
.to-top:hover { background: var(--color-accent); }
/* Anneau de progression autour du bouton */
.to-top__ring { position: absolute; inset: -4px; transform: rotate(-90deg); pointer-events: none; }
.to-top__ring circle { fill: none; stroke: var(--color-accent); stroke-width: 2.5; stroke-linecap: round; }


/* ==========================================================================
   N. VARIANTES DE RÉVÉLATION AU SCROLL
   Utilisation : data-reveal="left" | "right" | "scale" | "blur" | "flip"
   (data-reveal seul = glissement vers le haut, comportement d'origine)
   ========================================================================== */
.js-reveal [data-reveal="left"]  { transform: translateX(-42px); }
.js-reveal [data-reveal="right"] { transform: translateX(42px); }
.js-reveal [data-reveal="scale"] { transform: scale(0.9); }
.js-reveal [data-reveal="blur"]  { filter: blur(10px); transform: translateY(18px); }
.js-reveal [data-reveal="flip"]  { transform: perspective(900px) rotateX(-14deg) translateY(30px); transform-origin: 50% 100%; }
.js-reveal [data-reveal].is-visible { transform: none; filter: none; }

/* Cascade automatique appliquée par le JS sur les grilles */
[data-stagger] > * { transition-delay: calc(var(--i, 0) * 85ms); }


/* ==========================================================================
   O. TRANSITION ENTRE LES PAGES
   ========================================================================== */
/* RÈGLE DE SÉCURITÉ : aucune animation d'entrée globale sur <body> ni <main>.
   Deux raisons, apprises en test :
   1) un <body> animé/transformé devient le bloc conteneur des éléments
      position:fixed — le header collant, la barre de progression et le bouton
      « retour en haut » cessent alors de suivre l'écran ;
   2) si une animation qui part de opacity:0 ne démarre pas (onglet suspendu,
      transition de navigation interrompue), la page entière reste invisible.
   L'entrée est donc portée par les révélations section par section, qui ne
   peuvent jamais masquer plus qu'un bloc. */

/* Sortie de page : opacité seule, sur <main>, et jamais bloquante
   (le JS retire la classe au retour arrière). */
body.is-leaving main { opacity: 0; transition: opacity 240ms ease; }

/* NOTE : la transition native entre pages (@view-transition) n'est pas
   utilisée. Elle n'est encore supportée que par une partie des navigateurs,
   et elle ferait doublon avec le fondu JavaScript ci-dessus, qui donne le
   même résultat partout. Pour l'essayer plus tard : ajouter
   @view-transition { navigation: auto; } et retirer le fondu dans
   assets/js/animations.js (section 14). */


/* ==========================================================================
   ACCESSIBILITÉ — « réduire les animations » désactive TOUT le mouvement
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .aurora, .hero__grid, .particle, .marquee__track,
  .browser-mock__screen::after, .browser-mock__url::after,
  .float-badge, .float-badge__dot, .placeholder::after,
  .js-anim .browser-mock__screen .skeleton {
    animation: none !important;
  }
  .word__inner { transform: none !important; opacity: 1 !important; }
  .rotator__item { position: static; transform: none !important; opacity: 1 !important; }
  .rotator__item:not(.is-current) { display: none; }
  .js-anim .check-list li,
  .js-anim .nav .nav__list li,
  .js-anim .nav .nav__cta { opacity: 1 !important; transform: none !important; }
  body.is-leaving main { opacity: 1; }
  .cursor-glow { display: none; }
  [data-reveal] { filter: none !important; }
}
