/* ==========================================================================
   motion.css — les primitives d'animation (CHARTE §5.1)
   Ecrites une fois ici, consommees par le W5. ZERO ligne de JavaScript : ce
   fichier sert tout le site, y compris le blog qui reste a zero script.
   ==========================================================================

   🔴 RELEVE DE SUPPORT DE `animation-timeline: view()` — 10 aout 2026

   CHARTE §9 et PLAN W2 exigeaient que ce support soit releve et DATE avant
   d'ecrire ce fichier, pas suppose. Voici le releve.

     Chrome / Edge        115      juillet 2023      ✅
     Safari / iOS         26.0     2025              ✅
     Samsung Internet     23                         ✅
     Firefox              156      15 septembre 2026 ⛔ pas encore sorti

   Firefox stable au jour du releve : 153 (21 juillet 2026). Autrement dit,
   AUCUN Firefox livre ne connait la propriete par defaut aujourd'hui. MDN
   classe toujours la fonctionnalite « Limited availability — not Baseline ».
   Couverture mondiale mesuree : 85,4 % (caniuse).

   DECISION : CSS seul, aucun polyfill JavaScript. Trois raisons.

   1. Ce que Firefox perd est de la decoration, pas la demonstration. La
      sequence produit du §5.1 — pastille de point d'arret, ligne illuminee,
      panneau de variables — est en @keyframes temporelles : elle fonctionne
      partout. Seule la revelation au defilement tombe.
   2. Le polyfill violerait le budget qu'il pretend servir : il pese a lui seul
      plus que les 15 Ko du §8.1, qui interdit par ailleurs toute dependance
      tierce.
   3. L'ecart se referme avant l'ouverture : le 15 septembre 2026 tombe pendant
      W3-W4-W10. Un polyfill ecrit maintenant serait du code mort a la sortie.

   ⚠️ A rejouer au W5, avant le Lighthouse du hero : si Firefox 156 glisse, la
   conclusion ne change pas, mais la date de refermeture, si.

   -----------------------------------------------------------------------------
   🔴 RELEVE REJOUE — 11 aout 2026 (W5-b)

   La ligne ci-dessus annoncait Firefox 156 au 15 septembre 2026. CE POINT NE SE
   CONFIRME PAS, et l'ecart se creuse plutot qu'il ne se referme :

     • La fonctionnalite est IMPLEMENTEE dans Firefox depuis la 132, mais elle
       reste derriere la preference `layout.css.scroll-driven-animations.enabled`
       en canal stable — active par defaut en Nightly seulement. Autrement dit
       elle n'est pas « pas encore sortie », elle est SORTIE DESACTIVEE, ce qui
       est un etat plus durable : rien n'oblige a le lever a une version donnee.
     • Couverture mondiale relevee : ~84 % (contre 85,4 % au 10 aout — l'ordre de
       grandeur est le meme, l'ecart n'est pas significatif).
     • Sources consultees ce jour : MDN (« Experimental features in Firefox »,
       page `animation-timeline`) et agregateurs de type caniuse.

   ⚠️ CE RELEVE EST INDIRECT. Il vient de pages de documentation et non d'un essai
   sur un Firefox stable installe. La date du 15 septembre etait deja une
   prevision ; ne pas la remplacer par une autre prevision. Le seul relevé qui
   trancherait est un about:config sur un Firefox stable reel — un quart d'heure,
   a faire avant le Lighthouse du hero.

   DECISION INCHANGEE, et pour la premiere des trois raisons ci-dessus : ce que
   Firefox perd est de la DECORATION, pas la demonstration. La sequence produit
   est en @keyframes temporelles et fonctionne partout ; seule la revelation au
   defilement tombe, et l'etat final reste visible. Le troisieme argument, lui,
   ne tient plus — l'ecart ne se refermera pas necessairement avant J1 — ce qui
   renforce le refus du polyfill au lieu de l'affaiblir : un polyfill ecrit pour
   un ecart durable serait du code a maintenir, pas du code mort.

   -----------------------------------------------------------------------------
   🔴 RELEVE MESURE — 13 aout 2026 (W12-i). LE PREMIER QUI NE SOIT PAS INDIRECT.

   Les deux releves ci-dessus viennent de pages de documentation. Celui-ci vient
   du moteur. Firefox 153.0.3 installe sur le poste, pilote en WebDriver BiDi
   (Firefox n'expose plus CDP), profil neuf, pages servies par le profil `dev`.

     CSS.supports('animation-timeline: view()')   →   FALSE

   Le releve du 11 aout est donc confirme par Gecko lui-meme : la propriete est
   bien absente du canal stable. Et surtout, la CONSEQUENCE est mesuree, ce qui
   n'avait jamais ete fait :

     .reveal, .trio > li, .duo > *  →  opacity 1 sur /fr/, /fonctionnalites,
     /tarifs. ZERO element sous 0,01. Le @supports est dans le bon sens, et on
     le sait maintenant autrement qu'en le relisant.

   ⚠️ Ce que Firefox perd est bien de la DECORATION : `.seq > *` et le cadre de
   debogueur, en @keyframes temporelles, sont mesures a 1 comme sous Blink.

   Le rendu lui-meme ne differe pas : 22 rendus (11 pages publiques x 1280 et
   375 px), ecart de hauteur maximal 4 px, zero debordement horizontal, couleurs
   calculees identiques jusqu'a la cinquieme decimale d'une sonde oklab.

   ⚠️ CE QUI RESTE INDIRECT : la part de marche. Aucun releve local ne la donne.

   Rejouable : outils/banc (LISEZMOI.md). */

/* --------------------------------------------------------------------------
   🔴 LE @supports EST DANS CE SENS-LA, ET PAS DANS L'AUTRE.

   L'etat final est visible PAR DEFAUT. Le masquage n'existe qu'a l'interieur du
   @supports, donc uniquement chez qui sait animer.

   Ecrit a l'envers — masquer par defaut, reveler si supporte — la page
   s'afficherait VIDE chez les 15 % qui n'ont pas la propriete. Le contenu
   serait bien dans le HTML, donc indexable, mais illisible par un humain : le
   pire des deux mondes. Et le defaut est invisible sur le poste de
   l'integrateur, qui est sous Chrome.

   Un test unitaire lit ce fichier et echoue si `.reveal` apparait hors du bloc.
   -------------------------------------------------------------------------- */

/* 🔴 LA PLAGE SE TERMINE DANS `entry`, ET SUREMENT PAS DANS `cover` — corrige au
   W12-c, apres mesure.

   `entry 10% cover 30%` etait la valeur d'origine, et elle porte un defaut que
   seule une mesure revele : la phase `cover` court jusqu'a ce que l'element ait
   entierement TRAVERSE la fenetre. Un bloc deja visible au chargement — ce qui
   arrive des qu'un ecran est haut — s'y trouve a 30 % d'opacite et le RESTE tant
   que le visiteur n'a pas defile. Il ne voit pas une apparition : il voit du
   texte delave. Releve par Lighthouse sur /tarifs (2,74:1 sur cinq noeuds).
   La phase `entry`, elle, vaut 100 % des qu'un element est entierement dans la
   fenetre : au chargement, tout ce qui est visible est donc DEJA revele. */
@supports (animation-timeline: view()) {
  .reveal {
    opacity: 0;
    transform: translateY(12px);
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 75%;
  }
}

/* L'etat masque vit DANS l'image-cle, pas dans une regle statique. Avec un
   remplissage `both`, il ne s'applique donc qu'a partir du moment ou le
   navigateur a decide de jouer l'animation. Si rien ne se joue, l'element reste
   a son etat naturel : visible. C'est ce qui permet a `opacity: 0` de ne
   figurer NULLE PART hors d'un @supports dans ce fichier — un test le verifie. */
@keyframes reveal {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* Apparitions decalees : le retard se pose sur la plage, pas sur un delai —
   une animation pilotee par le defilement n'a pas de temps a retarder. */
@supports (animation-timeline: view()) {
  .reveal--late { animation-range: entry 25% entry 95%; }
}

/* --------------------------------------------------------------------------
   LE DECALAGE DES GRILLES — W12-c

   Les trois reperes de la dalle et les deux cartes du duo entrent l'un apres
   l'autre, et pas ensemble. Trois blocs qui paraissent au meme instant se
   lisent comme un seul bloc ; decales, l'oeil les compte.

   ⚠️ Le decalage se pose sur la PLAGE et jamais sur `animation-delay` : sous
   `animation-timeline: view()` le temps n'avance plus, c'est le defilement qui
   avance. Un delai de 100 ms n'y produirait rigoureusement rien — l'erreur ne
   se voit pas en relisant, seulement en defilant.

   🔴 ET LES TROIS PLAGES SE TERMINENT DANS `entry`, pour la raison ecrite plus
   haut : une plage qui finit dans `cover` laisse delave tout ce qui est deja
   visible au chargement. Les trois reperes de la dalle sont precisement dans ce
   cas sur un ecran haut. Le decalage tient donc a l'interieur d'une seule phase
   — 0-70 %, 15-85 %, 30-100 % — et non en repoussant la fin toujours plus loin.

   ⚠️ Ces regles ne sont pas `.reveal` : la classe est posee sur le CONTENEUR de
   la section, et un conteneur qui se revele emmene ses enfants d'un bloc. Le
   decalage doit donc viser les enfants, ce qui suppose que le conteneur, lui,
   ne porte pas `.reveal` — sinon les deux animations se superposent.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  .trio > li,
  .duo > * {
    opacity: 0;
    transform: translateY(12px);
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 70%;
  }

  .trio > li:nth-child(2),
  .duo > :nth-child(2) { animation-range: entry 15% entry 85%; }

  .trio > li:nth-child(3) { animation-range: entry 30% entry 100%; }
}

/* --------------------------------------------------------------------------
   Les regles non negociables du §5.1

   • Le h1 du hero n'est JAMAIS anime : c'est l'element LCP, et l'animer en
     fondu retarde la mesure d'autant. Aucune classe de ce fichier ne doit
     l'atteindre — c'est pourquoi il n'existe ici aucun selecteur de type.
   • `transform` et `opacity` uniquement : seules proprietes composees par le
     GPU. Animer width, top ou box-shadow declenche une remise en page a
     chaque image.
   • Aucune animation ne deplace la mise en page : le CLS est un Core Web Vital.
   • ≤ 400 ms, une seule courbe. Au-dela, l'interface parait lente au lieu de
     paraitre soignee.
   -------------------------------------------------------------------------- */

.fade-in {
  animation: reveal var(--dur) var(--ease) both;
}

/* Sequence produit du hero : @keyframes temporelles, donc supportees partout.
   C'est l'animation qui vend — elle ne depend d'aucune propriete recente, et
   c'est pour cela que Firefox ne perd que la decoration. */
.seq > * {
  animation: reveal var(--dur) var(--ease) both;
}
.seq > :nth-child(1) { animation-delay:   0ms; }
.seq > :nth-child(2) { animation-delay: 180ms; }
.seq > :nth-child(3) { animation-delay: 360ms; }

/* --------------------------------------------------------------------------
   LA SEQUENCE DU CADRE DE DEBOGUEUR — W12-b

   C'est l'animation annoncee par le §5.1 : « pastille de point d'arret, ligne
   illuminee, panneau de variables ». Elle est en @keyframes TEMPORELLES, donc
   elle se joue partout, Firefox compris — c'est exactement pour cela que le
   releve ci-dessus conclut que Firefox ne perd que de la decoration.

   🔴 ELLE NE BOUCLE PAS, et c'est un choix. Une boucle infinie a cote d'un titre
   qu'on essaie de lire est une distraction, elle empeche toute lecture du code
   montre, et elle tient le processeur eveille sur la seule page ou le visiteur
   s'attarde. La sequence raconte l'arret une fois — 1,4 s en tout — puis se
   tait. Quand la vraie boucle video arrivera, ce cadre disparaitra avec elle.

   ⚠️ Chaque etape reste sous les 400 ms du §5.1 : ce sont les DELAIS qui
   s'additionnent, pas les durees. Et une seule courbe, --ease, comme partout.

   ⚠️ Aucun de ces elements n'est le LCP : le h1 fait 76 px et paraît d'emblee.
   Aucun ne change la mise en page non plus — le cadre a un `aspect-ratio` fixe
   et ses lignes sont posees avant que la sequence ne commence, donc CLS a zero.
   -------------------------------------------------------------------------- */

/* 1. La pastille apparait. C'est LE geste a comprendre : un point d'arret se
      pose dans la gouttiere. Elle grossit depuis rien, comme au clic. */
.ide__arret { animation: ide-arret 220ms var(--ease) both; }

/* 2. La ligne s'illumine — l'execution vient de s'arreter la. */
.ide__ligne--courante::before { animation: ide-fondu 280ms var(--ease) 340ms both; }

/* 3. Le panneau des variables se remplit, ligne a ligne. C'est la reponse a la
      question que le visiteur se pose : « et je vois quoi, une fois arrete ? » */
.ide__panneau-titre { animation: reveal var(--dur) var(--ease) 620ms both; }

.ide__var { animation: reveal var(--dur) var(--ease) both; }
.ide__var:nth-child(1) { animation-delay:  740ms; }
.ide__var:nth-child(2) { animation-delay:  840ms; }
.ide__var:nth-child(3) { animation-delay:  940ms; }
.ide__var:nth-child(4) { animation-delay: 1040ms; }

/* 4. La barre d'etat nomme ce qui vient de se passer, en toutes lettres. */
.ide__statut { animation: ide-fondu var(--dur) var(--ease) 1180ms both; }

@keyframes ide-arret {
  from { opacity: 0; transform: scale(.2); }
  to   { opacity: 1; transform: none; }
}

@keyframes ide-fondu {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* --------------------------------------------------------------------------
   prefers-reduced-motion (CHARTE §7) — il s'applique en premier, et il doit
   neutraliser aussi bien le defilement revelateur que les sequences temporelles.
   Etat final visible, immediatement, sans exception.

   🔴 MESURE — 13 aout 2026 (W12-i). Ce bloc n'avait jamais ete OBSERVE : le
   test unitaire prouvait l'ordre de cascade, pas l'affichage, et le poste
   d'essai n'avait pas la preference active. Il l'a ete sous les deux moteurs —
   Chrome par --force-prefers-reduced-motion, Firefox par ui.prefersReducedMotion
   dans le profil. Les dix groupes touches ici sont mesures a opacity 1, zero
   element invisible, sur les trois pages qui portent du mouvement.

   ⚠️ Un test derive verifie desormais que TOUT selecteur masque statiquement
   dans un @supports est nomme ici. La liste ecrite a la main protegeait les
   classes qu'on avait pensees ; elle ne protegeait pas la suivante. */

@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal--late, .fade-in, .seq > * {
    opacity: 1;
    transform: none;
    animation: none;
  }

  /* 🔴 La sequence du cadre de debogueur en fait partie, et il faut la nommer :
     la regle universelle plus bas ramene les durees a 1 ms mais ne supprime pas
     les DELAIS — la pastille attendrait encore une seconde avant de paraitre,
     ce qui est precisement le clignotement que la preference veut eviter. */
  .ide__arret, .ide__ligne--courante::before, .ide__panneau-titre,
  .ide__var, .ide__statut {
    opacity: 1;
    transform: none;
    animation: none;
  }

  /* 🔴 Les grilles du W12-c aussi, et pour une raison DIFFERENTE des sequences
     ci-dessus : leur `opacity: 0` est une regle statique du @supports, pas une
     image-cle. Ramener la duree a 1 ms ne la leve pas — les trois reperes de la
     dalle resteraient invisibles pour de bon chez qui demande moins de
     mouvement, ce qui est l'exact contraire de ce que la preference demande. */
  .trio > li, .duo > * {
    opacity: 1;
    transform: none;
    animation: none;
  }

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