/* ============================================================
   Motionary, demos F
   Collection Code, plateforme web et outillage
   ============================================================ */

.tsc .lane,.rmp .lane,.spc2 .lane{display:flex;align-items:center;gap:8px}
.tsc .lb,.rmp .lb,.spc2 .lb{font:600 9px/1 var(--mono);color:var(--ink-3);width:44px;text-align:right;font-style:normal;letter-spacing:.04em}
.tsc .tk,.rmp .tk,.spc2 .tk{position:relative;display:block;width:76px;height:7px;border-radius:4px;background:var(--s2)}
.tsc .tk b,.spc2 .tk b{position:absolute;left:0;top:-1px;width:9px;height:9px;border-radius:5px;background:var(--lime);display:block}

/* ---------------- transitions de vue natives ---------------- */
/* Le nom SORT un element de l instantane racine. Les deux barres du
   fond sont non etiquetees, elles appartiennent donc a une seule image
   qui se croise en fondu, elles ne se deplacent pas et ne sont pas
   suivies individuellement. L element etiquete, lui, a sa propre paire
   et voyage seul.
   C est ce contraste qui est le terme. La version precedente montrait un
   rectangle qui volait entre deux cadres, ce qui ne disait pas ce que le
   nom change.
   Les limites sont interpolees, gauche haut largeur hauteur. Un scale de
   1.82 sur 1.69, ce qu utilisait la version precedente, ecrase le
   contenu et fausse le rayon.
   Gele, on verrait un element sur une page. */
.vtn{position:relative;width:146px;height:98px;border-radius:10px;overflow:hidden;background:var(--s0)}
.vtn .root{position:absolute;inset:0}
.vtn .ra,.vtn .rb{position:absolute;left:9px;right:9px;display:flex;flex-direction:column;gap:8px}
.vtn .ra{top:9px;animation:aVtnRa 4.2s cubic-bezier(.2,0,0,1) infinite}
.vtn .rb{top:34px;animation:aVtnRb 4.2s cubic-bezier(.2,0,0,1) infinite}
.vtn .ra i,.vtn .rb i{display:block;height:7px;border-radius:3px;background:var(--s3)}
.vtn .ra i:last-child{width:56%}
.vtn .rb i:last-child{width:44%}
.vtn .slot{position:absolute;display:block;border-radius:8px;border:1px dashed var(--s5);
  box-sizing:border-box}
.vtn .slot.a{left:9px;bottom:9px;width:38px;height:26px}
.vtn .slot.b{right:9px;top:30px;width:64px;height:46px}
.vtn .el{position:absolute;left:9px;top:63px;width:38px;height:26px;border-radius:8px;
  background:var(--lime);z-index:4;animation:aVtn 4.2s cubic-bezier(.2,.9,.25,1) infinite}
.vtn .el em{position:absolute;left:0;top:-12px;font:600 8px/1 var(--mono);color:var(--lime);
  font-style:normal}
/* ancrage A a 9 et 63 en 38 sur 26, ancrage B a 73 et 30 en 64 sur 46 */
@keyframes aVtn{0%,12%{left:9px;top:63px;width:38px;height:26px}
  50%,80%{left:73px;top:30px;width:64px;height:46px}
  96%,100%{left:9px;top:63px;width:38px;height:26px}}
/* non etiquete, donc une seule image qui se croise, sans deplacement */
@keyframes aVtnRa{0%,16%{opacity:1}50%,78%{opacity:0}96%,100%{opacity:1}}
@keyframes aVtnRb{0%,16%{opacity:0}50%,78%{opacity:1}96%,100%{opacity:0}}

/* Les QUATRE niveaux, puisque la definition en nomme quatre. La version
   precedente en montrait deux, donc elle ne pouvait pas dire lequel
   possede quelle propriete, ce qui est justement la seule raison de
   connaitre l arbre.
   L imbrication fait le travail. Seul le GROUPE anime sa position et sa
   taille, la paire et les instantanes suivent parce qu ils sont poses en
   retrait dedans. L ancien et le nouveau n animent que leur opacite et
   une petite transformation, jamais leur taille, ce sont des images.
   Le niveau exterieur est pose sur la RACINE, il ne bouge pas, et c est
   pour cela que les selecteurs partent de la racine et non de l element
   etiquete, qui ne peint plus pendant la transition.
   Le groupe passe de 140 sur 96 a 108 sur 64. La paire garde 20 de
   retrait horizontal et 26 de vertical, elle fait donc 88 sur 38 au plus
   petit, et les instantanes 72 sur 16, encore lisibles.
   Gele, on verrait un arbre, ce qui est deja la moitie de l information,
   mais pas quel niveau porte le mouvement. */
.vtp{position:relative;width:170px;height:134px}
.vtp .l1{position:absolute;inset:0;border:1px dashed var(--s5);border-radius:10px;box-sizing:border-box}
.vtp em{position:absolute;left:7px;top:4px;font:600 6px/1 var(--mono);font-style:normal;
  letter-spacing:.02em;white-space:nowrap}
.vtp .l1>em{color:var(--ink-3)}
.vtp .grp{position:absolute;left:14px;top:18px;width:140px;height:96px;box-sizing:border-box;
  border:1px dashed rgba(var(--accent-rgb),.6);border-radius:9px;
  animation:aVtpGrp 3.8s cubic-bezier(.3,.9,.3,1) infinite}
.vtp .grp>em{color:var(--lime)}
.vtp .pair{position:absolute;left:10px;top:16px;right:10px;bottom:10px;box-sizing:border-box;
  border:1px dashed var(--s4);border-radius:7px}
.vtp .pair>em{color:var(--ink-3)}
.vtp .old,.vtp .new{position:absolute;left:8px;top:14px;right:8px;bottom:8px;display:block;
  border-radius:5px}
.vtp .old{background:var(--s4);animation:aVtpOld 3.8s ease-in-out infinite}
.vtp .new{background:var(--lime);animation:aVtpNew 3.8s ease-in-out infinite}
/* seul le groupe anime sa position et sa taille */
@keyframes aVtpGrp{0%,14%{left:14px;top:18px;width:140px;height:96px}
  54%,84%{left:30px;top:34px;width:108px;height:64px}
  98%,100%{left:14px;top:18px;width:140px;height:96px}}
/* les instantanes n animent que leur opacite et une transformation */
@keyframes aVtpOld{0%,14%{opacity:1;transform:scale(1)}
  54%,84%{opacity:0;transform:scale(1.04)}
  98%,100%{opacity:1;transform:scale(1)}}
@keyframes aVtpNew{0%,14%{opacity:0;transform:scale(.96)}
  54%,84%{opacity:1;transform:scale(1)}
  98%,100%{opacity:0;transform:scale(.96)}}

/* DEUX DOCUMENTS, et les deux portent la regle. C est ce qui manquait,
   une transition dans un seul document donne exactement la meme image.
   Les deux pastilles du bas ont leur badge allume, parce qu un seul cote
   qui consent donne une coupure nette sans le moindre message d erreur.
   Le document sortant s eteint apres l echange, il n existe plus, et
   c est pour cela que tout ce qui style la transition doit vivre dans le
   document entrant.
   L en-tete est nomme dans les deux fichiers, il PERSISTE donc par
   dessus la frontiere, sans bouger ni se fondre, pendant que les corps
   s echangent. C est la seule raison de faire cela sur un site
   multipage.
   Gele, on verrait une page avec un en-tete. */
.cdv{position:relative;width:158px;height:106px;display:flex;flex-direction:column;gap:8px}
.cdv .frame{position:relative;height:74px;border-radius:10px;overflow:hidden;background:var(--s0);
  flex:0 0 auto}
/* nomme dans les deux documents, il ne recoit aucune animation */
.cdv .hdr{position:absolute;left:0;right:0;top:0;height:20px;background:var(--lime);z-index:3;
  display:flex;align-items:center;padding:0 9px;box-sizing:border-box}
.cdv .hdr i{display:block;width:34px;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.7)}
.cdv .pg{position:absolute;left:0;right:0;top:20px;bottom:0;padding:9px;box-sizing:border-box;
  display:flex;flex-direction:column;gap:8px}
.cdv .pg i{display:block;height:7px;border-radius:3px}
.cdv .pa{background:var(--s2);animation:aCdvA 4.6s cubic-bezier(.2,0,0,1) infinite}
.cdv .pa i{background:var(--s5)}
.cdv .pb{background:var(--s1);animation:aCdvB 4.6s cubic-bezier(.2,0,0,1) infinite}
.cdv .pb i{background:var(--s4)}
.cdv .pg i.s{width:52%}
.cdv .docs{display:flex;gap:8px;flex:1}
.cdv .doc{display:flex;align-items:center;justify-content:space-between;flex:1;padding:0 7px;
  border-radius:6px;box-sizing:border-box;background:var(--s1)}
.cdv .doc em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
/* le badge est la regle @view-transition, allumee des deux cotes */
.cdv .doc b{display:block;width:7px;height:7px;border-radius:2px;background:var(--lime)}
.cdv .da{animation:aCdvDa 4.6s steps(1,end) infinite}
.cdv .db{animation:aCdvDb 4.6s steps(1,end) infinite}
@keyframes aCdvA{0%,16%{transform:translateX(0)}40%,72%{transform:translateX(-100%)}
  96%,100%{transform:translateX(0)}}
@keyframes aCdvB{0%,16%{transform:translateX(100%)}40%,72%{transform:translateX(0)}
  96%,100%{transform:translateX(100%)}}
/* le sortant s eteint, il n existe plus apres l echange */
@keyframes aCdvDa{0%,39%{background:var(--s3)}40%,95%{background:var(--s1)}
  96%,100%{background:var(--s3)}}
@keyframes aCdvDb{0%,39%{background:var(--s1)}40%,95%{background:var(--s3)}
  96%,100%{background:var(--s1)}}


/* Le type est un SELECTEUR, pas une animation. La meme paire de pages
   recoit donc deux traitements de nature differente, un glissement quand
   le type est forward et un agrandissement en fondu quand il est back.
   La version precedente faisait glisser une page dans un sens puis dans
   l autre, ce qui montre une direction et non le fait qu une regle CSS a
   ete choisie.
   L etiquette s allume AVANT que quoi que ce soit bouge, a 8 pour cent
   pour une animation qui part a 12. La cause precede l effet.
   Un nom identifie un element, une classe un ensemble d elements, un
   type identifie la navigation.
   Gele, on verrait une page, sans savoir par quel chemin on y est
   arrive, qui est exactement l information que le type porte. */
.vtt{position:relative;width:140px;height:96px;display:flex;flex-direction:column;gap:9px}
.vtt .types{display:flex;gap:7px;flex:0 0 auto}
.vtt .types em{font:600 7px/1 var(--mono);font-style:normal;padding:3px 6px;border-radius:4px;
  background:var(--s1);color:var(--ink-3)}
.vtt .fwd{animation:aVttFwd 5.6s steps(1,end) infinite}
.vtt .bwd{animation:aVttBwd 5.6s steps(1,end) infinite}
.vtt .stg{position:relative;flex:1;border-radius:9px;overflow:hidden;background:var(--s0)}
.vtt .pg{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;gap:9px;
  padding:0 12px;box-sizing:border-box;border-radius:9px}
.vtt .pg i{display:block;height:8px;border-radius:4px}
.vtt .pg i.s{width:54%}
.vtt .pa{background:var(--s2);animation:aVttA 5.6s cubic-bezier(.2,0,0,1) infinite}
.vtt .pa i{background:var(--s5)}
.vtt .pb{background:var(--lime);animation:aVttB 5.6s cubic-bezier(.2,0,0,1) infinite}
.vtt .pb i{background:rgba(var(--on-accent-rgb),.5)}
/* forward, les deux glissent. back, l entrante grandit depuis 0.9 et la
   sortante ne fait que se fondre. Deux animations, un seul couple. */
@keyframes aVttA{0%,12%{transform:translateX(0) scale(1);opacity:1}
  34%,52%{transform:translateX(-30px) scale(1);opacity:0}
  56%{transform:translateX(0) scale(.9);opacity:0}
  78%,100%{transform:translateX(0) scale(1);opacity:1}}
@keyframes aVttB{0%,12%{transform:translateX(30px);opacity:0}
  34%,56%{transform:translateX(0);opacity:1}
  74%{transform:translateX(0);opacity:0}
  76%,100%{transform:translateX(30px);opacity:0}}
@keyframes aVttFwd{0%,7%{background:var(--s1);color:var(--ink-3)}
  8%,50%{background:rgba(var(--accent-rgb),.22);color:var(--lime)}
  51%,100%{background:var(--s1);color:var(--ink-3)}}
@keyframes aVttBwd{0%,51%{background:var(--s1);color:var(--ink-3)}
  52%,94%{background:rgba(var(--accent-rgb),.22);color:var(--lime)}
  95%,100%{background:var(--s1);color:var(--ink-3)}}

/* Six NOMS et UNE classe. Chaque carte porte son etiquette c1 a c6,
   parce que la classe ne leve pas la contrainte d unicite du nom, et le
   crochet du bas montre la seule regle qui les atteint toutes.
   La version precedente montrait six tuiles en cascade, ce qui ne se
   distinguait pas d une classe CSS ordinaire. Ce sont les deux
   etiquetages simultanes qui sont le terme.
   Trois colonnes de 40 avec 7 de gouttiere font 134 dans un interieur de
   136, et deux rangees de 30 avec la puce de classe font 87 pour 88.
   Gele, on verrait six cartes numerotees, ce qui est deja la moitie de
   l information, mais pas la cascade qui vient d une seule regle. */
.vtc{width:152px;height:104px;padding:8px;box-sizing:border-box;display:flex;flex-direction:column;
  gap:8px}
.vtc .gri{display:grid;grid-template-columns:repeat(3,40px);grid-template-rows:repeat(2,30px);gap:7px}
.vtc .cd{position:relative;display:block;border-radius:7px;background:var(--lime);
  animation:aVtc 2.8s cubic-bezier(.2,.9,.25,1) infinite}
.vtc .cd em{position:absolute;left:4px;top:3px;font:600 6px/1 var(--mono);font-style:normal;
  color:var(--on-accent);opacity:.8}
.vtc .cd:nth-child(2){animation-delay:.06s}
.vtc .cd:nth-child(3){animation-delay:.12s}
.vtc .cd:nth-child(4){animation-delay:.18s}
.vtc .cd:nth-child(5){animation-delay:.24s}
.vtc .cd:nth-child(6){animation-delay:.3s}
.vtc .cls{position:relative;display:flex;align-items:center;gap:6px;height:12px;flex:0 0 auto}
.vtc .brk{display:block;width:100px;height:5px;border-radius:0 0 4px 4px;box-sizing:border-box;
  border:1px solid var(--s4);border-top:none}
.vtc .cls em{font:600 7px/1 var(--mono);font-style:normal;color:var(--ink-3)}
@keyframes aVtc{0%,10%{transform:translateY(8px);opacity:.25}
  36%,76%{transform:translateY(0);opacity:1}
  96%,100%{transform:translateY(8px);opacity:.25}}

/* ---------------- scroll pilote ---------------- */
/* Les ANCRES sont ce qui distingue cette timeline de la suivante. Zero
   est le conteneur en haut de sa course, cent pour cent en bas, et les
   deux reperes du document s allument quand on les atteint. Aucun
   element particulier n est mesure, c est la boite qui defile.
   Le document fait 170 dans une fenetre de 78, la course utile vaut donc
   92, et la barre est exactement proportionnelle a cette course.
   Le retour est un vrai defilement vers le haut, la barre redescend donc
   avec lui, ce qui est le comportement reel et evite tout rembobinage.
   Gele, on verrait une barre a moitie pleine, sans savoir de quoi elle
   est la moitie. */
.spt2{position:relative;width:126px;height:94px;border-radius:9px;overflow:hidden;background:var(--s0)}
.spt2 .bar{position:absolute;left:0;right:0;top:0;height:6px;background:var(--s2);z-index:3}
.spt2 .fill{position:absolute;inset:0;display:block;background:var(--lime);transform-origin:left;
  animation:aSpt2Bar 5s linear infinite}
.spt2 .bar b{position:absolute;top:-2px;display:block;width:1px;height:10px;background:var(--ink-3)}
.spt2 .t0{left:0}
.spt2 .t1{right:0}
.spt2 .vp{position:absolute;left:9px;right:9px;top:14px;bottom:2px;overflow:hidden}
.spt2 .doc{position:absolute;left:0;right:0;top:0;height:170px;display:flex;flex-direction:column;
  gap:8px;padding:6px 0;box-sizing:border-box;animation:aSpt2Doc 5s linear infinite}
.spt2 .doc .ln{display:block;height:8px;border-radius:4px;background:var(--s3);flex:0 0 auto}
.spt2 .doc .edge{display:block;height:3px;border-radius:2px;background:var(--s2);flex:0 0 auto;
  margin:0 20px}
.spt2 .doc .tpp2{animation:aSpt2Top 5s linear infinite}
.spt2 .doc .bot{animation:aSpt2Bot 5s linear infinite}
/* course de 92, aller de 8 a 48 pour cent puis retour de 56 a 96 */
@keyframes aSpt2Bar{0%,8%{transform:scaleX(.02)}48%,56%{transform:scaleX(1)}96%,100%{transform:scaleX(.02)}}
@keyframes aSpt2Doc{0%,8%{transform:translateY(0)}48%,56%{transform:translateY(-92px)}
  96%,100%{transform:translateY(0)}}
@keyframes aSpt2Top{0%,10%{background:var(--lime)}14%,92%{background:var(--s2)}
  96%,100%{background:var(--lime)}}
@keyframes aSpt2Bot{0%,44%{background:var(--s2)}48%,56%{background:var(--lime)}
  60%,100%{background:var(--s2)}}

/* DEUX cartes, deux horloges. C est ce qu une timeline de defilement ne
   sait pas exprimer, et c est donc la seule facon de separer les deux
   termes. La premiere fait son passage tot, la seconde plus tard, parce
   qu elles ne traversent pas le scrollport au meme moment.
   Les deux traits nommes sont les ancres, entry en bas ou le sujet
   commence a devenir visible, exit en haut ou il a fini de sortir.
   La colonne fait 210 dans une fenetre de 96, la course utile vaut donc
   114. La carte du haut est a 90, elle traverse de 0 a -114. Celle du bas
   est a 150, elle n entre qu a -54, soit 29 pour cent du cycle.
   Gele, on verrait deux cartes, sans savoir qu elles sont sur deux
   horloges independantes. */
.vpt{position:relative;width:136px;height:96px;border-radius:9px;overflow:hidden;background:var(--s0)}
.vpt .edge{position:absolute;left:0;right:0;display:block;height:1px;z-index:3;
  background:repeating-linear-gradient(90deg,rgba(var(--accent-rgb),.5) 0 3px,transparent 3px 6px)}
.vpt .en{bottom:16px}
.vpt .ex{top:16px}
.vpt em{position:absolute;right:5px;z-index:4;font:600 6px/1 var(--mono);font-style:normal;
  color:var(--ink-3)}
.vpt .le{bottom:19px}
.vpt .lx{top:8px}
.vpt .col{position:absolute;left:12px;right:12px;top:0;height:210px;
  animation:aVptCol 5.4s linear infinite}
.vpt .cd{position:absolute;left:0;right:0;display:block;height:34px;border-radius:8px;
  background:var(--lime)}
.vpt .c1{top:90px;animation:aVptC1 5.4s cubic-bezier(.2,.9,.25,1) infinite}
.vpt .c2{top:150px;background:var(--a2);animation:aVptC2 5.4s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aVptCol{0%,8%{transform:translateY(0)}52%,60%{transform:translateY(-114px)}
  96%,100%{transform:translateY(0)}}
/* la premiere fait son entree tot, entre 8 et 24 pour cent */
@keyframes aVptC1{0%,8%{transform:scale(.9);opacity:.3}
  24%,80%{transform:scale(1);opacity:1}
  96%,100%{transform:scale(.9);opacity:.3}}
/* la seconde n entre qu a 30, elle est sur sa propre horloge */
@keyframes aVptC2{0%,30%{transform:scale(.9);opacity:.3}
  46%,74%{transform:scale(1);opacity:1}
  92%,100%{transform:scale(.9);opacity:.3}}

/* Une TRANCHE, pas une timeline. La tete de lecture parcourt toute la
   regle, du debut a la fin du passage, mais la carte de droite ne change
   que pendant la bande entry, et elle RETIENT ensuite ses valeurs de fin.
   C est ce contraste qui dit qu une plage decoupe et ne mesure rien.
   Les bandes sont de la geometrie, pas une preference. Pour un element de
   34 dans un scrollport de 88, cover vaut 122, entry et exit valent 34
   chacun soit 27.9 pour cent, et contain vaut les 44.2 qui restent. La
   version precedente ecrivait 32, 36 et 32, qui ne correspondent a
   aucune geometrie.
   Les deux traits dans la bande entry sont ses 0 et 100 pour cent, parce
   que les pourcentages d une plage nommee sont relatifs a cette plage.
   La regle fait 84 de haut et la tete 2, elle parcourt donc 82, et la bande entry
   se termine a 23.4, soit 30 pour cent du cycle.
   Gele, on verrait une regle a trois bandes et une carte. */
.arg{position:relative;width:142px;height:96px;display:flex;align-items:center;gap:14px}
.arg .ruler{position:relative;display:block;width:18px;height:84px;border-radius:5px;overflow:hidden;
  background:var(--s1);flex:0 0 auto}
.arg .ruler i{position:absolute;left:0;right:0;display:block}
.arg .entry{top:0;height:27.9%;background:rgba(var(--accent-rgb),.35)}
.arg .contain{top:27.9%;height:44.2%;background:var(--s2)}
.arg .exit{bottom:0;height:27.9%;background:var(--s1)}
.arg .ruler b{position:absolute;left:0;right:0;display:block;height:1px;background:var(--ink-3)}
.arg .e0{top:0}
.arg .e1{top:23.4px}
.arg .hdd{top:0;height:2px;background:var(--ink);z-index:2;
  animation:aArgHead 4.4s linear infinite}
.arg .side{display:flex;flex-direction:column;gap:9px;flex:1}
.arg .lb{font:600 7px/1 var(--mono);font-style:normal;color:var(--lime)}
.arg .cd{display:block;height:34px;border-radius:8px;background:var(--lime);
  animation:aArgCard 4.4s cubic-bezier(.2,.9,.25,1) infinite}
/* la tete descend puis remonte, 82 de course dans les deux sens, donc
   aucun rembobinage. La bande entry finit a 27.9 pour cent de la course,
   soit 18 pour cent du cycle a l aller et 85 au retour. */
@keyframes aArgHead{0%,6%{transform:translateY(0)}50%,56%{transform:translateY(82px)}
  96%,100%{transform:translateY(0)}}
/* la carte ne change que pendant entry et RETIENT ensuite, de 18 a 85
   pour cent, pendant que la tete continue de parcourir contain et exit */
@keyframes aArgCard{0%,6%{transform:translateY(10px) scale(.94);opacity:.25}
  18%,85%{transform:translateY(0) scale(1);opacity:1}
  96%,100%{transform:translateY(10px) scale(.94);opacity:.25}}

/* DEUX etats interroges, puisque la definition en nomme plusieurs. La
   pastille dit lequel repond, scrollable tant qu il reste de la course,
   stuck quand l en-tete est colle. Une seule reponse se lirait comme un
   declenchement au defilement et pas comme une requete sur un etat.
   L indice vers le bas ne vit que pendant scrollable, ce qui est
   exactement l etat qu un ecouteur de defilement suit le plus mal.
   Le document fait 170 dans une fenetre de 66, la course utile vaut donc
   104, et le palier de 70 reste bien en dessous.
   Gele, on verrait un en-tete fin avec une ombre, sans savoir de quel
   etat elle est la reponse. */
.sst{position:relative;width:136px;height:96px;border-radius:9px;overflow:hidden;background:var(--s0)}
.sst .hdr{position:absolute;left:0;right:0;top:0;height:24px;background:var(--lime);z-index:3;
  display:flex;align-items:center;padding:0 10px;box-sizing:border-box;
  animation:aSstHdr 5.2s cubic-bezier(.3,.9,.3,1) infinite}
.sst .hdr i{display:block;width:38px;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.7)}
.sst .vp{position:absolute;left:9px;right:9px;top:30px;bottom:0;overflow:hidden}
.sst .doc{position:absolute;left:0;right:0;top:0;height:170px;display:flex;flex-direction:column;
  gap:12px;animation:aSstDoc 5.2s cubic-bezier(.3,.9,.3,1) infinite}
.sst .doc i{display:block;height:8px;border-radius:4px;background:var(--s3);flex:0 0 auto}
.sst .badge{position:absolute;right:7px;top:30px;z-index:4;font-style:normal}
.sst .badge b{position:absolute;right:0;top:0;display:block;white-space:nowrap;font:600 6px/1 var(--mono);
  padding:3px 5px;border-radius:4px;background:rgba(var(--void-rgb),.45);color:var(--ink)}
.sst .b1{animation:aSstB1 5.2s steps(1,end) infinite}
.sst .b2{animation:aSstB2 5.2s steps(1,end) infinite}
.sst .hint{position:absolute;left:50%;bottom:6px;z-index:4;display:block;width:8px;height:8px;
  margin-left:-4px;box-sizing:border-box;border-right:2px solid var(--ink-3);
  border-bottom:2px solid var(--ink-3);transform:rotate(45deg);
  animation:aSstHint 5.2s steps(1,end) infinite}
/* course de 104 disponible, palier a 70 */
@keyframes aSstDoc{0%,16%{transform:translateY(0)}44%,84%{transform:translateY(-70px)}
  98%,100%{transform:translateY(0)}}
@keyframes aSstHdr{0%,16%{height:24px;box-shadow:none}
  44%,84%{height:15px;box-shadow:0 5px 12px rgba(var(--shadow-rgb),.6)}
  98%,100%{height:24px;box-shadow:none}}
@keyframes aSstB1{0%,30%{opacity:1}32%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aSstB2{0%,30%{opacity:0}32%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aSstHint{0%,30%{opacity:.8}32%,94%{opacity:0}96%,100%{opacity:.8}}

/* Le CANDIDAT ABANDONNE est tout le terme, et c est ce qui separe cette
   fiche de scroll-marker, qui portait exactement le meme markup.
   Le geste part vers la deuxieme vue, changing se declenche et son point
   passe en provisoire, puis l utilisateur REVIENT. changing se declenche
   de nouveau pour la premiere, le point provisoire retombe, et la vue se
   pose sur la premiere ou change se declenche enfin.
   On peut compter, quatre changing pour trois change. La difference est
   le candidat auquel personne n est jamais arrive, et c est la seule
   raison pour laquelle ces deux evenements existent separement.
   La bande fait 3 fois 66 avec 8 de gouttiere, soit 214, dans une
   fenetre de 150, le pas vaut donc 74 et la course d une vue 66.
   Gele, on verrait un carrousel avec un point allume. */
.sse{position:relative;width:150px;height:100px;display:flex;flex-direction:column;gap:9px}
.sse .vp{position:relative;height:52px;overflow:hidden;border-radius:8px;flex:0 0 auto}
.sse .strip{position:absolute;left:0;top:0;display:flex;gap:8px;
  animation:aSseStrip 6s cubic-bezier(.3,.9,.3,1) infinite}
.sse .strip i{display:block;flex:none;width:66px;height:52px;border-radius:8px;background:var(--s3)}
.sse .strip i:first-child{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.sse .dots{display:flex;justify-content:center;gap:8px;flex:0 0 auto}
.sse .dots b{display:block;width:8px;height:8px;border-radius:4px;background:var(--s3)}
.sse .d1{animation:aSseD1 6s steps(1,end) infinite}
.sse .d2{animation:aSseD2 6s steps(1,end) infinite}
.sse .evts{display:flex;gap:7px;justify-content:center;flex:0 0 auto}
.sse .evts em{font:600 6px/1 var(--mono);font-style:normal;padding:3px 5px;border-radius:4px;
  background:var(--s1);color:var(--ink-3)}
.sse .ing{animation:aSseIng 6s steps(1,end) infinite}
.sse .chg{animation:aSseChg 6s steps(1,end) infinite}
/* part vers la deuxieme a -40, REVIENT a 0, puis y va vraiment a -66 */
@keyframes aSseStrip{0%,6%{transform:translateX(0)}
  22%{transform:translateX(-40px)}
  36%,42%{transform:translateX(0)}
  56%,76%{transform:translateX(-66px)}
  98%,100%{transform:translateX(0)}}
/* provisoire en ambre, valide en lime */
@keyframes aSseD1{0%,25%{background:var(--lime)}
  26%,37%{background:var(--a4)}
  38%,59%{background:var(--lime)}
  60%,89%{background:var(--s3)}
  90%,95%{background:var(--a4)}
  96%,100%{background:var(--lime)}}
@keyframes aSseD2{0%,9%{background:var(--s3)}
  10%,25%{background:var(--a4)}
  26%,59%{background:var(--s3)}
  60%,71%{background:var(--a4)}
  72%,89%{background:var(--lime)}
  90%,100%{background:var(--s3)}}
/* quatre declenchements, a 10, 26, 60 et 90 */
@keyframes aSseIng{0%,9%{background:var(--s1);color:var(--ink-3)}
  10%,13%{background:var(--a4);color:var(--void)}
  14%,25%{background:var(--s1);color:var(--ink-3)}
  26%,29%{background:var(--a4);color:var(--void)}
  30%,59%{background:var(--s1);color:var(--ink-3)}
  60%,63%{background:var(--a4);color:var(--void)}
  64%,89%{background:var(--s1);color:var(--ink-3)}
  90%,93%{background:var(--a4);color:var(--void)}
  94%,100%{background:var(--s1);color:var(--ink-3)}}
/* trois declenchements seulement, a 38, 72 et 96 */
@keyframes aSseChg{0%,37%{background:var(--s1);color:var(--ink-3)}
  38%,41%{background:var(--lime);color:var(--on-accent)}
  42%,71%{background:var(--s1);color:var(--ink-3)}
  72%,75%{background:var(--lime);color:var(--on-accent)}
  76%,95%{background:var(--s1);color:var(--ink-3)}
  96%,99%{background:var(--lime);color:var(--on-accent)}
  100%{background:var(--s1);color:var(--ink-3)}}

/* Les CONTROLES sont generes par le conteneur, et ce sont eux qui
   separent cette fiche de scroll-snap-event. Deux boutons en plus des
   marqueurs, et ils se DESACTIVENT tout seuls aux extremites, le gauche
   sur la premiere vue et le droit sur la derniere. Aucune rangee de
   points ecrite a la main ne tient cet etat juste.
   Le marqueur courant s elargit au lieu d etre echange, parce que c est
   la seule information que l utilisateur a sur sa position.
   La bande fait 3 fois 132 avec 8 de gouttiere, soit 412, dans une
   fenetre de 132, le pas vaut donc 140 et la course totale 280.
   Gele, on verrait un carrousel avec trois marqueurs, sans les boutons
   qui se desactivent, qui sont la moitie de ce que le conteneur donne. */
.smk{position:relative;width:152px;height:100px;padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:10px}
.smk .vp{position:relative;flex:1;overflow:hidden;border-radius:8px;min-height:0}
.smk .strip{position:absolute;left:0;top:0;bottom:0;display:flex;gap:8px;
  animation:aSmkStrip 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.smk .strip i{display:block;flex:none;width:132px;border-radius:8px;background:var(--s3)}
.smk .strip i:first-child{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.smk .ctl{display:flex;align-items:center;justify-content:center;gap:10px;height:12px;flex:0 0 auto}
/* les deux boutons generes, ils se desactivent aux extremites */
.smk .ctl b{display:block;width:9px;height:9px;box-sizing:border-box;
  border-right:2px solid var(--ink-3);border-bottom:2px solid var(--ink-3)}
.smk .prev{transform:rotate(135deg);animation:aSmkPrev 5.6s steps(1,end) infinite}
.smk .next{transform:rotate(-45deg);animation:aSmkNext 5.6s steps(1,end) infinite}
.smk .mk{display:flex;gap:7px}
.smk .mk i{display:block;width:8px;height:8px;border-radius:4px;background:var(--s3)}
.smk .m1{animation:aSmkM1 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.smk .m2{animation:aSmkM2 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.smk .m3{animation:aSmkM3 5.6s cubic-bezier(.3,.9,.3,1) infinite}
/* pas de 140, trois positions, 0 puis -140 puis -280 */
@keyframes aSmkStrip{0%,8%{transform:translateX(0)}
  22%,32%{transform:translateX(-140px)}
  44%,56%{transform:translateX(-280px)}
  68%,78%{transform:translateX(-140px)}
  92%,100%{transform:translateX(0)}}
@keyframes aSmkM1{0%,14%{width:20px;background:var(--lime)}
  22%,86%{width:8px;background:var(--s3)}
  92%,100%{width:20px;background:var(--lime)}}
@keyframes aSmkM2{0%,14%{width:8px;background:var(--s3)}
  22%,38%{width:20px;background:var(--lime)}
  44%,62%{width:8px;background:var(--s3)}
  68%,86%{width:20px;background:var(--lime)}
  92%,100%{width:8px;background:var(--s3)}}
@keyframes aSmkM3{0%,38%{width:8px;background:var(--s3)}
  44%,62%{width:20px;background:var(--lime)}
  68%,100%{width:8px;background:var(--s3)}}
/* desactive sur la premiere vue */
@keyframes aSmkPrev{0%,14%{border-color:var(--s2)}22%,86%{border-color:var(--ink-3)}
  92%,100%{border-color:var(--s2)}}
/* desactive sur la derniere */
@keyframes aSmkNext{0%,38%{border-color:var(--ink-3)}44%,62%{border-color:var(--s2)}
  68%,100%{border-color:var(--ink-3)}}


/* UNE seule regle, et l index est visible sur chaque ligne. La version
   precedente ecrivait cinq animation-delay a la main dans cinq regles
   nth-child, ce qui est exactement la methode que cette fonction sert a
   remplacer, donc la scene demontrait le contraire du terme.
   Les index 1 a 5 sont affiches, la puce du bas donne le calcul, et les
   retards de 0, 40, 80, 120 et 160ms en decoulent. Un lecteur peut
   verifier l arithmetique sur la scene.
   Gele, on verrait une liste numerotee, sans la cascade qui sort du
   calcul. */
.six{position:relative;width:150px;height:104px;display:flex;flex-direction:column;gap:7px}
.six .rw{display:flex;align-items:center;gap:8px;
  animation:aSix 3.2s cubic-bezier(.2,.9,.25,1) infinite}
.six .rw em{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal;width:7px;text-align:right}
.six .rw i{display:block;height:11px;border-radius:5px;background:var(--lime)}
.six .r1 i{width:96px}
.six .r2 i{width:82px}
.six .r3 i{width:90px}
.six .r4 i{width:68px}
.six .r5 i{width:56px}
/* 40ms par rang, soit 0, 40, 80, 120 puis 160 */
.six .r2{animation-delay:.04s}
.six .r3{animation-delay:.08s}
.six .r4{animation-delay:.12s}
.six .r5{animation-delay:.16s}
.six .rule{position:absolute;left:0;bottom:0;font:600 7px/1 var(--mono);color:var(--lime);
  letter-spacing:0;text-transform:none}
@keyframes aSix{0%,10%{transform:translateX(-22px);opacity:0}
  36%,78%{transform:translateX(0);opacity:1}
  96%,100%{transform:translateX(-22px);opacity:0}}

/* ---------------- primitives d'entree et de sortie ---------------- */
/* L ETAT DE DEPART est dessine. Le cadre pointille de gauche est la
   valeur depuis laquelle la carte transitionne, decalee de 10 et a
   l echelle 0.9, et c est exactement ce que la regle fournit. A droite
   il n y a aucun etat d avant changement, donc la carte apparait
   directement a sa valeur finale.
   La comparaison porte sur l ENTREE et rien d autre, ce qui est la
   moitie du travail. La sortie demande allow-discrete, et sa fiche
   compare donc l autre bout.
   Gele, la carte de gauche et celle de droite seraient identiques, ce
   qui est precisement pourquoi ce defaut passe en revue. */
.sts{display:flex;gap:14px}
.sts .one{display:flex;flex-direction:column;align-items:center;gap:8px}
.sts em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.sts .ok em{color:var(--lime)}
.sts .ko em{color:var(--a4)}
.sts .stg{position:relative;display:block;width:62px;height:56px}
/* le cadre pointille EST l etat de depart, decale de 10 et a 0.9 */
.sts .ghost{position:absolute;left:0;right:0;top:10px;display:block;height:38px;border-radius:10px;
  box-sizing:border-box;border:1px dashed var(--s5);transform:scale(.9)}
.sts .cd{position:absolute;left:0;right:0;top:0;display:block;height:38px;border-radius:10px;
  background:var(--lime)}
.sts .ok .cd{animation:aSts 3.4s cubic-bezier(.2,.9,.25,1) infinite}
.sts .ko .cd{animation:aStsKo 3.4s steps(1,end) infinite}
@keyframes aSts{0%,6%{opacity:0;transform:translateY(10px) scale(.9)}
  34%,76%{opacity:1;transform:translateY(0) scale(1)}
  96%,100%{opacity:0;transform:translateY(10px) scale(.9)}}
/* sans etat de depart, elle apparait a sa valeur finale */
@keyframes aStsKo{0%,6%{opacity:0}7%,76%{opacity:1}77%,100%{opacity:0}}

/* La BASCULE de display est marquee sur la barre de duree, et c est le
   detail propre a cette fiche. A gauche elle tombe a 100 pour cent de la
   duree, la carte reste donc peinte pendant toute sa sortie. A droite
   elle tombe a 0 pour cent, la carte disparait a la premiere image et
   l animation ecrite se joue sur quelque chose que personne ne regarde.
   Cette asymetrie est la fonctionnalite, pas une bizarrerie.
   La comparaison porte sur la SORTIE, l entree a l air correcte dans les
   deux colonnes, ce qui est la raison pour laquelle ce bug est livre.
   La colonne avait align-items et gap sans display flex, donc les deux
   declarations ne faisaient rien.
   Gele, les deux colonnes seraient identiques. */
.ald{display:flex;gap:16px}
.ald .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.ald em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ald .ok em{color:var(--lime)}
.ald .ko em{color:var(--a4)}
.ald .p{display:block;width:46px;height:46px;border-radius:10px;background:var(--lime)}
.ald .ok .p{animation:aAldOk 3.2s ease infinite}
.ald .ko .p{animation:aAldKo 3.2s steps(1,end) infinite}
.ald .dur{position:relative;display:block;width:46px;height:4px;border-radius:2px;background:var(--s2)}
.ald .flip{position:absolute;top:-3px;display:block;width:2px;height:10px;border-radius:1px;
  background:var(--ink)}
/* la bascule a 100 pour cent de la duree */
.ald .ok .flip{right:0}
/* la bascule a 0 pour cent de la duree */
.ald .ko .flip{left:0}
@keyframes aAldOk{0%,30%{opacity:1;transform:scale(1)}
  62%,92%{opacity:0;transform:scale(.7)}100%{opacity:1;transform:scale(1)}}
@keyframes aAldKo{0%,30%{opacity:1}31%,92%{opacity:0}93%,100%{opacity:1}}

/* L ECHAPPEMENT AU DECOUPAGE est la definition, et il n etait pas
   montre. La boite a bord continu a overflow hidden. Le panneau du haut
   est dans la couche superieure, il sort donc de la boite et passe par
   dessus. Celui du bas porte un z-index de 9999 et reste decoupe net au
   bord, parce qu un ancetre qui decoupe ne regarde pas le z-index.
   C est la demonstration par l echec, et c est le seul moyen de rendre
   visible une propriete dont tout le monde croit qu un grand nombre
   suffit.
   Gele, on verrait deux panneaux dont un tronque, ce qui est deja
   l information, mais pas qu ils sont partis du meme endroit. */
.tpl{position:relative;width:160px;height:106px}
.tpl .clip{position:absolute;left:10px;top:24px;width:74px;height:54px;overflow:hidden;
  box-sizing:border-box;border:1px solid var(--s5);border-radius:8px;background:var(--s1)}
.tpl .trg{position:absolute;left:9px;bottom:9px;display:block;width:34px;height:12px;border-radius:6px;
  background:var(--s4)}
/* dans la boite, avec un grand z-index, et decoupe quand meme */
.tpl .inpanel{position:absolute;left:9px;bottom:26px;display:block;width:96px;height:38px;
  border-radius:8px;background:var(--a4);z-index:9999;
  animation:aTplIn 3.8s cubic-bezier(.2,1.1,.3,1) infinite}
/* dans la couche superieure, il ignore le decoupage de l ancetre */
.tpl .toppanel{position:absolute;left:19px;top:36px;display:block;width:96px;height:38px;
  border-radius:8px;background:var(--lime);z-index:5;
  box-shadow:0 12px 26px rgba(var(--shadow-rgb),.55);
  animation:aTplTop 3.8s cubic-bezier(.2,1.1,.3,1) infinite}
.tpl .tags{position:absolute;right:0;top:0;bottom:0;display:flex;flex-direction:column;
  justify-content:center;gap:10px;width:44px}
.tpl .tags em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.tpl .ok{color:var(--lime)}
.tpl .ko{color:var(--a4)}
@keyframes aTplTop{0%,8%{opacity:0;transform:scale(.7)}32%,74%{opacity:1;transform:scale(1)}
  94%,100%{opacity:0;transform:scale(.7)}}
@keyframes aTplIn{0%,8%{opacity:0;transform:scale(.7)}32%,74%{opacity:1;transform:scale(1)}
  94%,100%{opacity:0;transform:scale(.7)}}

/* La CIBLE est la hauteur du contenu, pas un nombre mesure a la main.
   Les deux panneaux portent les MEMES quatre lignes. Celui de gauche
   ouvre a auto et les montre toutes. Celui de droite ouvre a 48px, une
   valeur juste pour deux lignes, et tronque les deux autres.
   La version precedente animait vers 34px, c est a dire exactement ce
   qu on fait SANS cette fonctionnalite, donc la scene ne montrait pas le
   terme.
   Quatre lignes de 8 avec 5 de gouttiere font 47, la hauteur ouverte de
   gauche vaut donc 47 et celle de droite 48 ne peut en montrer que deux
   et demie.
   Gele, on verrait un panneau ouvert et un panneau tronque, sans savoir
   qu ils contiennent la meme chose. */
.isz{display:flex;gap:14px;width:158px}
.isz .one{display:flex;flex-direction:column;gap:6px;flex:1}
.isz em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.isz .ok em{color:var(--lime)}
.isz .ko em{color:var(--a4)}
.isz .hd{display:block;height:15px;border-radius:5px;background:var(--s3)}
.isz .bd{display:flex;flex-direction:column;gap:5px;overflow:hidden;height:0}
.isz .bd i{display:block;height:8px;border-radius:4px;background:var(--lime);flex:0 0 auto}
.isz .bd i:nth-child(2){width:82%}
.isz .bd i:nth-child(4){width:58%}
.isz .ok .bd{animation:aIszAuto 3.8s cubic-bezier(.3,.9,.3,1) infinite}
.isz .ko .bd{animation:aIszFix 3.8s cubic-bezier(.3,.9,.3,1) infinite}
/* quatre lignes de 8 avec 5 de gouttiere, la hauteur du contenu vaut 47 */
@keyframes aIszAuto{0%,10%{height:0}44%,80%{height:47px}96%,100%{height:0}}
/* 48px etait juste pour deux lignes, il en tronque deux */
@keyframes aIszFix{0%,10%{height:0}44%,80%{height:22px}96%,100%{height:0}}

/* Le nombre de CROISEMENTS est ce qui separe les deux. Le trait vertical
   est la cible. A gauche, une liste de sept arrets fait passer le point
   trois fois de part et d autre. A droite, le meilleur cubic-bezier ne
   depasse qu une fois, parce qu il n a que deux points de controle, et
   c est un plafond et non un reglage.
   Les deux courses sont identiques et les deux durees aussi, seule la
   fonction change.
   La piste fait 104 et le point 12, la course vaut donc 92, et la cible
   est posee a 92.
   Gele, on verrait deux points au meme endroit. */
.lne{display:flex;flex-direction:column;gap:14px;width:150px}
.lne .one{display:flex;flex-direction:column;gap:7px}
.lne em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.lne .ok em{color:var(--lime)}
.lne .ko em{color:var(--ink-3)}
.lne .tk{position:relative;display:block;width:104px;height:8px;border-radius:4px;background:var(--s2)}
.lne .tgt{position:absolute;left:92px;top:-4px;display:block;width:1px;height:16px;
  background:var(--ink-3)}
.lne .tk i{position:absolute;left:0;top:-2px;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--lime)}
/* sept arrets, trois croisements de la cible */
.lne .ok .tk i{animation:aLne 2.8s linear(0,.42,1.08,.94,1.05,.98,1) infinite}
/* deux points de controle, un seul depassement possible */
.lne .ko .tk i{animation:aLne 2.8s cubic-bezier(.2,1.6,.3,1) infinite}
@keyframes aLne{0%,6%{transform:translateX(0)}70%,92%{transform:translateX(92px)}
  100%{transform:translateX(0)}}

/* DEUX voyageurs sur le MEME trace, et c est offset-rotate qui les
   separe. La fleche est en auto, elle s incline dans la courbe. La barre
   est a 0deg, elle reste droite tout du long. C est la difference entre
   un vehicule qui suit une route et une etiquette qu on transporte le
   long de cette route, et la consigne dit qu un texte veut toujours la
   seconde.
   Le trace du SVG et celui de offset-path portent le meme d, au pixel
   pres, sinon les deux ne coincideraient pas.
   Gele, on verrait deux formes sur une courbe, sans savoir laquelle
   s incline. */
.mpt{position:relative;width:152px;height:90px}
.mpt svg{display:block;width:152px;height:62px}
.mpt .rail{fill:none;stroke:var(--s3);stroke-width:2;stroke-dasharray:4 4}
/* offset-rotate auto, elle bascule avec la tangente */
.mpt .ship{position:absolute;left:0;top:0;width:0;height:0;
  border-left:10px solid var(--lime);border-top:6px solid transparent;
  border-bottom:6px solid transparent;
  offset-path:path("M10 52C36 8 90 8 140 46");offset-rotate:auto;
  animation:aMpt 3.6s cubic-bezier(.4,0,.2,1) infinite}
/* offset-rotate 0deg, elle reste droite */
.mpt .lab{position:absolute;left:0;top:0;display:block;width:20px;height:7px;border-radius:3px;
  background:var(--a4);
  offset-path:path("M10 52C36 8 90 8 140 46");offset-rotate:0deg;
  animation:aMpt 3.6s cubic-bezier(.4,0,.2,1) infinite;animation-delay:-.5s}
.mpt .tags{position:absolute;left:0;right:0;bottom:0;display:flex;gap:12px;justify-content:center}
.mpt .tags em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.mpt .ok{color:var(--lime)}
.mpt .ko{color:var(--a4)}
@keyframes aMpt{0%,6%{offset-distance:0%}72%,88%{offset-distance:100%}100%{offset-distance:0%}}

/* La VARIABLE est animee, pas le degrade. A gauche l angle est enregistre
   avec une syntaxe, le navigateur sait donc interpoler entre deux de ses
   valeurs et le balayage est continu. A droite la meme variable n est pas
   enregistree, elle reste une chaine, et le navigateur bascule d un coup
   a mi parcours.
   La version precedente faisait tourner l element entier avec un
   transform, ce qui n a aucun besoin de cette fonctionnalite et ne
   montrait donc pas le terme.
   Aller retour de 0 a 180 puis retour a 0, la boucle revient donc a son
   point de depart, et le cote non enregistre bascule a 25 et a 75 pour
   cent.
   Gele, les deux anneaux seraient identiques. */
@property --tprA{syntax:"<angle>";inherits:false;initial-value:0deg}
.tpr{display:flex;gap:16px}
.tpr .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.tpr em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.tpr .ok em{color:var(--lime)}
.tpr .ko em{color:var(--a4)}
.tpr .ring{display:block;width:58px;height:58px;border-radius:50%}
/* enregistree, elle interpole */
.tpr .rg{background:conic-gradient(from var(--tprA),var(--lime),var(--s1) 45%,var(--s1) 60%,var(--lime));
  animation:aTprReg 3.6s linear infinite}
/* non enregistree, elle bascule a mi parcours */
.tpr .rp{background:conic-gradient(from var(--tprB,0deg),var(--a4),var(--s1) 45%,var(--s1) 60%,var(--a4));
  animation:aTprPlain 3.6s linear infinite}
@keyframes aTprReg{0%,100%{--tprA:0deg}50%{--tprA:180deg}}
@keyframes aTprPlain{0%,100%{--tprB:0deg}50%{--tprB:180deg}}


/* Le COMPORTEMENT, pas l apparence, et c est ce que la version
   precedente ne montrait pas. Un panneau qui grandit depuis un bouton se
   refait en dix lignes avec une classe, donc la scene demontrait le
   contraire du terme.
   Ici deux choses que rien de fait main ne donne. A 38 pour cent le
   pointeur touche le fond vide et le panneau se ferme, sans qu aucun
   ecouteur de clic exterieur ait ete ecrit. A 74 pour cent le pointeur
   touche le second declencheur, le premier panneau se ferme TOUT SEUL et
   le second s ouvre, parce que la pile est geree par le navigateur.
   Gele, on verrait un panneau ouvert, et rien de ce qui compte. */
.pop2{position:relative;width:154px;height:102px;border-radius:10px;overflow:hidden;background:var(--s0)}
.pop2 .page{position:absolute;left:10px;right:10px;top:10px;display:flex;flex-direction:column;gap:9px}
.pop2 .page i{display:block;height:7px;border-radius:3px;background:var(--s2)}
.pop2 .page i:last-child{width:62%}
.pop2 .ta,.pop2 .tb{position:absolute;bottom:10px;display:block;width:44px;height:16px;
  border-radius:8px;background:var(--s3)}
.pop2 .ta{left:10px}
.pop2 .tb{left:62px}
.pop2 .pa,.pop2 .pb{position:absolute;bottom:32px;display:flex;flex-direction:column;
  justify-content:center;gap:6px;width:72px;height:40px;padding:0 9px;box-sizing:border-box;
  border-radius:9px;background:var(--lime);transform-origin:bottom left;opacity:0}
.pop2 .pa{left:10px;animation:aPop2A 6s cubic-bezier(.2,1.1,.3,1) infinite}
.pop2 .pb{left:62px;animation:aPop2B 6s cubic-bezier(.2,1.1,.3,1) infinite}
.pop2 .pa i,.pop2 .pb i{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.6)}
.pop2 .pa i:last-child,.pop2 .pb i:last-child{width:56%}
.pop2 .note{position:absolute;right:10px;top:10px;font:600 6px/1 var(--mono);color:var(--ink-3);
  font-style:normal;letter-spacing:0;text-transform:none}
.pop2 .cur{left:25px;top:78px;animation:aPop2Cur 6s cubic-bezier(.4,0,.4,1) infinite}
/* declencheur A, fond vide, declencheur A, declencheur B */
@keyframes aPop2Cur{0%{transform:translate(0,0);opacity:0}
  6%{transform:translate(0,0);opacity:1}
  30%{transform:translate(0,0);opacity:1}
  36%{transform:translate(84px,-46px);opacity:1}
  52%{transform:translate(84px,-46px);opacity:1}
  56%{transform:translate(0,0);opacity:1}
  70%{transform:translate(0,0);opacity:1}
  74%{transform:translate(52px,0);opacity:1}
  94%{transform:translate(52px,0);opacity:1}
  100%{transform:translate(0,0);opacity:0}}
/* ouvert a 10, ferme par le clic exterieur a 38, reouvert a 58,
   puis ferme TOUT SEUL a 74 quand le second s ouvre */
@keyframes aPop2A{0%,8%{transform:scale(.1);opacity:0}
  18%,36%{transform:scale(1);opacity:1}
  46%,56%{transform:scale(.1);opacity:0}
  64%,72%{transform:scale(1);opacity:1}
  82%,100%{transform:scale(.1);opacity:0}}
@keyframes aPop2B{0%,74%{transform:scale(.1);opacity:0}
  82%,92%{transform:scale(1);opacity:1}
  99%,100%{transform:scale(.1);opacity:0}}

/* La LISTE est parcourue dans l ordre, ce qui est le terme, et la version
   precedente ne montrait qu un seul basculement.
   Trois candidats, trois chips, et le candidat actif est allume. Le
   premier est block-start, tant que l ancre est assez basse. Quand elle
   monte contre le bord haut, block-start ne tient plus et flip-block
   prend la suite. Quand elle atteint le coin, aucun des deux ne tient et
   flip-inline resout.
   Le basculement est instantane, pas anime, ce que la consigne dit
   explicitement.
   Gele, on verrait une bulle a cote d un point, sans savoir combien de
   places ont ete essayees. */
.ptf{position:relative;width:152px;height:106px;border-radius:9px;background:var(--s0);overflow:hidden}
.ptf .anchor{position:absolute;left:56px;top:62px;display:block;width:12px;height:12px;
  border-radius:6px;background:var(--lime);animation:aPtfA 5.4s cubic-bezier(.4,0,.4,1) infinite}
.ptf .tip{position:absolute;left:32px;top:38px;display:block;width:60px;height:18px;border-radius:7px;
  background:var(--s5);animation:aPtfT 5.4s steps(1,end) infinite}
.ptf .cands{position:absolute;left:8px;bottom:6px;display:flex;flex-direction:column;gap:4px}
.ptf .cands em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  color:var(--ink-3)}
.ptf .c1{animation:aPtfC1 5.4s steps(1,end) infinite}
.ptf .c2{animation:aPtfC2 5.4s steps(1,end) infinite}
.ptf .c3{animation:aPtfC3 5.4s steps(1,end) infinite}
/* l ancre descend au centre, monte contre le bord, puis va au coin */
@keyframes aPtfA{0%,14%{transform:translate(0,0)}
  34%,50%{transform:translate(0,-48px)}
  68%,84%{transform:translate(72px,-48px)}
  98%,100%{transform:translate(0,0)}}
/* block-start, puis flip-block, puis flip-inline, chaque bascule est
   instantanee */
@keyframes aPtfT{0%,25%{transform:translate(0,0)}
  26%,58%{transform:translate(0,-6px)}
  59%,90%{transform:translate(102px,-42px)}
  91%,100%{transform:translate(0,0)}}
@keyframes aPtfC1{0%,25%{color:var(--lime)}26%,90%{color:var(--ink-3)}91%,100%{color:var(--lime)}}
@keyframes aPtfC2{0%,25%{color:var(--ink-3)}26%,58%{color:var(--lime)}59%,100%{color:var(--ink-3)}}
@keyframes aPtfC3{0%,58%{color:var(--ink-3)}59%,90%{color:var(--lime)}91%,100%{color:var(--ink-3)}}

/* La TAILLE RESERVEE est le sujet, et la propriete seule est un bug.
   Les deux colonnes ont cinq blocs, deux a l ecran et trois hors champ.
   A gauche les trois hors champ gardent leur boite reservee, la barre du
   bas ne bouge donc pas. A droite ils s effondrent a rien, la barre
   remonte de leur hauteur, et c est exactement le saut de barre de
   defilement que la consigne demande d eviter.
   Trois blocs de 16 avec 7 de gouttiere font 62, la barre de droite
   remonte donc de 62.
   Gele, les deux colonnes seraient identiques, ce qui est precisement
   pourquoi cet oubli est livre. */
.cvz{display:flex;gap:14px;width:158px}
.cvz .one{display:flex;flex-direction:column;gap:7px;flex:1}
.cvz em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.cvz .ok em{color:var(--lime)}
.cvz .ko em{color:var(--a4)}
.cvz .col{display:flex;flex-direction:column;gap:7px}
.cvz .col i{display:block;height:16px;border-radius:6px;flex:0 0 auto}
.cvz .on{background:var(--lime)}
.cvz .off{background:var(--s1)}
/* a gauche la boite reste reservee, aucune animation n est necessaire */
/* a droite elle s effondre a rien */
.cvz .ko .off{animation:aCvzDrop 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.cvz .ftr{display:block;height:3px;border-radius:2px;background:var(--ink-3);opacity:.5}
.cvz .ko .ftr{animation:aCvzFoot 4.4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aCvzDrop{0%,14%{height:16px}44%,84%{height:0}96%,100%{height:16px}}
/* trois blocs de 16 plus trois gouttieres de 7, soit 62 de remontee */
@keyframes aCvzFoot{0%,14%{transform:translateY(0)}44%,84%{transform:translateY(-62px)}
  96%,100%{transform:translateY(0)}}

/* L ATTRIBUTION est la valeur de l API, pas la hauteur de la barre. Un
   graphique avec une barre rouge dit seulement que quelque chose a ete
   lent, ce que n importe quel compteur d images dit aussi.
   La barre longue est donc DECOUPEE. Le bas est la part bloquante, 56ms,
   le haut le reste, 22ms, et la chip nomme le gestionnaire responsable.
   L echelle est de 0.8px par milliseconde. Les images normales font 12ms
   soit 10px, la longue 78ms soit 62, et le seuil de 50ms tombe donc a 40,
   ou le trait est pose.
   Gele, on verrait un graphique, ce qui est deja utile, mais pas quelle
   part bloquait ni qui l a causee. */
.lfr{position:relative;width:160px;height:98px;display:flex;flex-direction:column;gap:9px}
.lfr .chart{position:relative;display:flex;align-items:flex-end;gap:5px;height:66px}
.lfr .f{display:block;width:10px;height:10px;border-radius:3px;background:var(--lime);flex:0 0 auto;
  transform-origin:bottom;animation:aLfrBar 4.4s cubic-bezier(.2,.9,.25,1) infinite}
/* 78ms a 0.8px par ms, soit 62 */
.lfr .long{position:relative;display:flex;flex-direction:column;justify-content:flex-end;width:22px;
  height:62px;flex:0 0 auto;transform-origin:bottom;
  animation:aLfrBar 4.4s cubic-bezier(.2,.9,.25,1) infinite}
.lfr .nb{display:block;height:17px;border-radius:3px 3px 0 0;background:var(--a4);opacity:.45}
.lfr .bl{display:block;height:45px;border-radius:0 0 3px 3px;background:var(--danger)}
/* le seuil de 50ms tombe a 40 */
.lfr .thr{position:absolute;left:0;right:0;bottom:40px;display:block;height:1px;
  background:repeating-linear-gradient(90deg,var(--danger) 0 3px,transparent 3px 6px)}
.lfr .attr{display:flex;flex-wrap:wrap;gap:5px}
.lfr .attr em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  padding:2px 4px;border-radius:3px}
.lfr .d{background:var(--s1);color:var(--ink-3);opacity:0;animation:aLfrD 4.4s steps(1,end) infinite}
.lfr .b{background:rgba(var(--danger-rgb),.18);color:var(--danger);opacity:0;
  animation:aLfrB 4.4s steps(1,end) infinite}
.lfr .s{background:var(--s1);color:var(--a4);opacity:0;animation:aLfrS 4.4s steps(1,end) infinite}
/* les barres arrivent, PUIS l attribution se lit, une chip apres l autre */
@keyframes aLfrBar{0%,6%{transform:scaleY(0)}16%,86%{transform:scaleY(1)}96%,100%{transform:scaleY(0)}}
@keyframes aLfrD{0%,29%{opacity:0}30%,86%{opacity:1}88%,100%{opacity:0}}
@keyframes aLfrB{0%,39%{opacity:0}40%,86%{opacity:1}88%,100%{opacity:0}}
@keyframes aLfrS{0%,49%{opacity:0}50%,86%{opacity:1}88%,100%{opacity:0}}

/* ---------------- timeline et scroll ---------------- */
/* L ENTREE est visible, et la tete S ARRETE quand elle s arrete. C est ce
   qu une animation sur horloge ne sait pas faire, et la version
   precedente, une barre qui se remplit et se vide, se refait avec un
   simple aller retour.
   La piste du haut est l entree, avec deux PAUSES et un retour en
   arriere. La tete du milieu, scrub true, la recopie exactement, pauses
   comprises. Celle du bas, scrub 0.5, prend du retard et rattrape, ce qui
   est le lissage de la consigne.
   Aucune duree n est declaree, la note le dit.
   La piste fait 104 et la tete 10, la course vaut donc 94.
   Gele, on verrait trois pistes, sans savoir laquelle commande. */
.scb{position:relative;width:154px;height:100px;display:flex;flex-direction:column;gap:12px}
.scb .lane{display:flex;align-items:center;gap:8px}
.scb .lane em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;width:32px;
  text-align:right;letter-spacing:0;text-transform:none}
.scb .tk{position:relative;display:block;width:104px;height:10px;border-radius:5px;background:var(--s2)}
.scb .tk i{position:absolute;left:0;top:0;display:block;width:10px;height:10px;border-radius:5px}
.scb .th{background:var(--ink)}
.scb .h1{background:var(--lime)}
.scb .h2{background:var(--a2)}
.scb .note{position:absolute;right:0;bottom:0;font:600 6px/1 var(--mono);color:var(--ink-3);
  font-style:normal;letter-spacing:0;text-transform:none}
/* deux pauses et un retour en arriere, course de 94 */
.scb .th,.scb .h1{animation:aScb 4.6s linear infinite}
.scb .h2{animation:aScb 4.6s cubic-bezier(.5,0,.5,1) infinite;animation-delay:.18s}
@keyframes aScb{0%,6%{transform:translateX(0)}
  22%,34%{transform:translateX(38px)}
  48%,58%{transform:translateX(94px)}
  70%,78%{transform:translateX(56px)}
  94%,100%{transform:translateX(0)}}

/* Le RESERVOIR est toute la difficulte, et c est lui qui manquait. A
   gauche un bloc pointille rend au document la distance pendant laquelle
   la carte a ete maintenue, les deux lignes du bas arrivent donc a leur
   place. A droite il n y a pas de reservoir, elles sont exactement cette
   distance trop haut, et la page saute quand l epinglage se relache.
   C est le detail propre a ce terme, la fiche scroll-pinning montre
   l autre moitie, ce que le defilement retenu paie.
   Le reservoir fait 34, la distance maintenue aussi.
   Gele, les deux colonnes seraient identiques. */
.pin2{display:flex;gap:12px;width:158px}
.pin2 .one{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.pin2 em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.pin2 .ok em{color:var(--lime)}
.pin2 .ko em{color:var(--a4)}
.pin2 .vp{position:relative;display:block;height:92px;overflow:hidden;border-radius:8px;
  background:var(--s0)}
.pin2 .ko .col{padding-bottom:0}
.pin2 .col{position:absolute;left:7px;right:7px;top:0;display:flex;flex-direction:column;gap:8px;
  animation:aPin2Col 4.4s linear infinite}
.pin2 .ln{display:block;height:9px;border-radius:4px;background:var(--s3);flex:0 0 auto}
/* le reservoir, il rend la distance maintenue au document */
.pin2 .sp{display:block;height:34px;border-radius:6px;box-sizing:border-box;
  border:1px dashed rgba(var(--accent-rgb),.5);flex:0 0 auto}
.pin2 .card2{position:absolute;left:7px;right:7px;top:34px;display:block;height:24px;border-radius:7px;
  background:var(--lime);z-index:2;animation:aPin2Card 4.4s linear infinite}
/* Les deux colonnes recoivent le MEME defilement de 78. Celle de gauche
   fait 170 avec son reservoir, elle a donc exactement de quoi. Celle de
   droite ne fait que 136, elle manque des 34 du reservoir et son bas
   arrive trop tot. */
@keyframes aPin2Col{0%,8%{transform:translateY(0)}50%{transform:translateY(-78px)}
  92%,100%{transform:translateY(0)}}
/* elle se cale a 26, tient jusqu a 40, se relache vers le haut au bout
   du defilement, puis se recale au retour et redescend */
@keyframes aPin2Card{0%,10%{transform:translateY(44px)}
  26%,40%{transform:translateY(0)}
  50%,58%{transform:translateY(-44px)}
  68%,82%{transform:translateY(0)}
  98%,100%{transform:translateY(44px)}}

/* Des OFFSETS par etape, avec un recouvrement, un vide et un label. Une
   cascade uniforme, ce que faisait la version precedente avec trois
   delais egaux, n a ni recouvrement ni vide et se confond avec stagger.
   A part a zero. B part a moins 0.3, elle mord donc sur la fin de A. C
   est ancree sur le label, apres un vide voulu. La tete de lecture
   balaye la regle et l on voit qui est simultane avec qui.
   La regle fait 132. A occupe de 0 a 52, B de 34 a 86, le label est a
   102 et C va de 102 a 132.
   Gele, on verrait trois barres decalees, sans le recouvrement ni le
   vide, qui sont tout le terme. */
.ppm{position:relative;width:160px;height:106px;display:flex;flex-direction:column;gap:10px}
.ppm .ruler{position:relative;display:block;width:132px;height:5px;border-radius:3px;
  background:var(--s2);margin-left:14px;flex:0 0 auto}
.ppm .lbmk{position:absolute;left:102px;top:-4px;display:block;width:1px;height:13px;
  background:var(--a2)}
.ppm .hdd{position:absolute;left:0;top:-5px;display:block;width:2px;height:15px;background:var(--ink);
  animation:aPpmHead 4.4s linear infinite}
.ppm .steps{position:relative;display:flex;flex-direction:column;gap:8px;height:70px}
.ppm .st{position:absolute;display:flex;align-items:center;height:18px;border-radius:6px;
  padding:0 6px;box-sizing:border-box;background:var(--lime)}
.ppm .st em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  color:var(--on-accent);opacity:.85}
/* A de 0 a 52, B de 34 a 86, C de 102 a 132, tous decales de 14 */
.ppm .a{left:14px;top:0;width:52px;animation:aPpmA 4.4s steps(1,end) infinite}
.ppm .b{left:48px;top:26px;width:52px;background:var(--a2);animation:aPpmB 4.4s steps(1,end) infinite}
.ppm .c{left:116px;top:52px;width:30px;background:var(--a4);animation:aPpmC 4.4s steps(1,end) infinite}
/* elle s eteint aux deux bouts, le rembobinage ne se voit donc pas */
@keyframes aPpmHead{0%{transform:translateX(0);opacity:0}
  6%{opacity:1}94%{opacity:1}
  100%{transform:translateX(130px);opacity:0}}
/* A active pendant que la tete traverse sa portee, de 6 a 41 pour cent */
@keyframes aPpmA{0%,5%{opacity:.35}6%,41%{opacity:1}42%,100%{opacity:.35}}
@keyframes aPpmB{0%,28%{opacity:.35}29%,64%{opacity:1}65%,100%{opacity:.35}}
@keyframes aPpmC{0%,74%{opacity:.35}75%,94%{opacity:1}95%,100%{opacity:.35}}

/* UNE valeur, QUATRE proprietes derivees, et une cinquieme qui garde son
   propre etat. C est cette derniere qui prouve le terme, elle est
   d accord avec les autres au debut et ne l est plus ensuite, ce qui est
   exactement la desynchronisation que la consigne decrit.
   La version precedente montrait un anneau de progression, ce qui se
   refait avec n importe quoi et ne dit rien de la source unique.
   Le dasharray etait code en dur a 120 pour un cercle de 119.38 de
   perimetre. Ici il n y a plus de cercle, et les quatre sorties sont une
   largeur, une rotation, une couleur et une position.
   La piste fait 104 et la tete 2, la course vaut donc 102. La valeur fait
   un aller retour, et celle qui garde son propre etat tourne sur 4.3s
   contre 3.6, elle se decale donc un peu plus a chaque passage.
   Gele, on verrait cinq elements, sans savoir lequel derive et lequel
   derive. */
.npg{position:relative;width:160px;height:112px;display:flex;flex-direction:column;gap:10px}
.npg .src,.npg .drift{display:flex;align-items:center;gap:8px}
.npg em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;width:40px;text-align:right;
  letter-spacing:0;text-transform:none}
.npg .tk{position:relative;display:block;width:104px;height:6px;border-radius:3px;background:var(--s2)}
.npg .hdd{position:absolute;left:0;top:-4px;display:block;width:2px;height:14px;background:var(--ink);
  animation:aNpgP 3.6s linear infinite}
.npg .outs{display:flex;gap:8px;justify-content:flex-end}
.npg .o{position:relative;display:grid;place-items:center;width:32px;height:32px;border-radius:8px;
  background:var(--s1)}
/* les quatre sorties, toutes derivees de la meme valeur et du meme cycle */
.npg .w{display:block;width:0;height:8px;border-radius:4px;background:var(--lime);
  animation:aNpgW 3.6s linear infinite}
.npg .r{display:block;width:16px;height:16px;border-radius:4px;background:var(--lime);
  animation:aNpgR 3.6s linear infinite}
.npg .c{display:block;width:18px;height:18px;border-radius:50%;background:var(--s3);
  animation:aNpgC 3.6s linear infinite}
.npg .d{position:absolute;left:3px;display:block;width:8px;height:8px;border-radius:4px;
  background:var(--lime);animation:aNpgD 3.6s linear infinite}
/* son propre etat, sur son propre cycle, elle derive */
.npg .dh{position:absolute;left:0;top:-4px;display:block;width:2px;height:14px;background:var(--a4);
  animation:aNpgP 4.3s linear infinite}
/* La valeur monte de 0 a 1 puis redescend, ce qui est honnete puisque une
   progression liee a une entree se rembobine, et les quatre derivees
   suivent dans les deux sens, ce qui renforce la demonstration. */
@keyframes aNpgP{0%,6%{transform:translateX(0)}50%{transform:translateX(102px)}
  94%,100%{transform:translateX(0)}}
@keyframes aNpgW{0%,6%{width:0}50%{width:24px}94%,100%{width:0}}
@keyframes aNpgR{0%,6%{transform:rotate(0)}50%{transform:rotate(180deg)}
  94%,100%{transform:rotate(0)}}
@keyframes aNpgC{0%,6%{background:var(--s3)}50%{background:var(--lime)}
  94%,100%{background:var(--s3)}}
@keyframes aNpgD{0%,6%{transform:translateX(0)}50%{transform:translateX(18px)}
  94%,100%{transform:translateX(0)}}


/* UNE seule course, et c est la CADENCE qui change en route. La version
   precedente donnait 1.8s a une piste et 3.6s a l autre, ce qui fait deux
   timelines et non un multiplicateur, donc la scene demontrait le
   contraire du terme.
   Ici la piste du bas parcourt la meme distance que celle du haut, mais
   son multiplicateur passe de 1 a 0.25 puis revient a 1 et devient
   NEGATIF, ce qui rejoue tout a l envers. La jauge signee du bas le dit,
   a droite pour l avant et a gauche pour l arriere.
   Rien de tout cela ne s ecrit avec des durees. La piste du haut, a 1x
   constant, est la reference inerte.
   La piste fait 76 et le point 9, la course vaut donc 67.
   Gele, les deux points seraient quelque part, sans la cadence. */
.tsc{position:relative;width:158px;height:94px;display:flex;flex-direction:column;gap:14px}
.tsc .lane{display:flex;align-items:center;gap:8px}
/* reference a cadence constante, aucune animation du multiplicateur */
.tsc .n1{animation:aTscRef 4.6s linear infinite}
.tsc .n2{animation:aTscScaled 4.6s linear infinite}
.tsc .gauge{position:relative;display:block;height:5px;border-radius:3px;background:var(--s2);
  margin-top:auto}
.tsc .gauge b{position:absolute;top:0;bottom:0;width:50%;border-radius:3px}
.tsc .pos{left:50%;transform-origin:left;transform:scaleX(0);background:var(--a2);
  animation:aTscPos 4.6s linear infinite}
.tsc .neg{left:0;transform-origin:right;transform:scaleX(0);background:var(--a4);
  animation:aTscNeg 4.6s linear infinite}
.tsc .zero{position:absolute;left:50%;top:-3px;display:block;width:1px;height:11px;
  background:var(--ink-3)}
@keyframes aTscRef{0%,6%{transform:translateX(0)}50%{transform:translateX(67px)}
  94%,100%{transform:translateX(0)}}
/* 1x, puis 0.25x, puis 1x, puis negatif */
@keyframes aTscScaled{0%,8%{transform:translateX(0)}
  24%{transform:translateX(30px)}
  52%{transform:translateX(38px)}
  64%{transform:translateX(67px)}
  80%{transform:translateX(20px)}
  96%,100%{transform:translateX(0)}}
@keyframes aTscPos{0%,7%{transform:scaleX(0)}
  9%,23%{transform:scaleX(.6)}
  26%,50%{transform:scaleX(.15)}
  54%,63%{transform:scaleX(.6)}
  65%,100%{transform:scaleX(0)}}
@keyframes aTscNeg{0%,64%{transform:scaleX(0)}
  66%,95%{transform:scaleX(.6)}
  97%,100%{transform:scaleX(0)}}

/* UN rappel, N valeurs. La barre du haut est le tick. La rangee du milieu
   lit ce tick, ses quatre points sont donc rigoureusement ensemble a
   chaque image. La rangee du bas a quatre minuteurs a elle, de durees
   voisines mais differentes, et ils se DECALENT, un peu plus a chaque
   passage.
   La version precedente montrait quatre points en unisson, ce qui se
   refait avec quatre animations identiques et ne prouvait rien. La
   preuve est la derive de la seconde rangee.
   Gele, les huit points seraient quelque part, et l unisson comme la
   derive seraient invisibles. */
.tkl{position:relative;width:158px;height:102px;display:flex;flex-direction:column;gap:12px}
.tkl .tick{display:block;height:5px;border-radius:3px;background:var(--s2);overflow:hidden;
  flex:0 0 auto}
.tkl .tick b{display:block;width:100%;height:5px;border-radius:3px;background:var(--ink);
  transform-origin:left;animation:aTklTick 1.2s steps(1,end) infinite}
.tkl .one{display:flex;flex-direction:column;gap:7px}
.tkl em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.tkl .ok em{color:var(--lime)}
.tkl .ko em{color:var(--a4)}
.tkl .row{display:flex;gap:11px}
.tkl .row i{display:block;width:16px;height:16px;border-radius:8px;background:var(--lime)}
/* tous sur le meme tick, aucun delai, aucune duree propre */
.tkl .ok .row i{animation:aTklPulse 1.2s ease-in-out infinite}
/* quatre minuteurs a eux, ils derivent */
.tkl .ko .row i{background:var(--a4)}
.tkl .t1{animation:aTklPulse 1.2s ease-in-out infinite}
.tkl .t2{animation:aTklPulse 1.34s ease-in-out infinite}
.tkl .t3{animation:aTklPulse 1.48s ease-in-out infinite}
.tkl .t4{animation:aTklPulse 1.62s ease-in-out infinite}
/* il se vide puis se remplit, la boucle revient donc a son point de depart */
@keyframes aTklTick{0%,44%{transform:scaleX(1)}48%,96%{transform:scaleX(.04)}
  100%{transform:scaleX(1)}}
@keyframes aTklPulse{0%,100%{transform:scale(.5);opacity:.35}50%{transform:scale(1);opacity:1}}

/* ---------------- plomberie de composants ---------------- */
/* Le COMPTEUR DE RENDUS est la preuve, et c est tout ce que ce terme a de
   particulier. Les deux carres suivent la meme valeur et bougent
   pareillement. La difference est en dessous, la barre de rendus du haut
   reste a ZERO pendant tout le cycle, celle du bas se remplit image apres
   image.
   La version precedente faisait defiler un rouleau de chiffres, ce qui
   appartient a odometer et ne disait rien du cycle de rendu.
   La barre se vide a 94 pour cent, sinon la boucle sauterait.
   La piste fait 104 et le curseur 12, la course vaut donc 92.
   Gele, les deux carres seraient au meme endroit et la difference serait
   entierement invisible, ce qui est exactement le probleme. */
.mvl{position:relative;width:158px;height:106px;display:flex;flex-direction:column;gap:11px}
.mvl .src{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.mvl em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  width:52px;text-align:right;color:var(--ink-3)}
.mvl .ok em{color:var(--lime)}
.mvl .ko em{color:var(--a4)}
.mvl .tk{position:relative;display:block;width:96px;height:8px;border-radius:4px;background:var(--s2)}
.mvl .th{position:absolute;left:0;top:-2px;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--ink);animation:aMvlVal 3.4s cubic-bezier(.4,0,.4,1) infinite}
.mvl .one{display:flex;align-items:center;gap:8px}
.mvl .bx{position:relative;display:block;width:52px;height:20px;border-radius:6px;background:var(--s1)}
.mvl .sq{position:absolute;left:2px;top:2px;display:block;width:16px;height:16px;border-radius:4px;
  background:var(--lime);animation:aMvlSq 3.4s cubic-bezier(.4,0,.4,1) infinite}
.mvl .rn{position:relative;display:block;flex:1;height:8px;border-radius:4px;background:var(--s2);
  overflow:hidden}
.mvl .rn b{display:block;width:0;height:8px;border-radius:4px;background:var(--a4)}
/* zero rendu, la barre ne bouge jamais */
/* un rendu par image, la barre se remplit puis se vide a 94 */
.mvl .fill{animation:aMvlRn 3.4s linear infinite}
@keyframes aMvlVal{0%,6%{transform:translateX(0)}50%{transform:translateX(92px)}
  94%,100%{transform:translateX(0)}}
@keyframes aMvlSq{0%,6%{transform:translateX(0)}50%{transform:translateX(32px)}
  94%,100%{transform:translateX(0)}}
@keyframes aMvlRn{0%,6%{width:0}88%{width:100%}94%,100%{width:0}}

/* Quatre ARRETS, un PLATEAU et un BORNAGE, ce qu une simple liaison de
   deux valeurs ne montre pas. Les traits sur la piste d entree sont les
   arrets, a 0, 20, 44 et 60.
   Entre le deuxieme et le troisieme la sortie ne bouge PAS, elle est
   tenue a 40 pour cent, ce qui est le seul usage des arrets
   intermediaires. Passe le dernier arret a 60, l entree continue jusqu a
   76 et la sortie reste a 100, c est le bornage.
   La piste fait 76 et le point 9, la course vaut donc 67, et les arrets
   tombent a 0, 18, 39 et 53 de cette course.
   Gele, on verrait une barre a moitie, sans le plateau ni la butee. */
.rmp{position:relative;width:158px;height:102px;display:flex;flex-direction:column;gap:16px}
.rmp .lane{display:flex;align-items:center;gap:8px}
.rmp .tk u{position:absolute;top:-3px;display:block;width:1px;height:13px;
  background:rgba(var(--accent-rgb),.5)}
.rmp .s1{left:0}
.rmp .s2{left:18px}
.rmp .s3{left:39px}
.rmp .s4{left:53px}
.rmp .tk b{position:absolute;left:0;top:-1px;display:block;width:9px;height:9px;border-radius:5px;
  background:var(--lime);animation:aRmpIn 4.2s linear infinite}
.rmp .ob{position:relative;display:block;width:76px;height:16px;border-radius:5px;background:var(--s2);
  overflow:hidden}
.rmp .ob .fill{display:block;width:0;height:16px;border-radius:5px;background:var(--lime);
  animation:aRmpOut 4.2s linear infinite}
.rmp .note{position:absolute;right:0;bottom:0;font:600 6px/1 var(--mono);color:var(--ink-3);
  font-style:normal;letter-spacing:0;text-transform:none}
/* l entree parcourt les 67 en entier, arrets a 0, 18, 39 et 53 */
@keyframes aRmpIn{0%,6%{transform:translateX(0)}
  24%{transform:translateX(18px)}
  42%{transform:translateX(39px)}
  56%{transform:translateX(53px)}
  70%,80%{transform:translateX(67px)}
  96%,100%{transform:translateX(0)}}
/* 0, 40, 40 puis 100, et la sortie ne bouge plus passe le dernier arret */
@keyframes aRmpOut{0%,6%{width:0}
  24%{width:40%}
  42%{width:40%}
  56%{width:100%}
  70%,80%{width:100%}
  96%,100%{width:0}}

/* Le NOM voyage. La puce du haut le porte, et chaque enfant le reprend a
   son tour, en dessous, sur sa propre etiquette. Aucun enfant n est
   adresse individuellement.
   L ordonnancement est beforeChildren, le conteneur finit de s ouvrir
   AVANT que les enfants commencent, ce qui est une consigne explicite.
   Et chaque enfant l interprete differemment, le premier glisse, le
   deuxieme grandit, le troisieme se fond, parce que le parent dit quel
   etat et l enfant dit comment.
   La version precedente montrait un panneau et trois lignes en cascade,
   ce qui se refait avec n importe quel parent anime.
   Gele, on verrait un panneau ouvert avec trois lignes. */
.vpr{position:relative;width:158px;height:106px;padding-top:14px;box-sizing:border-box}
.vpr .nm{position:absolute;left:0;top:0;font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;padding:2px 5px;border-radius:3px;background:rgba(var(--accent-rgb),.22);
  color:var(--lime)}
.vpr .panel{position:relative;display:flex;flex-direction:column;justify-content:center;gap:9px;
  height:88px;padding:0 12px;box-sizing:border-box;border-radius:11px;background:var(--lime);
  transform-origin:center;animation:aVprP 4.4s cubic-bezier(.2,1,.3,1) infinite}
.vpr .ch{display:flex;align-items:center;gap:8px}
.vpr .ch i{display:block;flex:1;height:8px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.5)}
.vpr .ch u{font:600 6px/1 var(--mono);font-style:normal;text-decoration:none;letter-spacing:0;
  color:var(--on-accent);opacity:0}
/* chaque enfant interprete le meme nom a sa facon */
.vpr .c1 i{animation:aVprSlide 4.4s cubic-bezier(.2,.9,.25,1) infinite}
.vpr .c2 i{animation:aVprGrow 4.4s cubic-bezier(.2,1.5,.3,1) infinite}
.vpr .c3 i{animation:aVprFade 4.4s ease infinite}
.vpr .c1 u{animation:aVprNm1 4.4s steps(1,end) infinite}
.vpr .c2 u{animation:aVprNm2 4.4s steps(1,end) infinite}
.vpr .c3 u{animation:aVprNm3 4.4s steps(1,end) infinite}
/* le conteneur finit AVANT que les enfants commencent, a 30 pour cent */
@keyframes aVprP{0%,8%{transform:scale(.55);opacity:0}30%,80%{transform:scale(1);opacity:1}
  96%,100%{transform:scale(.55);opacity:0}}
@keyframes aVprSlide{0%,32%{transform:translateX(-18px);opacity:0}
  46%,80%{transform:translateX(0);opacity:1}92%,100%{transform:translateX(-18px);opacity:0}}
@keyframes aVprGrow{0%,38%{transform:scaleX(.2);opacity:0}
  54%,80%{transform:scaleX(1);opacity:1}92%,100%{transform:scaleX(.2);opacity:0}}
@keyframes aVprFade{0%,44%{opacity:0}62%,80%{opacity:1}92%,100%{opacity:0}}
@keyframes aVprNm1{0%,33%{opacity:0}34%,88%{opacity:.8}90%,100%{opacity:0}}
@keyframes aVprNm2{0%,39%{opacity:0}40%,88%{opacity:.8}90%,100%{opacity:0}}
@keyframes aVprNm3{0%,45%{opacity:0}46%,88%{opacity:.8}90%,100%{opacity:0}}

/* En meme temps, ou apres. A gauche l element qui part est sorti du flux
   des le debut, les deux voisines refermer donc l espace PENDANT sa
   sortie. A droite il reste dans le flux, il faut attendre qu il ait fini
   pour que le trou se referme, et l on voit ce trou.
   C est ce trou qui est le sujet, et il ne se voit qu en face de son
   contraire.
   Les rangees font 19 avec 7 de gouttiere, la remontee vaut donc 26.
   Gele, les deux colonnes montreraient deux rangees, sans savoir laquelle
   a attendu. */
.plq{display:flex;gap:14px;width:158px}
.plq .one{display:flex;flex-direction:column;gap:7px;flex:1}
.plq em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.plq .ok em{color:var(--lime)}
.plq .ko em{color:var(--a4)}
.plq .col{position:relative;display:block;height:73px}
.plq .col i{position:absolute;left:0;right:0;display:block;height:19px;border-radius:8px;
  background:var(--s3)}
.plq .r1{top:0}
.plq .go{top:26px;background:var(--lime)}
.plq .r3{top:52px}
/* la sortie est la meme des deux cotes */
.plq .ok .go{animation:aPlqGo 3.8s cubic-bezier(.3,.9,.3,1) infinite}
.plq .ko .go{animation:aPlqGo 3.8s cubic-bezier(.3,.9,.3,1) infinite}
/* a gauche la voisine remonte PENDANT la sortie, de 16 a 52 */
.plq .ok .r3{animation:aPlqNow 3.8s cubic-bezier(.3,.9,.3,1) infinite}
/* a droite elle attend la fin, de 52 a 68 */
.plq .ko .r3{animation:aPlqAfter 3.8s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aPlqGo{0%,16%{transform:scale(1);opacity:1}52%,84%{transform:scale(.86);opacity:0}
  96%,100%{transform:scale(1);opacity:1}}
@keyframes aPlqNow{0%,16%{transform:translateY(0)}52%,84%{transform:translateY(-26px)}
  96%,100%{transform:translateY(0)}}
@keyframes aPlqAfter{0%,52%{transform:translateY(0)}68%,84%{transform:translateY(-26px)}
  96%,100%{transform:translateY(0)}}

/* Deux vues de HAUTEURS DIFFERENTES, ce qui est la vraie consequence du
   mode. A gauche elles ne sont jamais ensemble, le conteneur peut donc
   se redimensionner entre les deux et chacune est mesuree seule. A droite
   elles se recouvrent, le conteneur doit tenir la plus haute en
   permanence, et l on voit les deux l une sur l autre.
   La courte fait 30, la haute 62, le conteneur de gauche passe donc de 30
   a 62 et celui de droite reste a 62.
   C est ce qui separe ce mode de fade-through, qui est une transition
   Material precise avec son trou de 90ms et son echelle de 0.92.
   Gele, on verrait deux vues empilees, sans savoir laquelle attend. */
.ebe{display:flex;gap:14px;width:158px}
.ebe .one{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.ebe em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ebe .ok em{color:var(--lime)}
.ebe .ko em{color:var(--a4)}
.ebe .bx{position:relative;display:block;border-radius:8px;background:var(--s1);overflow:hidden;
  padding:7px;box-sizing:border-box}
/* le conteneur suit la vue montee, il passe de 30 a 62 */
.ebe .ok .bx{height:30px;animation:aEbeBox 4.2s cubic-bezier(.3,.9,.3,1) infinite}
/* il doit tenir la plus haute en permanence */
.ebe .ko .bx{height:62px}
.ebe .v{position:absolute;left:7px;right:7px;top:7px;display:flex;flex-direction:column;gap:5px}
.ebe .v i{display:block;height:7px;border-radius:3px;flex:0 0 auto}
.ebe .a i{background:var(--s5)}
.ebe .b i{background:var(--lime)}
/* a gauche, sequentiel, elles ne se croisent jamais */
.ebe .ok .a{animation:aEbeWaitA 4.2s cubic-bezier(.3,.9,.3,1) infinite}
.ebe .ok .b{animation:aEbeWaitB 4.2s cubic-bezier(.3,.9,.3,1) infinite}
/* a droite, elles se recouvrent */
.ebe .ko .a{animation:aEbeConA 4.2s cubic-bezier(.3,.9,.3,1) infinite}
.ebe .ko .b{animation:aEbeConB 4.2s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aEbeBox{0%,16%{height:30px}42%,80%{height:62px}96%,100%{height:30px}}
@keyframes aEbeWaitA{0%,16%{opacity:1}30%,88%{opacity:0}96%,100%{opacity:1}}
@keyframes aEbeWaitB{0%,34%{opacity:0}48%,80%{opacity:1}92%,100%{opacity:0}}
@keyframes aEbeConA{0%,16%{opacity:1}48%,88%{opacity:0}96%,100%{opacity:1}}
@keyframes aEbeConB{0%,16%{opacity:0}48%,80%{opacity:1}92%,100%{opacity:0}}

/* Les QUATRE temps, nommes. First, la boite est mesuree, le fantome
   pointille la garde en memoire. Last, l element SAUTE instantanement a
   sa vraie position de mise en page, celle de droite. Invert, une
   transformation le ramene exactement sur le fantome, ce qui le fait
   ressembler a son etat d avant. Play, cette transformation est animee
   vers rien et il arrive donc a sa vraie place.
   Le saut de Last est instantane et c est le point, la mise en page est
   VRAIE des ce moment, seule l apparence est rattrapee ensuite.
   La version precedente translatait un carre entre deux cases, ce qui se
   refait sans FLIP, et ce carre n avait aucun fond declare.
   Les cases sont a 0 et a 62, la course vaut donc 62.
   Gele, on verrait un carre dans une case. */
.flp{position:relative;width:158px;height:104px}
.flp .cell{position:absolute;top:22px;display:block;width:50px;height:50px;border-radius:10px;
  box-sizing:border-box;border:1px dashed var(--s5)}
.flp .cell:nth-of-type(1){left:0}
.flp .cell:nth-of-type(2){left:62px}
/* le fantome, la boite mesuree a First */
.flp .ghost{position:absolute;left:9px;top:31px;display:block;width:32px;height:32px;border-radius:8px;
  box-sizing:border-box;border:1px dashed rgba(var(--accent-rgb),.6);
  animation:aFlpGhost 5s steps(1,end) infinite}
.flp .sq{position:absolute;left:9px;top:31px;display:block;width:32px;height:32px;border-radius:8px;
  background:var(--lime);animation:aFlpSq 5s cubic-bezier(.3,.9,.3,1) infinite}
.flp .beats{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:space-between}
.flp .beats em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  color:var(--ink-3)}
.flp .b1{animation:aFlpB1 5s steps(1,end) infinite}
.flp .b2{animation:aFlpB2 5s steps(1,end) infinite}
.flp .b3{animation:aFlpB3 5s steps(1,end) infinite}
.flp .b4{animation:aFlpB4 5s steps(1,end) infinite}
/* Last saute a 26, Invert ramene a 32, Play anime de 40 a 74 */
@keyframes aFlpSq{0%,25%{transform:translateX(0)}
  26%,31%{transform:translateX(62px)}
  32%,39%{transform:translateX(0)}
  74%,94%{transform:translateX(62px)}
  95%,100%{transform:translateX(0)}}
/* le fantome ne vit que pendant First, Invert et Play */
/* il reparait pour le First du cycle suivant, donc 100 egale 0 */
@keyframes aFlpGhost{0%,25%{opacity:.9}26%,31%{opacity:0}32%,74%{opacity:.9}
  75%,96%{opacity:0}100%{opacity:.9}}
@keyframes aFlpB1{0%,25%{color:var(--lime)}26%,94%{color:var(--ink-3)}95%,100%{color:var(--lime)}}
@keyframes aFlpB2{0%,25%{color:var(--ink-3)}26%,31%{color:var(--lime)}32%,100%{color:var(--ink-3)}}
@keyframes aFlpB3{0%,31%{color:var(--ink-3)}32%,39%{color:var(--lime)}40%,100%{color:var(--ink-3)}}
@keyframes aFlpB4{0%,39%{color:var(--ink-3)}40%,74%{color:var(--lime)}75%,100%{color:var(--ink-3)}}


/* Le RAYON est le tell, et il manquait. Les deux boites sont mises a
   l echelle 2.1 en largeur. Celle du haut divise son enfant par 2.1, donc
   0.476, ET son rayon horizontal par 2.1, donc 12 devient 5.71, rien ne
   change de forme. Celle du bas ne corrige rien, ses coins deviennent
   ovales et sa barre s etire.
   La version precedente corrigeait l enfant mais pas le rayon, alors que
   des coins ovales sur une carte arrondie sont ce qui se voit en premier.
   Gele, les deux boites seraient etirees pareil, et la correction serait
   entierement invisible. */
.scr{width:158px;height:100px;display:flex;flex-direction:column;gap:10px}
.scr .one{display:flex;flex-direction:column;gap:6px}
.scr em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.scr .ok em{color:var(--lime)}
.scr .ko em{color:var(--a4)}
.scr .box{position:relative;display:grid;place-items:center;width:52px;height:34px;
  border-radius:12px;background:var(--lime);transform-origin:left}
.scr .box i{display:block;width:26px;height:7px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.55)}
/* 12 divise par 2.1 vaut 5.71, et 1 divise par 2.1 vaut 0.476 */
.scr .ok .box{animation:aScrBox 3.4s cubic-bezier(.4,0,.4,1) infinite}
.scr .ok .box i{animation:aScrChild 3.4s cubic-bezier(.4,0,.4,1) infinite}
.scr .ko .box{animation:aScrRaw 3.4s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aScrBox{0%,10%{transform:scaleX(1);border-radius:12px}
  50%,80%{transform:scaleX(2.1);border-radius:5.71px/12px}
  96%,100%{transform:scaleX(1);border-radius:12px}}
@keyframes aScrChild{0%,10%{transform:scaleX(1)}50%,80%{transform:scaleX(.476)}
  96%,100%{transform:scaleX(1)}}
@keyframes aScrRaw{0%,10%{transform:scaleX(1)}50%,80%{transform:scaleX(2.1)}
  96%,100%{transform:scaleX(1)}}

/* ---------------- gestes, physique, assets ---------------- */
/* Ce qui se passe AU BORD est une decision, pas une limite. Le trait
   rouge est la borne, pose a 98. Le bouton fait 20 et part de 6, il
   atteint donc la borne a 72 de course.
   En haut, elasticite 0.2, le doigt depasse jusqu a 84 contre une
   resistance croissante et le bouton revient se poser a 72 au
   relachement. En bas, elasticite 0, il s arrete net a 72.
   La version precedente placait la borne a 98 et laissait le bouton la
   franchir des 84 de course, donc le repere ne voulait rien dire.
   Gele, on verrait deux boutons contre un trait. */
.dcs{width:158px;height:100px;display:flex;flex-direction:column;gap:16px;
  justify-content:center}
.dcs .one{display:flex;align-items:center;gap:9px}
.dcs em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  width:20px;text-align:right}
.dcs .ok em{color:var(--lime)}
.dcs .ko em{color:var(--a4)}
.dcs .rail{position:relative;display:block;width:118px;height:20px;border-radius:10px;
  background:var(--s2)}
/* la borne, a 98, soit 72 de course pour un bouton de 20 parti de 6 */
.dcs .limit{position:absolute;left:92px;top:-4px;display:block;width:2px;height:28px;
  border-radius:2px;background:rgba(var(--danger-rgb),.7)}
.dcs .handle{position:absolute;left:0;top:0;display:block;width:20px;height:20px;border-radius:10px;
  background:var(--lime)}
/* elastique, il depasse a 84 puis revient a 72 */
.dcs .ok .handle{animation:aDcsElastic 3.8s cubic-bezier(.3,.8,.3,1) infinite}
/* rigide, il s arrete net a 72 */
.dcs .ko .handle{animation:aDcsHard 3.8s cubic-bezier(.3,.8,.3,1) infinite}
@keyframes aDcsElastic{0%,10%{transform:translateX(0)}
  44%{transform:translateX(72px)}
  56%{transform:translateX(84px)}
  70%,88%{transform:translateX(72px)}
  100%{transform:translateX(0)}}
@keyframes aDcsHard{0%,10%{transform:translateX(0)}
  44%,88%{transform:translateX(72px)}
  100%{transform:translateX(0)}}

/* L ORIGINE est un choix, et c est tout ce qui distingue ce terme d une
   cascade lineaire. Les deux grilles sont identiques et ne different que
   par le point de depart, marque d un anneau. A gauche depuis le centre,
   les quatre cases centrales partent ensemble puis les huit du tour puis
   les quatre coins. A droite depuis le coin haut gauche, le retard vaut
   ligne plus colonne, donc sept valeurs distinctes.
   Une seule origine ne montrerait pas qu il y a un choix.
   Quatre colonnes de 9 avec 6 de gouttiere font 54, deux grilles avec 14
   entre elles font donc 122 pour 158.
   Gele, on verrait deux grilles de points identiques. */
.gst{width:158px;height:104px;display:flex;gap:14px;justify-content:center;align-items:center}
.gst .one{display:flex;flex-direction:column;align-items:center;gap:8px}
.gst em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;letter-spacing:0;
  text-transform:none}
.gst .gd{display:grid;grid-template-columns:repeat(4,9px);gap:6px}
.gst .gd i{display:block;width:9px;height:9px;border-radius:5px;background:var(--lime);
  animation:aGst 2.8s ease-out infinite}
/* origine au centre, trois anneaux */
.gst .c i:nth-child(6),.gst .c i:nth-child(7),
.gst .c i:nth-child(10),.gst .c i:nth-child(11){animation-delay:0s;
  box-shadow:0 0 0 2px rgba(var(--accent-rgb),.35)}
.gst .c i:nth-child(2),.gst .c i:nth-child(3),.gst .c i:nth-child(5),.gst .c i:nth-child(8),
.gst .c i:nth-child(9),.gst .c i:nth-child(12),.gst .c i:nth-child(14),
.gst .c i:nth-child(15){animation-delay:.18s}
.gst .c i:nth-child(1),.gst .c i:nth-child(4),
.gst .c i:nth-child(13),.gst .c i:nth-child(16){animation-delay:.36s}
/* origine au coin, le retard vaut ligne plus colonne fois 70ms */
.gst .k i:nth-child(1){animation-delay:0s;box-shadow:0 0 0 2px rgba(var(--accent-rgb),.35)}
.gst .k i:nth-child(2),.gst .k i:nth-child(5){animation-delay:.07s}
.gst .k i:nth-child(3),.gst .k i:nth-child(6),.gst .k i:nth-child(9){animation-delay:.14s}
.gst .k i:nth-child(4),.gst .k i:nth-child(7),.gst .k i:nth-child(10),
.gst .k i:nth-child(13){animation-delay:.21s}
.gst .k i:nth-child(8),.gst .k i:nth-child(11),.gst .k i:nth-child(14){animation-delay:.28s}
.gst .k i:nth-child(12),.gst .k i:nth-child(15){animation-delay:.35s}
.gst .k i:nth-child(16){animation-delay:.42s}
@keyframes aGst{0%,8%{transform:scale(.3);opacity:.2}30%,70%{transform:scale(1);opacity:1}
  94%,100%{transform:scale(.3);opacity:.2}}

/* La RESOLUTION du conflit, montree des deux facons. Les deux traits sont
   les deux cibles. En haut, auto, la premiere animation est tuee sur la
   propriete en conflit et le point rejoint proprement la seconde cible.
   En bas, aucune, les deux animations restent vivantes et ecrivent la
   meme valeur, le point BAT entre les deux cibles et le resultat depend
   de celle qui a tourne en dernier.
   La version precedente faisait diverger un point d une ligne pointillee,
   ce qui ne montrait ni le conflit ni sa resolution, et ce point n avait
   aucun fond declare, il etait invisible.
   Le rail fait 118 et le point 12, les deux cibles sont a 40 et 92 de
   course.
   Gele, les deux points seraient quelque part sur leur rail. */
.ovw{width:158px;height:100px;display:flex;flex-direction:column;gap:16px;justify-content:center}
.ovw .one{display:flex;align-items:center;gap:9px}
.ovw em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  width:26px;text-align:right}
.ovw .ok em{color:var(--lime)}
.ovw .ko em{color:var(--a4)}
.ovw .rail{position:relative;display:block;width:118px;height:12px;border-radius:6px;
  background:var(--s2)}
.ovw .rail u{position:absolute;top:-4px;display:block;width:1px;height:20px;
  background:rgba(var(--accent-rgb),.5)}
.ovw .ta{left:46px}
.ovw .tb{left:98px}
.ovw .dot{position:absolute;left:0;top:0;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--lime)}
/* auto, la premiere est tuee, le point rejoint la seconde cible */
.ovw .ok .dot{animation:aOvwAuto 3.6s cubic-bezier(.3,.9,.3,1) infinite}
/* aucune, les deux ecrivent et le point bat entre les deux */
.ovw .ko .dot{animation:aOvwNone 3.6s linear infinite}
@keyframes aOvwAuto{0%,8%{transform:translateX(0)}
  32%{transform:translateX(40px)}
  62%,86%{transform:translateX(92px)}
  98%,100%{transform:translateX(0)}}
@keyframes aOvwNone{0%,8%{transform:translateX(0)}
  32%{transform:translateX(40px)}
  40%{transform:translateX(92px)}
  46%{transform:translateX(42px)}
  52%{transform:translateX(90px)}
  58%{transform:translateX(44px)}
  64%{transform:translateX(88px)}
  70%,86%{transform:translateX(66px)}
  98%,100%{transform:translateX(0)}}

/* La TRAINEE qui n arrive jamais, et le seuil qui la termine. Le repere
   pointille est la cible, le petit trait est le seuil. Le point vert
   approche de plus en plus lentement, et a 74 pour cent il CALE le
   dernier demi pixel d un coup, parce que la distance restante n atteint
   jamais zero.
   Le point ambre est un ressort sur la meme cible. Il DEPASSE puis
   revient, ce qu un lerp amorti ne peut pas faire, et c est la seule
   difference qui compte entre les deux.
   Le rail fait 118, les points 14, la cible est a 90 de course et le
   seuil a 84.
   Gele, on verrait deux points pres d une cible, sans la trainee ni le
   depassement. */
.dlp{width:158px;height:104px;display:flex;flex-direction:column;gap:12px;justify-content:center}
.dlp .rail{position:relative;display:block;width:118px;height:36px;border-radius:8px;
  background:var(--s1);margin:0 auto}
.dlp .target{position:absolute;left:90px;top:3px;display:block;width:14px;height:14px;
  border-radius:4px;box-sizing:border-box;border:1.5px dashed var(--s6);
  animation:aDlpTarget 4.2s steps(1,end) infinite}
/* le seuil, a 84, six pixels avant la cible */
.dlp .thr{position:absolute;left:84px;top:0;display:block;width:1px;height:36px;
  background:rgba(var(--accent-rgb),.45)}
.dlp .chase{position:absolute;left:0;top:3px;display:block;width:14px;height:14px;border-radius:7px;
  background:var(--lime);animation:aDlpChase 4.2s linear infinite}
.dlp .spring{position:absolute;left:0;top:20px;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--a4);animation:aDlpSpring 4.2s cubic-bezier(.2,1.5,.3,1) infinite}
.dlp .tags{display:flex;gap:14px;justify-content:center}
.dlp .tags em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.dlp .ok{color:var(--lime)}
.dlp .ko{color:var(--a4)}
@keyframes aDlpTarget{0%,10%{opacity:.35}12%,88%{opacity:1}94%,100%{opacity:.35}}
/* approche exponentielle, la trainee se tasse puis cale a 74 */
@keyframes aDlpChase{0%,12%{transform:translateX(0)}
  30%{transform:translateX(52px)}
  46%{transform:translateX(72px)}
  60%{transform:translateX(80px)}
  72%{transform:translateX(84px)}
  74%,88%{transform:translateX(90px)}
  98%,100%{transform:translateX(0)}}
/* un ressort, lui, depasse et revient */
@keyframes aDlpSpring{0%,12%{transform:translateX(0)}
  52%{transform:translateX(102px)}
  68%,88%{transform:translateX(90px)}
  98%,100%{transform:translateX(0)}}

/* UN SEUL ressort, DEUX vocabulaires. Les deux premieres pistes ont
   exactement le meme mouvement, la meme courbe et la meme duree, parce
   qu elles decrivent la meme chose, une fois en duree et rebond et une
   fois en raideur, amortissement et masse.
   La version precedente donnait deux courbes differentes aux deux pistes,
   ce qui montrait deux ressorts et non deux facons d en decrire un.
   La troisieme piste a les deux jeux definis en meme temps. La barre de
   duree en dessous s arrete a 520ms, et cette piste ne s y arrete PAS,
   parce que les valeurs physiques gagnent et la duree est ignoree sans un
   mot. C est le Common trap de la fiche.
   La piste fait 76 et le point 9, la course vaut donc 67.
   Gele, les trois pistes seraient au meme endroit. */
.spc2{position:relative;width:158px;height:104px;display:flex;flex-direction:column;gap:11px;
  padding-right:6px;box-sizing:border-box;justify-content:center}
.spc2 .goal{position:absolute;right:2px;top:8px;bottom:22px;width:2px;border-radius:2px;
  background:rgba(var(--accent-rgb),.4)}
.spc2 .ko .lb{color:var(--a4)}
/* les deux premieres, meme courbe et meme duree, un seul ressort */
.spc2 .v1{animation:aSpc 3.6s cubic-bezier(.15,1.6,.35,1) infinite}
.spc2 .v2{animation:aSpc 3.6s cubic-bezier(.15,1.6,.35,1) infinite}
/* les deux jeux definis, la physique gagne et la duree est ignoree */
.spc2 .v3{animation:aSpcBoth 3.6s cubic-bezier(.15,1.6,.35,1) infinite}
.spc2 .dur{position:relative;display:block;height:4px;border-radius:2px;background:var(--s2);
  margin-left:44px;width:76px}
.spc2 .dur b{position:absolute;left:0;top:0;bottom:0;display:block;width:0;border-radius:2px;
  background:var(--ink-3);animation:aSpcDur 3.6s linear infinite}
/* la duree declaree finit a 62 pour cent */
@keyframes aSpcDur{0%,12%{width:0}62%,88%{width:100%}96%,100%{width:0}}
@keyframes aSpc{0%,12%{transform:translateX(0)}62%,88%{transform:translateX(67px)}
  100%{transform:translateX(0)}}
/* elle finit a 82, bien apres la duree qu on lui a donnee */
@keyframes aSpcBoth{0%,12%{transform:translateX(0)}82%,92%{transform:translateX(67px)}
  100%{transform:translateX(0)}}

/* Un GRAPHE, avec ses conditions ecrites sur les aretes. Le jeton
   voyage, et l arete active s allume, ce qui dit que c est le graphe qui
   decide de la suite. L application ne joue rien par son nom, elle
   rapporte une condition.
   La version precedente montrait trois boites et un jeton sans aucune
   arete ni condition, donc rien ne disait que les transitions ont des
   regles.
   Gele, on verrait trois etats nommes et un jeton dans l un d eux, ce qui
   est deja la moitie, mais pas les regles. */
.stm{position:relative;width:158px;height:104px}
.stm .node{position:absolute;display:grid;place-items:center;width:38px;height:30px;
  border-radius:8px;box-sizing:border-box;border:1.5px solid var(--s5);background:var(--s1)}
.stm .node em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;letter-spacing:0;
  text-transform:none}
.stm .n1{left:0;top:36px}
.stm .n2{left:58px;top:0}
.stm .n3{right:0;top:52px}
/* les aretes, avec leur condition */
.stm .ed{position:absolute;display:block;height:1.5px;border-radius:2px;background:var(--s3)}
.stm .e1{left:36px;top:30px;width:26px;transform:rotate(-24deg);transform-origin:left;
  animation:aStmE1 4.4s steps(1,end) infinite}
.stm .e2{left:94px;top:24px;width:32px;transform:rotate(30deg);transform-origin:left;
  animation:aStmE2 4.4s steps(1,end) infinite}
.stm .e3{left:38px;top:60px;width:78px;transform:rotate(4deg);transform-origin:left;
  animation:aStmE3 4.4s steps(1,end) infinite}
.stm .tok{position:absolute;left:10px;top:44px;display:block;width:14px;height:14px;border-radius:4px;
  background:var(--lime);animation:aStm 4.4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aStm{0%,10%{transform:translate(0,0)}
  34%,46%{transform:translate(58px,-36px)}
  70%,82%{transform:translate(110px,16px)}
  98%,100%{transform:translate(0,0)}}
@keyframes aStmE1{0%,11%{background:var(--s3)}12%,33%{background:var(--lime)}
  34%,100%{background:var(--s3)}}
@keyframes aStmE2{0%,46%{background:var(--s3)}47%,69%{background:var(--lime)}
  70%,100%{background:var(--s3)}}
@keyframes aStmE3{0%,82%{background:var(--s3)}83%,97%{background:var(--lime)}
  98%,100%{background:var(--s3)}}

/* TROIS types, puisque la definition en nomme trois, et chacun se
   comporte autrement. Le booleen TIENT son etat, le temoin reste allume
   tant que l interrupteur est mis. Le nombre porte un niveau continu, le
   temoin suit la position du curseur. Le declencheur se produit une fois
   et s oublie, le temoin pulse et retombe alors que le bouton est encore
   enfonce.
   C est cette derniere ligne qui compte le plus, un declencheur est
   consomme, le definir deux fois dans la meme image ne le declenche
   qu une fois.
   La version precedente ne montrait qu un interrupteur, donc un type sur
   trois.
   Gele, on verrait trois entrees et trois temoins. */
.smi{width:158px;height:104px;display:flex;flex-direction:column;gap:12px;justify-content:center}
.smi .rw{display:flex;align-items:center;gap:9px}
.smi em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;width:38px;text-align:right;
  letter-spacing:0;text-transform:none}
.smi .sw{position:relative;display:block;width:34px;height:18px;border-radius:10px;background:var(--s2);
  flex:0 0 auto;animation:aSmiSwBg 4s steps(1,end) infinite}
.smi .sw i{position:absolute;left:3px;top:3px;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--ink);animation:aSmiSw 4s cubic-bezier(.2,1.2,.3,1) infinite}
.smi .sl{position:relative;display:block;width:34px;height:5px;border-radius:3px;background:var(--s2);
  flex:0 0 auto}
.smi .sl i{position:absolute;left:0;top:-4px;display:block;width:12px;height:12px;border-radius:6px;
  background:var(--ink);animation:aSmiSl 4s cubic-bezier(.4,0,.4,1) infinite}
.smi .bt{position:relative;display:block;width:34px;height:18px;border-radius:5px;background:var(--s2);
  flex:0 0 auto;animation:aSmiBtBg 4s steps(1,end) infinite}
.smi .bt i{position:absolute;left:11px;top:6px;display:block;width:12px;height:6px;border-radius:3px;
  background:var(--ink)}
.smi .out{display:block;width:56px;height:14px;border-radius:7px;background:var(--s2)}
/* il tient son etat tant que l interrupteur est mis */
.smi .ob{animation:aSmiOutB 4s steps(1,end) infinite}
/* il suit le niveau en continu */
.smi .on{transform-origin:left;animation:aSmiOutN 4s cubic-bezier(.4,0,.4,1) infinite}
/* il pulse une fois et retombe, alors que le bouton est encore enfonce */
.smi .ot{animation:aSmiOutT 4s cubic-bezier(.2,.9,.3,1) infinite}
@keyframes aSmiSw{0%,16%{transform:translateX(0)}26%,74%{transform:translateX(16px)}
  84%,100%{transform:translateX(0)}}
@keyframes aSmiSwBg{0%,20%{background:var(--s2)}21%,79%{background:var(--lime)}
  80%,100%{background:var(--s2)}}
@keyframes aSmiOutB{0%,20%{background:var(--s2)}21%,79%{background:var(--lime)}
  80%,100%{background:var(--s2)}}
@keyframes aSmiSl{0%,12%{transform:translateX(0)}52%{transform:translateX(22px)}
  92%,100%{transform:translateX(0)}}
@keyframes aSmiOutN{0%,12%{transform:scaleX(.1)}52%{transform:scaleX(1)}
  92%,100%{transform:scaleX(.1)}}
@keyframes aSmiBtBg{0%,34%{background:var(--s2)}35%,68%{background:var(--s4)}
  69%,100%{background:var(--s2)}}
@keyframes aSmiOutT{0%,35%{background:var(--s2);transform:scaleY(1)}
  38%{background:var(--a2);transform:scaleY(1.5)}
  52%,100%{background:var(--s2);transform:scaleY(1)}}


/* Les DEUX EXTREMES sont dessines en pointille, et la bouche pleine se
   trouve entre eux, a la position que dit la jauge du bas. C est ce qui
   montre que la pose visible n est PAS dans le fichier, elle est calculee.
   La version precedente morphait une bouche entre deux formes sans montrer
   ni le poids ni les extremes, donc rien ne disait qu il s agissait d un
   melange plutot que de deux keyframes.
   Les deux traces ont la meme structure de commandes, M puis C, sans quoi
   d n interpolerait pas du tout.
   La piste fait 76 et le point 9, la course vaut donc 67.
   Gele, on verrait une bouche entre deux fantomes, ce qui est deja
   presque tout, mais pas le lien avec le nombre. */
.bst{position:relative;width:152px;height:96px}
.bst svg{display:block;width:152px;height:60px}
/* les deux poses dessinees, en pointille */
.bst .gh{fill:none;stroke:var(--s4);stroke-width:2;stroke-dasharray:3 4}
/* la pose calculee, entre les deux */
.bst .mouth{fill:none;stroke:var(--lime);stroke-width:4;animation:aBst 3.8s ease-in-out infinite}
.bst .eye{position:absolute;top:16px;display:block;width:9px;height:9px;border-radius:5px;
  background:var(--lime)}
.bst .eye.l{left:44px}
.bst .eye.r{right:44px}
.bst .w{position:absolute;left:0;right:0;bottom:4px;display:flex;align-items:center;gap:7px;
  justify-content:center}
.bst .w em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;letter-spacing:0;
  text-transform:none}
.bst .w .tk{position:relative;display:block;width:76px;height:6px;border-radius:3px;
  background:var(--s2)}
.bst .w .tk b{position:absolute;left:0;top:-1.5px;display:block;width:9px;height:9px;border-radius:5px;
  background:var(--a2);animation:aBstW 3.8s ease-in-out infinite}
@keyframes aBst{0%,100%{d:path("M22 40C42 20 88 20 108 40")}
  50%{d:path("M22 24C42 46 88 46 108 24")}}
@keyframes aBstW{0%,100%{transform:translateX(0)}50%{transform:translateX(67px)}}

/* Le RE-EXPORT est la preuve, et il n etait pas montre. A 52 pour cent
   deux images sont INSEREES au debut, elles poussent tout le reste de 42
   vers la droite. En haut la selection suit le NOM, elle se decale donc de
   42 aussi et reste sur les deux cases claires, le bon contenu. En bas
   elle est codee en images 3 et 4, elle ne bouge pas, et elle designe
   maintenant deux cases sombres.
   C est exactement ce que dit la consigne, un nom survit au re-export et
   un numero non.
   Les cases font 17 avec 4 de marge, le pas vaut donc 21. Deux images
   inserees valent 42 de decalage, et la selection couvre deux cases plus
   leur marge, soit 38.
   Gele, les deux rangees seraient identiques. */
.mkr{position:relative;width:158px;height:100px;display:flex;flex-direction:column;gap:14px;
  justify-content:center}
.mkr .one{display:flex;flex-direction:column;gap:6px}
.mkr em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.mkr .ok em{color:var(--lime)}
.mkr .ko em{color:var(--a4)}
.mkr .tl{position:relative;display:flex}
.mkr .f{display:block;width:17px;height:26px;margin-right:4px;border-radius:4px;box-sizing:border-box;
  border:1px solid var(--line);flex:0 0 auto}
.mkr .g{background:var(--s1)}
.mkr .l{background:var(--lime);border-color:transparent}
/* les deux images inserees, leur largeur ET leur marge partent de zero */
.mkr .n2{background:var(--s2);width:0;margin-right:0;overflow:hidden;
  animation:aMkrNew 5s steps(1,end) infinite}
.mkr .sel{position:absolute;left:0;top:-3px;display:block;width:38px;height:32px;border-radius:6px;
  box-sizing:border-box;border:1.5px solid var(--a2)}
/* elle suit le nom, elle se decale de 42 au re-export */
.mkr .ok .sel{animation:aMkrByName 5s steps(1,end) infinite}
/* elle est codee en dur, elle ne bouge jamais, aucune animation */
.mkr .ko .sel{transform:translateX(42px)}
@keyframes aMkrNew{0%,51%{width:0;margin-right:0}52%,94%{width:17px;margin-right:4px}
  96%,100%{width:0;margin-right:0}}
/* avant, le contenu clair commence a 42. apres l insertion de 42, a 84. */
@keyframes aMkrByName{0%,51%{transform:translateX(42px)}52%,94%{transform:translateX(84px)}
  96%,100%{transform:translateX(42px)}}

