/* ============================================================
   Lightworker Collective — Animations (animations.css)
   La lumière est la signature : lueurs floutées qui respirent,
   lentement. Tout le reste demeure sobre et discipliné.

   Cohérence : une seule famille d'easing (--ease-soft),
   durées 0,6 à 1,1 s, staggers 60 à 120 ms. La variété vient
   de la direction et de la nature du mouvement, jamais du
   rythme. Les états initiaux cachés sont scopés sous html.js :
   sans JavaScript, tout le contenu est visible.
   ============================================================ */

/* ------------------------------------------------------------
   Écran de chargement (à chaque chargement de page — voir loading.js)
   ------------------------------------------------------------ */

html.lw-loading { background: var(--charcoal); }
html.lw-loading body { visibility: hidden; }
html.lw-loading body .lw-loader { visibility: visible; }

.lw-loader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--charcoal);
  opacity: 1;
  transition: opacity 0.6s var(--ease-soft);
}

.lw-loader.is-done { opacity: 0; pointer-events: none; }

.lw-loader-mark {
  position: relative;
  display: grid;
  place-items: center;
  width: min(420px, 82vw);
  aspect-ratio: 16 / 10;
}

.lw-loader-bloom {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 280px;
  margin: -140px 0 0 -140px;
  animation-duration: 4.5s;
}

.lw-loader-orb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  color: #6D9BA8;   /* orbe du loader en bleu clair (currentColor) */
}

.lw-loader-logo {
  position: relative;
  text-align: center;
  color: var(--cream);
}

.lw-loader-logo .logo-light { font-size: 2.1rem; }
.lw-loader-logo .logo-worker { font-size: 1.42rem; }
.lw-loader-logo .logo-collective { font-size: 0.68rem; }

/* ------------------------------------------------------------
   Blooms ambiants — grands radial-gradients très flous qui
   respirent en boucle lente (transform/opacity seulement).
   Jamais plus de 2 par viewport. Jamais néon.
   ------------------------------------------------------------ */

.bloom {
  position: absolute;
  width: min(46vw, 620px);
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.7;
  pointer-events: none;
  z-index: 0;
  animation: bloom-breathe 12s ease-in-out infinite alternate;
  will-change: transform, opacity;
}

@keyframes bloom-breathe {
  from { transform: scale(1);    opacity: 0.7; }
  to   { transform: scale(1.12); opacity: 0.9; }
}

/* Teintes de lueur permises : powder blue, teal, tan */
.bloom--powder { background: radial-gradient(circle, color-mix(in srgb, var(--powder-blue) 55%, transparent), transparent 70%); }
.bloom--teal   { background: radial-gradient(circle, color-mix(in srgb, var(--teal) 50%, transparent), transparent 70%); }
.bloom--tan    { background: radial-gradient(circle, color-mix(in srgb, var(--tan) 45%, transparent), transparent 70%); }

/* Positions */
.bloom--tl { top: -8%; left: -6%; }
.bloom--br { right: -8%; bottom: -10%; animation-duration: 14s; }
.bloom--center { left: calc(50% - min(23vw, 310px)); top: -12%; animation-duration: 13s; }
/* À cheval sur la jointure : le halo semble émaner vers la section voisine */
.bloom--joint-bottom { bottom: calc(-0.5 * min(46vw, 620px)); left: 16%; animation-duration: 10s; }
.bloom--vip { width: 380px; top: -120px; right: -90px; animation-duration: 11s; }

/* Les blooms qui débordent sur la section voisine doivent passer
   au-dessus de son fond (peint en couche z-auto) tout en restant
   sous tous les contenus (.section > .container est z-index: 1 et
   peint après dans l'ordre du DOM). */
.bloom--joint-bottom,
.bloom--br { z-index: 1; }

/* Motif « boule de lumière » en orbite (SVG inline, SMIL) */
.lw-orb { pointer-events: none; color: var(--teal); }

/* ------------------------------------------------------------
   01 · HERO — séquence orchestrée au chargement.
   Chaque élément .hro porte son délai (--d); tout démarre quand
   loading.js pose html.lw-ready (fin du fondu du loader).
   ------------------------------------------------------------ */

.js .hro {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.9s var(--ease-soft) var(--d, 0s),
              transform 0.9s var(--ease-soft) var(--d, 0s);
}

/* Le titre entre ligne par ligne, masqué par sa ligne */
.hero .h1-line { display: block; overflow: hidden; padding-bottom: 0.08em; margin-bottom: -0.08em; }
.hero .h1-inner { display: block; }
.js .hero .h1-line .hro { transform: translateY(105%); }

/* Le bloc vidéo glisse de la droite */
.js .hro--slide { transform: translateX(48px); }

html.lw-ready .hro { opacity: 1; transform: none; }

/* Spécificité supérieure à l'état initial des lignes du titre
   (.js .hero .h1-line .hro) — sans cette règle, le H1 resterait
   clippé sous l'overflow de .h1-line. */
html.lw-ready .hero .h1-line .hro { transform: none; }

/* ------------------------------------------------------------
   Chorégraphie des reveals — pattern interrupts.
   Mécanique commune : IntersectionObserver ajoute .is-visible
   une seule fois par section (voir main.js).
   ------------------------------------------------------------ */

/* Tête de section : entrée douce commune à tous les patterns */
.js [data-reveal] .section-head {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.8s var(--ease-soft), transform 0.8s var(--ease-soft);
}

.js [data-reveal].is-visible .section-head { opacity: 1; transform: none; }

/* 02 · Témoignages — carrousel horizontal piloté par le scroll.
   La piste est déplacée par main.js (var --tx) en mode épinglé;
   ici on ne gère que l'entrée douce de la piste au premier affichage. */
.js [data-testi] .testi-stage {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.9s var(--ease-soft), transform 0.9s var(--ease-soft);
}

.js [data-testi].is-visible .testi-stage { opacity: 1; transform: none; }

/* En mode épinglé, la piste est déjà translatée par --tx : on neutralise
   le translateY d'entrée pour ne pas cumuler les transforms. */
.js [data-testi].is-pinned .testi-stage { opacity: 1; transform: none; transition: none; }

/* 03 · Pitch — séquence de textes pilotée par le scroll (voir
   components.css .pitch-seq + main.js initPitchSeq). */

/* 04 · Piliers — cascade : scale(0.96 → 1) + rotation résiduelle
   d'environ 1,5° qui se redresse, décalées de 120 ms */
/* Animation (pas transition) : préserve le hover des cartes */
.js [data-reveal="cascade-tilt"] .pillar-card { opacity: 0; }

.js [data-reveal="cascade-tilt"].is-visible .pillar-card {
  opacity: 1;
  animation: rv-tilt 0.9s var(--ease-soft) calc(var(--i, 0) * 120ms) backwards;
}

@keyframes rv-tilt { from { opacity: 0; transform: scale(0.96) rotate(1.5deg); } }

/* 05 · Lieu — galerie « zoom parallax » : les tuiles se zooment au
   scroll (transforms posés par main.js). L'entrée douce des blocs de
   la section est portée par le pattern « fade » commun. */

/* 06 · La table — split croisé : photo par la gauche, texte par
   la droite, directions opposées simultanées */
.js [data-reveal="split-cross"] [data-rv="from-left"],
.js [data-reveal="split-cross"] [data-rv="from-right"],
.js [data-reveal="split-cross"] [data-rv="from-below"] {
  opacity: 0;
  transition: opacity 0.95s var(--ease-soft), transform 0.95s var(--ease-soft);
}

.js [data-reveal="split-cross"] [data-rv="from-left"]  { transform: translateX(-48px); }
.js [data-reveal="split-cross"] [data-rv="from-right"] { transform: translateX(48px); }
.js [data-reveal="split-cross"] [data-rv="from-below"] { transform: translateY(28px); }

.js [data-reveal="split-cross"].is-visible [data-rv="from-left"],
.js [data-reveal="split-cross"].is-visible [data-rv="from-right"],
.js [data-reveal="split-cross"].is-visible [data-rv="from-below"] { opacity: 1; transform: none; }

/* 06b · La table — le texte se matérialise en cascade au scroll, à
   l'entrée de la section : label → titre → corps → signature. Chaque
   ligne monte et se dé-floute avec un léger décalage. La vidéo (colonne
   média) garde son propre défilement, on n'anime que le texte. */
.js [data-reveal="table-text"] .split-text > * {
  opacity: 0;
  transform: translateY(24px);
  filter: blur(5px);
  transition: opacity 0.9s var(--ease-soft), transform 0.9s var(--ease-soft), filter 0.9s var(--ease-soft);
}

.js [data-reveal="table-text"].is-visible .split-text > * { opacity: 1; transform: none; filter: none; }
.js [data-reveal="table-text"].is-visible .split-text > *:nth-child(1) { transition-delay: 0.02s; }
.js [data-reveal="table-text"].is-visible .split-text > *:nth-child(2) { transition-delay: 0.15s; }
.js [data-reveal="table-text"].is-visible .split-text > *:nth-child(3) { transition-delay: 0.30s; }
.js [data-reveal="table-text"].is-visible .split-text > *:nth-child(4) { transition-delay: 0.45s; }

/* 07 · Équipe — présentateur animé. L'entrée douce est portée par
   le pattern « fade » commun (tête + contenu). Le texte du membre
   actif apparaît mot par mot (spans .crew-word posés par main.js). */
.js .crew-word {
  opacity: 0;
  transform: translateY(7px);
  filter: blur(6px);
  animation: crew-word 0.5s var(--ease-soft) calc(var(--w, 0) * 0.028s) forwards;
}

@keyframes crew-word {
  to { opacity: 1; transform: none; filter: blur(0); }
}

/* 08 · Bénéfices — les crochets se dessinent (stroke-dashoffset)
   en cascade rapide pendant que les textes fadent */
.js [data-reveal="check-draw"] .check path {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: stroke-dashoffset 0.6s var(--ease-soft) calc(var(--i, 0) * 70ms);
}

.js [data-reveal="check-draw"] .check-item span {
  opacity: 0;
  transition: opacity 0.6s var(--ease-soft) calc(var(--i, 0) * 70ms);
}

.js [data-reveal="check-draw"].is-visible .check path { stroke-dashoffset: 0; }
.js [data-reveal="check-draw"].is-visible .check-item span { opacity: 1; }

/* 08b · Cartes « arche » — montée + léger scale en cascade */
.js [data-reveal="gift-rise"] .gift-card {
  opacity: 0;
  transform: translateY(26px) scale(0.96);
  transition:
    opacity 0.7s var(--ease-soft) calc(var(--i, 0) * 60ms),
    transform 0.7s var(--ease-soft) calc(var(--i, 0) * 60ms);
}
.js [data-reveal="gift-rise"].is-visible .gift-card { opacity: 1; transform: none; }

/* 09 · Bande CTA — le fond entre en scale(1.04 → 1) très lent
   pendant que le titre fade — respiration inversée */
.js [data-reveal="breathe-in"] .cta-bg {
  transform: scale(1.04);
  transition: transform 1.1s var(--ease-soft);
}

.js [data-reveal="breathe-in"] h2,
.js [data-reveal="breathe-in"] .cta-lines {
  opacity: 0;
  transition: opacity 1s var(--ease-soft);
}

.js [data-reveal="breathe-in"] .cta-lines { transition-delay: 0.25s; }

/* Le bouton est interactif : animation « backwards » pour ne pas
   écraser sa transition de hover */
.js [data-reveal="breathe-in"] .btn { opacity: 0; }

.js [data-reveal="breathe-in"].is-visible .cta-bg { transform: scale(1); }
.js [data-reveal="breathe-in"].is-visible h2,
.js [data-reveal="breathe-in"].is-visible .cta-lines { opacity: 1; }

.js [data-reveal="breathe-in"].is-visible .btn {
  opacity: 1;
  animation: rv-fade 1s var(--ease-soft) 0.5s backwards;
}

@keyframes rv-fade { from { opacity: 0; } }

/* 10 · Après + VIP — le bloom « allume » la carte : la lueur
   apparaît d'abord, la carte ensuite */
.js [data-reveal="glow-vip"] .vip-wrap .bloom {
  opacity: 0;
  animation: none;
  transition: opacity 1s var(--ease-soft);
}

.js [data-reveal="glow-vip"].is-visible .vip-wrap .bloom {
  opacity: 0.7;
  animation: bloom-breathe 11s ease-in-out 1s infinite alternate;
}

.js [data-reveal="glow-vip"] .vip-card {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.9s var(--ease-soft) 0.35s, transform 0.9s var(--ease-soft) 0.35s;
}

.js [data-reveal="glow-vip"].is-visible .vip-card { opacity: 1; transform: none; }

/* 11 · Investissement — les deux cartes montent en alternance;
   les prix comptent une seule fois (count-up, voir main.js) */
/* Animation (pas transition) : préserve le hover des cartes */
.js [data-reveal="rise-alt"] .price-card { opacity: 0; }

.js [data-reveal="rise-alt"].is-visible .price-card {
  opacity: 1;
  animation: rv-rise 0.9s var(--ease-soft) calc(var(--i, 0) * 120ms) backwards;
}

@keyframes rv-rise { from { opacity: 0; transform: translateY(34px); } }

.js [data-reveal="rise-alt"] .vip-reminder,
.js [data-reveal="rise-alt"] .rarity-line {
  opacity: 0;
  transition: opacity 0.9s var(--ease-soft) 0.5s;
}

.js [data-reveal="rise-alt"].is-visible .vip-reminder,
.js [data-reveal="rise-alt"].is-visible .rarity-line { opacity: 1; }

/* 12 · Footer — simple fondu, on termine calme */
.js [data-reveal="fade"] > .container {
  opacity: 0;
  transition: opacity 1s var(--ease-soft);
}

.js [data-reveal="fade"].is-visible > .container { opacity: 1; }

/* ------------------------------------------------------------
   Vidéo : placeholder visible derrière tant que le poster
   n'existe pas (voir main.js)
   ------------------------------------------------------------ */

.video-frame.no-poster .img-placeholder { display: flex; z-index: 0; }
.video-frame.no-poster video { position: relative; z-index: 1; }

/* ------------------------------------------------------------
   Bannière de consentement — entrée douce
   ------------------------------------------------------------ */

.consent.is-open { animation: consent-in 0.6s var(--ease-soft); }

@keyframes consent-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------
   prefers-reduced-motion : toutes les animations décoratives se
   désactivent; tout apparaît instantanément.
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {

  .js .hro,
  .js [data-reveal] .section-head,
  .js [data-reveal] [data-rv],
  .js [data-reveal="table-text"] .split-text > *,
  .js [data-reveal="cascade-tilt"] .pillar-card,
  .js .crew-word,
  .js [data-reveal="check-draw"] .check-item span,
  .js [data-reveal="gift-rise"] .gift-card,
  .js [data-reveal="breathe-in"] h2,
  .js [data-reveal="breathe-in"] .cta-lines,
  .js [data-reveal="breathe-in"] .btn,
  .js [data-reveal="breathe-in"] .cta-bg,
  .js [data-reveal="glow-vip"] .vip-card,
  .js [data-reveal="rise-alt"] .price-card,
  .js [data-reveal="rise-alt"] .vip-reminder,
  .js [data-reveal="rise-alt"] .rarity-line,
  .js [data-testi] .testi-stage,
  .js [data-reveal="fade"] > .container {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    filter: none !important;
    transition: none !important;
    animation: none !important;
  }

  .js [data-reveal="check-draw"] .check path {
    stroke-dashoffset: 0 !important;
    transition: none !important;
  }

  /* Les lueurs deviennent statiques, les orbites disparaissent */
  .bloom,
  .js [data-reveal="glow-vip"] .vip-wrap .bloom {
    animation: none !important;
    opacity: 0.7 !important;
    transition: none !important;
  }

  .lw-orb { display: none !important; }

  /* Présentateur d'équipe : transitions instantanées entre membres */
  .crew.is-enhanced .crew-member { transition: none !important; }

  .btn, .btn-arrow, .testi-card figure, .pillar-card, .price-card,
  .crew-arrow, .video-play, .nav-links a::after, .consent.is-open {
    transition: none !important;
    animation: none !important;
  }

  .lw-loader { transition: none !important; }
}
