/* ============================================================
   B-effets.css — couche d'animation autonome · Noveria
   Zéro dépendance. S'active via la classe .js sur <html>
   (ajoutée par B-effets.js). Sans JS, tout reste visible.
   Effets pilotés par attributs : data-anim, data-stagger,
   data-tilt, data-spotlight, data-marquee, data-words,
   data-typing, em.brille, .blob, #progres, curseur discret.
   Uniquement transform/opacity : 60 fps garantis.
   ============================================================ */

/* Courbe de sortie commune, douce et naturelle */
:root { --bz-ease: cubic-bezier(.2, .7, .2, 1); }

/* ---------- 1. Apparitions au défilement ---------- */
/* data-anim="fade-up|fade-left|fade-right|zoom" + data-delay="ms" */
.js [data-anim] {
  opacity: 0;
  transition: opacity .7s var(--bz-ease), transform .7s var(--bz-ease);
  transition-delay: var(--bz-d, 0ms);   /* délai posé en JS depuis data-delay */
  will-change: opacity, transform;
}
.js [data-anim="fade-up"]    { transform: translateY(24px); }
.js [data-anim="fade-left"]  { transform: translateX(28px); }
.js [data-anim="fade-right"] { transform: translateX(-28px); }
.js [data-anim="zoom"]       { transform: scale(.92); }
.js [data-anim].anim-in { opacity: 1; transform: none; }

/* ---------- 2. Cascade : data-stagger sur un parent ---------- */
/* Les enfants entrent les uns après les autres (délais posés en JS) */
.js [data-stagger] > * {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .6s var(--bz-ease), transform .6s var(--bz-ease);
  transition-delay: var(--bz-d, 0ms);
}
.js [data-stagger].anim-in > * { opacity: 1; transform: none; }

/* ---------- 3. Titre mot par mot : data-words ---------- */
/* Les <span class="mot"> sont créés en JS ; sans JS le texte est intact */
.js [data-words] .mot {
  display: inline-block;
  opacity: 0;
  transform: translateY(.45em) rotate(2deg);
  transition: opacity .55s var(--bz-ease), transform .55s var(--bz-ease);
  transition-delay: var(--bz-d, 0ms);
}
.js [data-words].anim-in .mot { opacity: 1; transform: none; }

/* ---------- 4. Mot tapé puis effacé : data-typing ---------- */
/* Curseur qui clignote après le mot en cours de frappe */
[data-typing]::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 3px;
  vertical-align: -.12em;
  background: currentColor;
  animation: bz-clignote 1s steps(2) infinite;
}
@keyframes bz-clignote { 50% { opacity: 0; } }

/* ---------- 5. Défilé infini : data-marquee ---------- */
/* B-effets.js double le contenu dans une .bz-piste ; pause au survol */
.js [data-marquee] {
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.bz-piste {
  display: flex;
  align-items: center;
  gap: 56px;
  width: max-content;
  animation: bz-defile var(--bz-v, 30s) linear infinite;
}
[data-marquee]:hover .bz-piste { animation-play-state: paused; }
@keyframes bz-defile { to { transform: translateX(-50%); } }

/* ---------- 6. Carte inclinable 3D : data-tilt ---------- */
/* Le JS pose la rotation ; ici le reflet qui suit la souris (--mx/--my) */
.js [data-tilt] { position: relative; will-change: transform; }
.js [data-tilt]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 50%),
              rgba(255, 255, 255, .38), transparent 60%);
  opacity: 0;
  transition: opacity .3s;
  pointer-events: none;
}
.js [data-tilt]:hover::after { opacity: 1; }

/* ---------- 7. Lumière radiale : data-spotlight ---------- */
/* Halo bleu qui suit le curseur sur la carte (variables --mx/--my) */
.js [data-spotlight] { position: relative; }
.js [data-spotlight]::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%),
              rgba(31, 111, 235, .12), transparent 60%);
  opacity: 0;
  transition: opacity .3s;
  pointer-events: none;
}
.js [data-spotlight]:hover::before { opacity: 1; }

/* ---------- 8. Bouton magnétique : data-magnet ---------- */
/* Le JS pose le translate ; la transition rend le retour élastique */
.js [data-magnet] { transition: transform .25s var(--bz-ease); will-change: transform; }

/* ---------- 9. Barre de progression de lecture : #progres ---------- */
/* La largeur est posée en JS au défilement */
#progres {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  background: linear-gradient(90deg, #1F6FEB, #7FB3FF);
  z-index: 60;
  pointer-events: none;
}

/* ---------- 10. Curseur personnalisé discret (desktop) ---------- */
/* Point + halo, visibles seulement quand le corps porte .bz-curseur */
.bz-cur, .bz-halo {
  position: fixed;
  top: 0;
  left: 0;
  border-radius: 50%;
  pointer-events: none;
  z-index: 9999;
  opacity: 0;
  transition: opacity .3s;
}
.bz-cur  { width: 7px;  height: 7px;  background: #1F6FEB; }
.bz-halo { width: 34px; height: 34px; border: 1.5px solid rgba(31, 111, 235, .45);
           transition: opacity .3s, width .25s var(--bz-ease), height .25s var(--bz-ease); }
body.bz-curseur .bz-cur, body.bz-curseur .bz-halo { opacity: 1; }
.bz-halo.bz-sur { width: 48px; height: 48px; } /* halo qui s'élargit sur les liens */

/* ---------- 11. Fonds « blobs » lumineux : .blob ---------- */
/* Blobs colorés flous qui dérivent lentement (à placer en absolu) */
.blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: .5;
  z-index: -1;
  animation: bz-derive 18s ease-in-out infinite alternate;
}
@keyframes bz-derive {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(48px, 36px) scale(1.12); }
}

/* ---------- 12. Dégradé animé : <em class="brille"> ---------- */
/* Texte en dégradé bleu dont la lumière circule en boucle */
em.brille {
  font-style: normal;
  background: linear-gradient(90deg, #1F6FEB, #7FB3FF, #1F6FEB);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: bz-brille 6s linear infinite;
}
@keyframes bz-brille { to { background-position: 200% center; } }

/* ---------- Accessibilité : mouvement réduit ---------- */
/* Tout coupé, contenu entièrement visible, aucun état caché */
@media (prefers-reduced-motion: reduce) {
  .js [data-anim],
  .js [data-stagger] > *,
  .js [data-words] .mot {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .blob, .bz-piste, em.brille, [data-typing]::after { animation: none !important; }
  .js [data-tilt], .js [data-magnet] { transition: none !important; transform: none !important; }
  .bz-cur, .bz-halo { display: none !important; }
}
