/* ============================================================================
   animations.css

   DEUX RÈGLES DURES :
   1. On n'anime QUE `transform` et `opacity` — jamais width/height/top/left ni
      box-shadow directement. Ces propriétés-là repassent par la mise en page et
      se paient en réactivité.
   2. L'état initial invisible d'un reveal est scopé sous `html.js`. Sans
      JavaScript, la classe n'existe pas et le contenu reste visible.
   ============================================================================ */

/* ---- reveals : rien n'est masqué tant que le JS n'a pas pris la main ---- */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity var(--t-slow) var(--ease-out-expo),
              transform var(--t-slow) var(--ease-out-expo);
  will-change: opacity, transform;
}
.js [data-reveal].is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  will-change: auto;
}
/* décalage en cascade, borné : au-delà de 6 éléments l'attente devient une gêne */
.js [data-reveal][data-delai="1"] { transition-delay: 60ms; }
.js [data-reveal][data-delai="2"] { transition-delay: 120ms; }
.js [data-reveal][data-delai="3"] { transition-delay: 180ms; }
.js [data-reveal][data-delai="4"] { transition-delay: 240ms; }
.js [data-reveal][data-delai="5"] { transition-delay: 300ms; }

/* ---- atmosphère : deux nappes de lumière qui dérivent lentement.
   La page est un espace, pas une feuille. Le mouvement est lent (40 s et 55 s)
   et de faible amplitude : il se remarque sans distraire.
   Un bouton de pause le contrôle — c'est une exigence distincte de
   « mouvement réduit » (WCAG 2.2.2, niveau A). ---- */
.aura {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: clip;
  background:
    radial-gradient(900px 620px at 12% -8%, rgba(28, 93, 220, 0.30), transparent 62%),
    radial-gradient(760px 540px at 92% 4%, rgba(22, 218, 255, 0.13), transparent 60%),
    radial-gradient(1200px 760px at 50% 108%, rgba(0, 34, 90, 0.55), transparent 66%);
}
.aura::before,
.aura::after {
  content: "";
  position: absolute;
  inline-size: 46vmax;
  block-size: 46vmax;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.42;
  will-change: transform;
}
.aura::before {
  inset-block-start: -14vmax;
  inset-inline-start: -10vmax;
  background: radial-gradient(circle, rgba(28, 93, 220, 0.55), transparent 68%);
  animation: derive-a 40s var(--ease-out-cubic) infinite alternate;
}
.aura::after {
  inset-block-end: -18vmax;
  inset-inline-end: -12vmax;
  background: radial-gradient(circle, rgba(22, 218, 255, 0.30), transparent 68%);
  animation: derive-b 55s var(--ease-out-cubic) infinite alternate;
}
@keyframes derive-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(7vmax, 5vmax, 0) scale(1.14); }
}
@keyframes derive-b {
  from { transform: translate3d(0, 0, 0) scale(1.08); }
  to   { transform: translate3d(-6vmax, -4vmax, 0) scale(1); }
}

/* grain : casse le banding des dégradés sombres, coûte un seul élément */
.aura__grain {
  position: absolute;
  inset: 0;
  opacity: 0.16;
  mix-blend-mode: overlay;
  background-image: var(--grain-url);
  background-size: 180px 180px;
}

/* ---- pause du mouvement d'ambiance ---- */
html[data-mouvement="pause"] .aura::before,
html[data-mouvement="pause"] .aura::after { animation-play-state: paused; }

/* ---- survol des cartes : élévation par transform, ombre par pseudo-élément
   (animer box-shadow directement repeint à chaque image) ---- */
.card, .rule, .tool { position: relative; }
.card::after, .rule::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--shadow-2);
  opacity: 0;
  transition: opacity var(--t) var(--ease-out-cubic);
  pointer-events: none;
}
.card:hover::after, .rule:hover::after { opacity: 1; }

/* ---- soulignement animé des liens de contenu.
   On anime `text-decoration-color`, PAS un dégradé de fond : un
   `linear-gradient` posé derrière du texte est lu comme un fond par tout
   contrôleur de contraste, qui calcule alors 1:1 sur son point le plus
   défavorable. Défaut mesuré ici le 2026-08-07 sur les 5 liens de sources. ---- */
.prose a {
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  transition: text-decoration-color var(--t) var(--ease-out-cubic), color var(--t-fast) var(--ease-out-cubic);
}
.prose a:hover, .prose a:focus-visible { text-decoration-color: currentColor; }

/* ---- apparition des blocs dépliés, sans JavaScript ---- */
details > div { overflow: clip; }
@supports (transition-behavior: allow-discrete) {
  details[open] > div { animation: deplier var(--t) var(--ease-out-expo); }
  @keyframes deplier { from { opacity: 0; transform: translate3d(0, -6px, 0); } to { opacity: 1; transform: none; } }
}

/* Mouvement réduit et couleurs forcées sont traités dans `preferences.css`,
   chargé en dernier — voir l'explication en tête de ce fichier-là. */
