/* ==========================================================================
   animations.css — bibliothèque d'animations vanilla pour sites vitrines
   Aucune dépendance. Aucun build. Transform et opacity uniquement.
   Inspirée des patterns de SmoothUI / Magic UI, réécrite sans React.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Réglages
   Surchargez ces variables dans votre tokens.css pour accorder le rythme
   des animations à la direction artistique du site.
   -------------------------------------------------------------------------- */
:root {
  --anim-duree-courte: 260ms;
  --anim-duree: 720ms;
  --anim-duree-longue: 1100ms;
  --anim-decalage: 90ms;          /* pas entre deux éléments d'une cascade */
  --anim-distance: 26px;          /* amplitude des translations */
  --anim-courbe: cubic-bezier(.22, 1, .36, 1);      /* sortie franche, arrivée douce */
  --anim-courbe-douce: cubic-bezier(.65, 0, .35, 1);
  --anim-flou: 10px;
}

/* --------------------------------------------------------------------------
   2. Révélations au scroll
   L'état de départ n'est appliqué que si JS est actif (classe .js sur <html>).
   Sans JS, tout reste visible : le site fonctionne, il est juste immobile.
   C'est volontaire — un site qui disparaît quand le JS échoue est un site cassé.
   -------------------------------------------------------------------------- */
.js [data-anim] {
  opacity: 0;
  will-change: transform, opacity;
  transition:
    opacity var(--anim-duree) var(--anim-courbe),
    transform var(--anim-duree) var(--anim-courbe),
    filter var(--anim-duree) var(--anim-courbe),
    clip-path var(--anim-duree-longue) var(--anim-courbe);
}

.js [data-anim="monte"]    { transform: translate3d(0, var(--anim-distance), 0); }
.js [data-anim="descend"]  { transform: translate3d(0, calc(var(--anim-distance) * -1), 0); }
.js [data-anim="gauche"]   { transform: translate3d(calc(var(--anim-distance) * -1.6), 0, 0); }
.js [data-anim="droite"]   { transform: translate3d(calc(var(--anim-distance) * 1.6), 0, 0); }
.js [data-anim="fondu"]    { transform: none; }
.js [data-anim="zoom"]     { transform: scale(.93); }
.js [data-anim="zoom-out"] { transform: scale(1.07); }
.js [data-anim="flou"]     { filter: blur(var(--anim-flou)); transform: translate3d(0, 14px, 0); }
.js [data-anim="rideau"]   { clip-path: inset(0 0 100% 0); opacity: 1; }
.js [data-anim="rideau-h"] { clip-path: inset(0 100% 0 0); opacity: 1; }
.js [data-anim="bascule"]  { transform: perspective(1000px) rotateX(12deg) translate3d(0, 20px, 0); transform-origin: top center; }

/* État révélé — une seule règle pour tous les types */
.js [data-anim].est-visible {
  opacity: 1;
  transform: none;
  filter: none;
  clip-path: inset(0 0 0 0);
}

/* Cascade : chaque enfant part avec un retard croissant.
   Utilisez data-anim-cascade sur le parent. Le JS pose --i sur chaque enfant. */
.js [data-anim-cascade] > * {
  transition-delay: calc(var(--i, 0) * var(--anim-decalage));
}

/* Retard manuel ponctuel */
.js [data-anim-retard="1"] { transition-delay: 120ms; }
.js [data-anim-retard="2"] { transition-delay: 240ms; }
.js [data-anim-retard="3"] { transition-delay: 360ms; }
.js [data-anim-retard="4"] { transition-delay: 480ms; }

/* --------------------------------------------------------------------------
   3. Texte découpé
   Le JS découpe en mots ou en lettres et pose --i sur chaque fragment.
   -------------------------------------------------------------------------- */
.js [data-texte-decoupe] { display: inline-block; }

.js .frag {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, .7em, 0);
  transition:
    opacity var(--anim-duree) var(--anim-courbe),
    transform var(--anim-duree) var(--anim-courbe);
  transition-delay: calc(var(--i, 0) * 38ms);
}

.js .est-visible .frag,
.js .frag.est-visible {
  opacity: 1;
  transform: none;
}

/* Variante masque : la lettre monte derrière un cache. Plus cinématique. */
.js [data-texte-decoupe="masque"] .frag-cache {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.js [data-texte-decoupe="masque"] .frag {
  transform: translate3d(0, 100%, 0);
}

/* --------------------------------------------------------------------------
   4. Parallaxe
   Le JS écrit --parallaxe (en pixels) pendant le scroll.
   -------------------------------------------------------------------------- */
[data-parallaxe] {
  transform: translate3d(0, var(--parallaxe, 0px), 0);
  will-change: transform;
}

[data-parallaxe-echelle] {
  transform: translate3d(0, var(--parallaxe, 0px), 0) scale(var(--parallaxe-echelle, 1));
  will-change: transform;
}

/* --------------------------------------------------------------------------
   5. Bandeau défilant (marquee)
   Le JS duplique le contenu pour rendre la boucle continue.
   -------------------------------------------------------------------------- */
.marquee {
  display: flex;
  overflow: hidden;
  user-select: none;
  gap: var(--marquee-gap, 3rem);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee__piste {
  display: flex;
  flex-shrink: 0;
  gap: var(--marquee-gap, 3rem);
  min-width: 100%;
  animation: marquee-defile var(--marquee-duree, 32s) linear infinite;
}

.marquee[data-sens="inverse"] .marquee__piste { animation-direction: reverse; }
.marquee:hover .marquee__piste { animation-play-state: paused; }

@keyframes marquee-defile {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-100% - var(--marquee-gap, 3rem)), 0, 0); }
}

/* --------------------------------------------------------------------------
   6. Micro-interactions
   -------------------------------------------------------------------------- */

/* Bouton magnétique — le JS écrit --mx / --my au survol */
[data-magnetique] {
  transform: translate3d(var(--mx, 0px), var(--my, 0px), 0);
  transition: transform 240ms var(--anim-courbe);
}

/* Carte inclinable — le JS écrit --rx / --ry */
[data-inclinaison] {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform 320ms var(--anim-courbe);
  transform-style: preserve-3d;
}

/* Lien souligné qui se trace au survol */
.lien-trace {
  position: relative;
  text-decoration: none;
}
.lien-trace::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -.15em;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 420ms var(--anim-courbe);
}
.lien-trace:hover::after,
.lien-trace:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left center;
}

/* Image qui respire au survol de son conteneur */
.cadre-image { overflow: hidden; }
.cadre-image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 900ms var(--anim-courbe);
}
.cadre-image:hover img { transform: scale(1.06); }

/* --------------------------------------------------------------------------
   7. Repères de lecture
   -------------------------------------------------------------------------- */

/* Barre de progression du scroll — le JS écrit --progression (0 → 1) */
.progression-scroll {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 9999;
  background: currentColor;
  transform: scaleX(var(--progression, 0));
  transform-origin: left center;
  pointer-events: none;
}

/* Chapitre collant */
.chapitre-collant {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: grid;
  align-content: center;
}

/* --------------------------------------------------------------------------
   8. Performance
   content-visibility évite au navigateur de peindre ce qui est hors écran.
   contain-intrinsic-size réserve la place pour que la barre de scroll
   ne saute pas — sans elle, le gain de perf crée un défaut visuel.
   -------------------------------------------------------------------------- */
.section-differee {
  content-visibility: auto;
  contain-intrinsic-size: auto 900px;
}

/* --------------------------------------------------------------------------
   9. Accessibilité
   Cette section n'est pas une politesse : certaines personnes ont des
   troubles vestibulaires que le mouvement déclenche physiquement.
   On neutralise le mouvement, jamais le contenu.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js [data-anim],
  .js .frag {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
    transition: none !important;
  }

  [data-parallaxe],
  [data-parallaxe-echelle],
  [data-magnetique],
  [data-inclinaison] {
    transform: none !important;
  }

  .marquee__piste { animation: none; }
  .cadre-image:hover img { transform: none; }

  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Focus clavier toujours visible — même quand le design est minimaliste */
:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}
