﻿/* ============================================================
   Motionary, demos E
   Collection Platform Animations, Apple, Material, Fluent
   ============================================================ */

/* petites pistes reutilisees dans cette collection */
.kft .lane,.msc .lane,.spr2 .lane{display:flex;align-items:center;gap:8px}
.kft .lb,.msc .lb,.spr2 .lb{font:600 9px/1 var(--mono);color:var(--ink-3);width:44px;text-align:right;font-style:normal;letter-spacing:.04em}
.kft .tk,.msc .tk,.spr2 .tk,.emp .tk{position:relative;display:block;width:78px;height:7px;border-radius:4px;background:var(--s2)}
.msc .tk b,.spr2 .tk b,.emp .tk b{position:absolute;left:0;top:-1px;width:9px;height:9px;border-radius:5px;background:var(--lime);display:block}

/* ---------------- Apple ---------------- */
/* Il faut une GRILLE, sinon rien ne dit que la vue revient dans la case
   d ou elle vient. Les cinq autres cases ne bougent jamais, elles
   reservent leur place, et c est ce qui rend le retour lisible. Un seul
   rectangle qui grandit, ce que faisait la version precedente, ne
   raconte aucune navigation.
   Le cadre est interpole, gauche haut largeur hauteur, jamais par un
   scale non uniforme. Un scale de 4.62 sur 4, ce qu utilisait la
   version precedente, ecrase tout ce qui est a l interieur et donne un
   rayon de coin faux, ce qui est precisement ce que la consigne
   interdit.
   Le rayon passe de 8 a 12, la valeur de la vue complete.
   Le retour est plus lent et sur une autre courbe, parce qu il est
   pilote par un geste et non par une duree.
   Gele, on verrait une grille avec une case ouverte. */
.zt{position:relative;width:132px;height:96px;border-radius:10px;background:var(--s1);
  overflow:hidden;padding:3px;box-sizing:border-box}
.zt .cell{position:absolute;width:38px;height:42px;border-radius:8px;background:var(--s3)}
.zt .cell:nth-of-type(1){left:3px;top:3px}
.zt .cell:nth-of-type(2){left:47px;top:3px}
.zt .cell:nth-of-type(3){left:91px;top:3px}
.zt .cell:nth-of-type(4){left:3px;top:51px}
.zt .cell:nth-of-type(5){left:47px;top:51px}
/* la case ouverte est celle du milieu en bas, elle garde sa place */
.zt .hot{left:91px;top:51px;z-index:3;overflow:hidden;padding:8px;box-sizing:border-box;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  display:flex;flex-direction:column;gap:6px;justify-content:flex-end;
  animation:aZoomT 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.zt .hot i{display:block;height:5px;border-radius:3px;background:rgba(var(--on-accent-rgb),.7);
  opacity:0;animation:aZoomD 4.6s ease infinite}
.zt .hot .d1{width:70%}
.zt .hot .d2{width:46%}
.zt .hot .d3{width:58%}
@keyframes aZoomT{0%,12%{left:91px;top:51px;width:38px;height:42px;border-radius:8px}
  46%,74%{left:3px;top:3px;width:126px;height:90px;border-radius:12px;
    animation-timing-function:cubic-bezier(.4,0,.2,1)}
  96%,100%{left:91px;top:51px;width:38px;height:42px;border-radius:8px}}
@keyframes aZoomD{0%,34%{opacity:0}48%,72%{opacity:1}84%,100%{opacity:0}}

/* Le CRAN se prouve en le ratant. La feuille est tiree jusqu a 48 pour
   cent, au dessus du cran medium pose a 58, puis relachee, et elle
   REDESCEND se poser sur le cran. Sans ce depassement suivi du retour,
   une feuille qui s arrete a une hauteur ne dit pas qu il y avait un
   cran, seulement qu on l a lachee la.
   Le contenu CHANGE d un cran a l autre, resume a medium et liste a
   large. La consigne le demande explicitement, et c est ce qui fait
   d un cran un etat du panneau et pas une taille.
   Le curseur suit le bord haut pendant les deux glissements et se leve
   au relachement, ce qui dit lequel des deux mouvements est un geste et
   lequel est le ressort.
   La feuille fait 100 de haut. translateY 58 pour cent laisse donc 42
   au dessus du bas, ce qui est la position du repere s1, et 14 pour
   cent en laisse 86, la position de s2.
   Gele, on verrait un panneau a mi hauteur. */
.sdt{position:relative;width:112px;height:100px;border-radius:12px;background:var(--s1);overflow:hidden}
.sdt .bg{position:absolute;inset:0;background:var(--s0)}
.sdt .stop{position:absolute;left:0;right:0;height:1px;background:rgba(var(--accent-rgb),.3)}
.sdt .s1{bottom:42px}
.sdt .s2{bottom:86px}
.sdt .sheet{position:absolute;left:0;right:0;bottom:0;height:100px;border-radius:12px 12px 0 0;
  background:var(--lime);padding:14px 12px 0;box-sizing:border-box;
  animation:aDetent 6s cubic-bezier(.2,1.5,.3,1) infinite}
.sdt .grab{position:absolute;top:6px;left:50%;margin-left:-12px;width:24px;height:3px;border-radius:2px;
  background:rgba(var(--on-accent-rgb),.4)}
.sdt .ct{position:absolute;left:12px;right:12px;top:16px;display:flex;flex-direction:column;gap:6px;
  opacity:0}
.sdt .ct i{display:block;height:5px;border-radius:3px;background:rgba(var(--on-accent-rgb),.55)}
.sdt .sum i:first-child{height:8px;width:64%}
.sdt .list i:first-child{height:8px;width:52%}
.sdt .sum{animation:aDetentSum 6s steps(1,end) infinite}
.sdt .list{animation:aDetentList 6s steps(1,end) infinite}
.sdt .cur{left:44px;top:-8px;animation:aDetentCur 6s cubic-bezier(.2,1.5,.3,1) infinite}
/* depassement a 48 puis retour au cran a 58, puis depassement a 6 puis
   retour au cran a 14 */
@keyframes aDetent{0%,6%{transform:translateY(100%)}
  20%{transform:translateY(48%)}
  28%,40%{transform:translateY(58%)}
  54%{transform:translateY(6%)}
  62%,84%{transform:translateY(14%)}
  96%,100%{transform:translateY(100%)}}
@keyframes aDetentSum{0%,27%{opacity:0}29%,53%{opacity:1}55%,100%{opacity:0}}
@keyframes aDetentList{0%,60%{opacity:0}62%,86%{opacity:1}88%,100%{opacity:0}}
/* le curseur ne suit que les glissements, il se leve au relachement */
@keyframes aDetentCur{0%{transform:translateY(108px);opacity:0}
  8%{transform:translateY(108px);opacity:1}
  20%{transform:translateY(49px);opacity:1}
  24%{transform:translateY(49px);opacity:0}
  42%{transform:translateY(59px);opacity:0}
  46%{transform:translateY(59px);opacity:1}
  54%{transform:translateY(7px);opacity:1}
  58%{transform:translateY(7px);opacity:0}
  99%,100%{transform:translateY(108px);opacity:0}}


/* La VITESSE decide, pas la distance, et c est la consigne que la
   version precedente ne montrait pas. Premier temps, un glissement LENT
   mene jusqu a 58, bien au dela du seuil pointille pose a 42, puis
   relache, et la feuille revient quand meme. Second temps, une breve
   chiquenaude a 26 seulement, en dessous du seuil, et elle part.
   Cette inversion est toute la demonstration, un lecteur qui croit que
   le seuil suffit se trompe deux fois dans la meme scene.
   La vue du dessous reste VIVANTE, sa pastille continue de battre
   pendant toute la fermeture, la consigne le demande explicitement.
   La feuille est representee de 92 a 100 pour cent, la boucle revient
   donc a son point de depart sans rembobinage visible.
   Gele, on verrait un panneau a mi hauteur, sans savoir s il part ou
   s il revient. */
.idm{position:relative;width:118px;height:106px;border-radius:12px;background:var(--s0);overflow:hidden}
.idm .live{position:absolute;inset:0;padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:8px}
.idm .live .im{display:block;height:38px;border-radius:7px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.idm .live .t{display:block;height:6px;width:70%;border-radius:3px;background:var(--s4)}
.idm .live .beat{display:block;width:9px;height:9px;border-radius:50%;background:var(--a2);
  animation:aIdmBeat 1.2s ease-in-out infinite}
.idm .thr{position:absolute;left:0;right:0;top:66px;height:1px;z-index:2;
  background:repeating-linear-gradient(90deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.idm .sheet{position:absolute;left:0;right:0;bottom:0;height:82px;border-radius:12px 12px 0 0;
  background:var(--lime);z-index:3;padding:14px 12px 0;box-sizing:border-box;
  animation:aIdm 5.2s cubic-bezier(.3,.8,.3,1) infinite}
.idm .grab{position:absolute;top:6px;left:50%;margin-left:-12px;width:24px;height:3px;border-radius:2px;
  background:rgba(var(--on-accent-rgb),.4)}
.idm .sheet .l{display:block;height:6px;border-radius:3px;background:rgba(var(--on-accent-rgb),.5);
  margin-bottom:7px}
.idm .sheet .l2{width:58%}
.idm .cur{left:52px;top:24px;z-index:4;animation:aIdmCur 5.2s cubic-bezier(.3,.8,.3,1) infinite}
/* lent jusqu a 58, au dela du seuil de 42, et pourtant annule.
   rapide jusqu a 26, en deca du seuil, et pourtant valide. */
@keyframes aIdm{0%,6%{transform:translateY(0)}
  34%,38%{transform:translateY(58px)}
  52%,58%{transform:translateY(0)}
  64%,66%{transform:translateY(26px)}
  80%,92%{transform:translateY(106px)}
  100%{transform:translateY(0)}}
/* le curseur ne suit que les glissements et se leve a chaque relachement */
@keyframes aIdmCur{0%{transform:translateY(0);opacity:0}
  6%{transform:translateY(0);opacity:1}
  34%{transform:translateY(58px);opacity:1}
  38%{transform:translateY(58px);opacity:0}
  57%{transform:translateY(0);opacity:0}
  59%{transform:translateY(0);opacity:1}
  64%{transform:translateY(26px);opacity:1}
  67%{transform:translateY(26px);opacity:0}
  99%,100%{transform:translateY(0);opacity:0}}
@keyframes aIdmBeat{0%,100%{transform:scale(1);opacity:.6}50%{transform:scale(1.4);opacity:1}}

/* L effet lit les COUCHES du glyphe, et c est ce qu aucune
   transformation generique ne sait faire. A gauche variableColor allume
   le point puis les trois arcs vers l exterieur, en cumulatif. A
   droite, le meme symbole recoit une mise a l echelle ecrite a la main,
   qui deplace tout le glyphe d un bloc et ignore entierement sa
   structure. C est le Common trap de la fiche, mis cote a cote.
   La version precedente empilait trois barres qui grandissaient, ce qui
   ne se distinguait d aucune cascade generique.
   Une keyframe par couche plutot qu un delai partage, sinon la remise a
   zero de la derniere deborderait au dela du cycle.
   Gele, on verrait deux icones de wifi identiques. */
.sfx{display:flex;gap:16px;align-items:flex-start}
.sfx .one{display:flex;flex-direction:column;align-items:center;gap:7px}
.sfx .gl{display:block;width:62px;height:50px}
.sfx .gl path{fill:none;stroke-width:9;stroke-linecap:round}
.sfx em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.sfx .bad em{color:var(--a4)}
.sfx .vc .a0{fill:var(--s3);animation:aSfxL0 2.6s steps(1,end) infinite}
.sfx .vc .a1{stroke:var(--s3);animation:aSfxL1 2.6s steps(1,end) infinite}
.sfx .vc .a2{stroke:var(--s3);animation:aSfxL2 2.6s steps(1,end) infinite}
.sfx .vc .a3{stroke:var(--s3);animation:aSfxL3 2.6s steps(1,end) infinite}
/* le piege, tout le glyphe bouge d un bloc, les couches sont ignorees */
.sfx .sc{transform-origin:50% 82%;animation:aSfxScale 2.6s ease-in-out infinite}
.sfx .sc .a0{fill:var(--lime)}
.sfx .sc path{stroke:var(--lime)}
@keyframes aSfxL0{0%,7%{fill:var(--s3)}8%,92%{fill:var(--lime)}94%,100%{fill:var(--s3)}}
@keyframes aSfxL1{0%,23%{stroke:var(--s3)}24%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aSfxL2{0%,39%{stroke:var(--s3)}40%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aSfxL3{0%,55%{stroke:var(--s3)}56%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aSfxScale{0%,8%{transform:scale(1)}40%{transform:scale(1.16)}72%,100%{transform:scale(1)}}

/* Quatre PHASES, et toutes les proprietes basculent AUX MEMES
   instants. C est la difference avec les pistes de keyframes, ou
   chacune suit son propre calendrier. Ici position, echelle et rotation
   changent ensemble a 25, 50 et 75 pour cent, et chaque phase porte sa
   propre courbe, ce qui est la seule raison de decouper un mouvement en
   phases.
   La rangee de pastilles dit quelle phase est en cours, sans quoi une
   liste ordonnee d etats ne se distingue pas d une animation continue.
   La version precedente n avait ni fond ni couleur sur son objet, il
   etait litteralement invisible.
   Gele, on verrait un carre quelque part sur son trajet. */
.pha{position:relative;width:132px;height:96px}
.pha .obj{position:absolute;left:56px;bottom:26px;display:block;width:24px;height:24px;
  border-radius:7px;background:var(--lime);animation:aPhase 3.2s infinite}
.pha .pips{position:absolute;left:0;right:0;bottom:6px;display:flex;justify-content:center;gap:7px}
.pha .pips i{display:block;width:7px;height:7px;border-radius:50%;background:var(--s3)}
.pha .p1{animation:aPhaseP1 3.2s steps(1,end) infinite}
.pha .p2{animation:aPhaseP2 3.2s steps(1,end) infinite}
.pha .p3{animation:aPhaseP3 3.2s steps(1,end) infinite}
.pha .p4{animation:aPhaseP4 3.2s steps(1,end) infinite}
/* une courbe par phase, ce que la fiche appelle sa propre courbe */
@keyframes aPhase{
  0%{transform:translateY(0) scale(1) rotate(0);animation-timing-function:cubic-bezier(.1,.9,.2,1)}
  25%{transform:translateY(-42px) scale(1.22) rotate(-14deg);animation-timing-function:linear}
  50%{transform:translateY(-42px) scale(.9) rotate(14deg);animation-timing-function:cubic-bezier(.6,0,.9,.4)}
  75%{transform:translateY(-14px) scale(1.1) rotate(0);animation-timing-function:cubic-bezier(.2,.9,.3,1)}
  100%{transform:translateY(0) scale(1) rotate(0)}}
@keyframes aPhaseP1{0%,24%{background:var(--lime)}25%,99%{background:var(--s3)}100%{background:var(--lime)}}
@keyframes aPhaseP2{0%,24%{background:var(--s3)}25%,49%{background:var(--lime)}50%,100%{background:var(--s3)}}
@keyframes aPhaseP3{0%,49%{background:var(--s3)}50%,74%{background:var(--lime)}75%,100%{background:var(--s3)}}
@keyframes aPhaseP4{0%,74%{background:var(--s3)}75%,99%{background:var(--lime)}100%{background:var(--s3)}}

/* Une SEULE duree partagee, trois pistes, trois instants de pointe
   differents. Les trois tetes de lecture avancent en verrouillage et
   chacune atteint son repere a un moment distinct, 30, 45 puis 62 pour
   cent. L echelle culmine quand la tete de la piste scale touche son
   repere, le decalage quand celle d offset touche le sien, la rotation
   en dernier.
   La version precedente donnait 2s a une piste et 3.2s a l autre. Deux
   durees differentes derivent l une par rapport a l autre et ne se
   resynchronisent jamais, ce qui est un autre effet, et c est le Common
   trap de la fiche.
   Trois elements imbriques, un par propriete, parce que c est
   exactement ce que le terme decrit.
   La piste fait 78 et la tete 2, la course vaut donc 78, et le repere
   d une piste qui culmine a 30 pour cent est pose a 23.4.
   Gele, on verrait trois barres et un carre. */
.kft{display:flex;align-items:center;gap:12px;width:160px}
.kft .lanes{display:flex;flex-direction:column;gap:9px}
.kft .lane{display:flex;align-items:center;gap:8px}
.kft .mk{position:absolute;top:-2px;display:block;width:2px;height:11px;border-radius:1px;
  background:rgba(var(--accent-rgb),.55)}
.kft .m1{left:23.4px}
.kft .m2{left:35.1px}
.kft .m3{left:48.4px}
.kft .ph{position:absolute;left:0;top:-3px;display:block;width:2px;height:13px;border-radius:1px;
  background:var(--ink);animation:aKftHead 3.4s linear infinite}
.kft .sq{display:block;width:28px;height:28px;flex:0 0 auto;animation:aKftOff 3.4s ease-in-out infinite}
.kft .sqR{display:block;width:28px;height:28px;animation:aKftRot 3.4s ease-in-out infinite}
.kft .sqS{display:block;width:28px;height:28px;border-radius:8px;background:var(--lime);
  animation:aKftScale 3.4s ease-in-out infinite}
/* la tete s eteint aux deux bouts, le rembobinage ne se voit pas */
@keyframes aKftHead{0%{transform:translateX(0);opacity:0}
  4%{opacity:1}96%{opacity:1}
  100%{transform:translateX(78px);opacity:0}}
@keyframes aKftScale{0%,100%{transform:scale(1)}30%{transform:scale(1.32)}}
@keyframes aKftOff{0%,100%{transform:translateY(0)}45%{transform:translateY(-18px)}}
@keyframes aKftRot{0%,100%{transform:rotate(0)}62%{transform:rotate(100deg)}}

/* Le flou et l assombrissement tombent sur LES MEMES IMAGES que la
   levee, de 26 a 34 pour cent. Les separer se lirait comme deux
   evenements et la profondeur ne prendrait pas.
   L echelle vaut 1.03, la valeur de la consigne. La version precedente
   en faisait 1.06, ce qui fait passer la cible pour un nouvel objet
   plutot que pour la rangee qu on vient de presser.
   La sequence complete est la, et c est ce qui manquait. L anneau de
   pression grandit de 10 a 26 pour cent, soit les 400 a 500ms d appui
   demandes, PUIS la levee, PUIS le menu. Sans le temps d appui, rien ne
   dit pourquoi la rangee se souleve.
   Le flou seul ne suffirait pas, il baisse le detail mais pas la
   luminosite, d ou l opacite qui descend en meme temps.
   Gele, on verrait une rangee nette devant deux rangees floues. */
.cml{position:relative;width:132px;height:138px;padding:10px;box-sizing:border-box}
.cml .rowb{position:absolute;left:10px;right:10px;display:block;height:20px;border-radius:7px;
  background:var(--s3)}
.cml .r1{top:10px;animation:aCmlBg 4.4s ease infinite}
.cml .r2{top:37px}
.cml .r3{top:64px;animation:aCmlBg 4.4s ease infinite;animation-delay:.04s}
.cml .lift{background:var(--lime);z-index:3;animation:aCmlLift 4.4s cubic-bezier(.2,.9,.25,1) infinite}
.cml .press{position:absolute;left:50%;top:50%;display:block;width:8px;height:8px;margin:-4px 0 0 -4px;
  border-radius:50%;box-sizing:border-box;border:1.5px solid rgba(var(--on-accent-rgb),.6);opacity:0;
  animation:aCmlPress 4.4s cubic-bezier(.3,0,.4,1) infinite}
.cml .menu{position:absolute;left:10px;right:10px;top:92px;display:flex;flex-direction:column;gap:7px;
  padding:9px;box-sizing:border-box;border-radius:9px;background:var(--s1);z-index:4;
  transform-origin:50% 0;animation:aCmlMenu 4.4s cubic-bezier(.2,.9,.25,1) infinite}
.cml .menu i{display:block;height:5px;border-radius:3px;background:var(--s5)}
.cml .menu i:nth-child(2){width:74%}
.cml .menu i:nth-child(3){width:58%}
/* la levee et le flou tombent sur les memes images, 26 a 34 */
@keyframes aCmlLift{0%,26%{transform:scale(1);box-shadow:none}
  34%,80%{transform:scale(1.03);box-shadow:0 10px 20px rgba(var(--shadow-rgb),.55)}
  92%,100%{transform:scale(1);box-shadow:none}}
@keyframes aCmlBg{0%,26%{filter:blur(0);opacity:1}
  34%,80%{filter:blur(2.6px);opacity:.4}
  92%,100%{filter:blur(0);opacity:1}}
/* l appui dure de 10 a 26 pour cent, les 400 a 500ms de la consigne */
@keyframes aCmlPress{0%,10%{transform:scale(1);opacity:0}
  14%{opacity:.9}
  26%{transform:scale(4.4);opacity:0}
  100%{transform:scale(1);opacity:0}}
@keyframes aCmlMenu{0%,34%{transform:scaleY(.6);opacity:0}
  44%,80%{transform:scaleY(1);opacity:1}
  90%,100%{transform:scaleY(.6);opacity:0}}

/* TROIS presentations, pas deux, puisque la definition en nomme trois.
   Minimal 26 sur 18, compact 88 sur 22, expanded 112 sur 52, et le
   rayon vaut toujours la moitie de la plus petite dimension, donc 9, 11
   puis 26. La surface reste une vraie capsule a chaque taille
   intermediaire, ce que la consigne demande.
   Le contenu se croise en fondu APRES la forme, il arrive une fois la
   place faite. Le faire glisser pendant que le conteneur change de
   taille produirait deux mouvements contradictoires.
   Chacune des trois tailles est un etat de repos valide, une
   interruption en cours de morphing ne laisse donc jamais la surface a
   une taille que rien n a dessinee.
   Gele, on verrait une pilule. */
.din{display:grid;place-items:center;width:136px;height:80px}
.din .pill{position:relative;display:block;width:26px;height:18px;border-radius:9px;
  background:var(--void);border:1px solid var(--s2);overflow:hidden;
  animation:aIsland 5.2s cubic-bezier(.3,.9,.3,1) infinite}
.din .st{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:7px;
  padding:0 9px;box-sizing:border-box;opacity:0}
.din .minimal{animation:aIslandS1 5.2s ease infinite}
.din .compact{animation:aIslandS2 5.2s ease infinite}
.din .expanded{animation:aIslandS3 5.2s ease infinite;flex-direction:row;justify-content:flex-start}
.din .minimal i{display:block;width:7px;height:7px;border-radius:50%;background:var(--lime)}
.din .compact .d{display:block;width:9px;height:9px;border-radius:50%;background:var(--lime);flex:0 0 auto}
.din .compact .b{display:block;flex:1;height:5px;border-radius:3px;background:var(--s5)}
.din .expanded .ic{display:block;width:22px;height:22px;border-radius:7px;background:var(--lime);
  flex:0 0 auto}
.din .col{display:flex;flex-direction:column;gap:6px;flex:1}
.din .col i{display:block;height:5px;border-radius:3px;background:var(--s5)}
.din .col i:last-child{width:56%}
/* rayon toujours egal a la moitie de la plus petite dimension */
@keyframes aIsland{0%,10%{width:26px;height:18px;border-radius:9px}
  22%,40%{width:88px;height:22px;border-radius:11px}
  54%,76%{width:112px;height:52px;border-radius:26px}
  90%,100%{width:26px;height:18px;border-radius:9px}}
/* le contenu arrive apres la forme, une fois la place faite */
@keyframes aIslandS1{0%,8%{opacity:1}12%,88%{opacity:0}94%,100%{opacity:1}}
@keyframes aIslandS2{0%,20%{opacity:0}26%,44%{opacity:1}50%,100%{opacity:0}}
@keyframes aIslandS3{0%,52%{opacity:0}58%,76%{opacity:1}82%,100%{opacity:0}}

/* Le reflet est CALCULE sur le decalage du panneau, il n a pas de
   timeline a lui. Memes instants, signe oppose, facteur 1.6, donc
   quand le panneau va de 16 vers la droite le reflet part de 26 vers la
   gauche. Un vrai reflet speculaire se deplace a l inverse de la surface
   qui le porte.
   La version precedente balayait le reflet de 0 a 130 sur sa propre
   courbe, ce qui est une brillance decorative. Elle continuerait de
   balayer avec le panneau a l arret.
   Le rayon se RESSERRE du cote ou la surface accoste, de 20 a 8, ce que
   la consigne demande, une forme sur le point de fusionner avec un bord
   cesse de pretendre flotter.
   Le panneau fait 106 dans un cadre de 138, la course de 16 l amene
   donc exactement au bord, ni avant ni apres.
   Gele, on verrait un panneau de verre pose sur un fond. */
.lgl{position:relative;width:138px;height:92px;border-radius:12px;overflow:hidden}
.lgl .under{position:absolute;inset:0;background:linear-gradient(120deg,var(--lime),var(--a3) 55%,var(--a4))}
.lgl .glass{position:absolute;left:16px;top:26px;width:106px;height:40px;border-radius:20px;
  background:rgba(var(--ink-rgb),.16);border:1px solid rgba(var(--ink-rgb),.3);
  backdrop-filter:blur(7px);overflow:hidden;animation:aGlass 5s ease-in-out infinite}
.lgl .spec{position:absolute;top:-20px;left:36px;display:block;width:30px;height:80px;
  background:linear-gradient(90deg,transparent,rgba(var(--ink-rgb),.85),transparent);
  animation:aSpec 5s ease-in-out infinite}
@keyframes aGlass{0%,100%{transform:translateX(0);border-radius:20px}
  25%{transform:translateX(16px);border-radius:20px 8px 8px 20px}
  50%{transform:translateX(0);border-radius:20px}
  75%{transform:translateX(-16px);border-radius:8px 20px 20px 8px}}
@keyframes aSpec{0%,100%{transform:translateX(0) rotate(18deg)}
  25%{transform:translateX(-26px) rotate(18deg)}
  50%{transform:translateX(0) rotate(18deg)}
  75%{transform:translateX(26px) rotate(18deg)}}

/* Le bon et le mauvais COTE A COTE, parce que la rarete ne se demontre
   qu en face de son contraire. En haut, un seul anneau part a l image
   exacte ou le bouton franchit le repere, une fois a l aller et une
   fois au retour. En bas, un anneau part a chaque image du glissement,
   ce que la consigne interdit nommement, et le lecteur voit
   immediatement que la seconde ne veut plus rien dire.
   La piste fait 92 et le bouton 18, la course vaut donc 74. Le centre
   du bouton atteint le repere pose a 58 quand son bord gauche est a 49,
   soit 66 pour cent de la course, ce qui tombe a 43 pour cent du cycle.
   L anneau est pose exactement la.
   Gele, on verrait deux curseurs et deux reperes. */
.hpt{display:flex;flex-direction:column;gap:14px;width:152px}
/* meme raison que .lt, la primitive .row est horizontale */
.hpt .col{display:flex;flex-direction:column;gap:6px}
.hpt em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.hpt .ko em{color:var(--a4)}
.hpt .track{position:relative;display:block;width:92px;height:24px;border-radius:12px;background:var(--s2)}
.hpt .tick{position:absolute;left:58px;top:-3px;display:block;width:1px;height:30px;
  background:rgba(var(--accent-rgb),.5)}
.hpt .knob{position:absolute;top:3px;left:3px;display:block;width:18px;height:18px;border-radius:50%;
  background:var(--lime);animation:aKnob 3.6s cubic-bezier(.3,.8,.3,1) infinite}
.hpt .ring{position:absolute;top:0;left:49px;display:block;width:24px;height:24px;border-radius:50%;
  box-sizing:border-box;border:2px solid var(--lime);opacity:0}
.hpt .ok .ring{animation:aHapticOk 3.6s cubic-bezier(.2,.7,.3,1) infinite}
.hpt .ko .ring{border-color:var(--a4);animation:aHapticKo 3.6s linear infinite}
/* course de 74, le repere est franchi a 43 pour cent puis a 77 */
@keyframes aKnob{0%,10%{transform:translateX(0)}60%,68%{transform:translateX(71px)}
  94%,100%{transform:translateX(0)}}
/* une frappe par franchissement, rien entre les deux */
@keyframes aHapticOk{0%,42%{transform:scale(.6);opacity:0}
  44%{transform:scale(.6);opacity:.9}
  50%{transform:scale(1.9);opacity:0}
  76%{transform:scale(.6);opacity:0}
  78%{transform:scale(.6);opacity:.9}
  84%{transform:scale(1.9);opacity:0}
  100%{transform:scale(.6);opacity:0}}
/* le contre exemple, une frappe a chaque image du glissement */
@keyframes aHapticKo{0%{transform:scale(.6);opacity:0}
  4%{transform:scale(.6);opacity:.8}
  10%{transform:scale(1.7);opacity:0}
  14%{transform:scale(.6);opacity:.8}
  20%{transform:scale(1.7);opacity:0}
  24%{transform:scale(.6);opacity:.8}
  30%{transform:scale(1.7);opacity:0}
  34%{transform:scale(.6);opacity:.8}
  40%{transform:scale(1.7);opacity:0}
  44%{transform:scale(.6);opacity:.8}
  50%{transform:scale(1.7);opacity:0}
  54%{transform:scale(.6);opacity:.8}
  60%{transform:scale(1.7);opacity:0}
  100%{transform:scale(.6);opacity:0}}


/* Deux cibles de FORMES differentes, et le surlignage suit le rayon de
   chacune. C est la consigne de decouper l effet sur la vraie zone
   cliquable, et elle ne se demontre qu avec deux formes.
   Une seule cible est allumee a la fois. Celle qu on quitte s eteint a
   l instant du depart du regard, celle qu on rejoint s allume a
   l instant de l arrivee. Ni anticipation ni delai, la consigne le
   demande, et la version precedente allumait le bouton a 22 pour cent
   pour un regard qui arrivait a 44.
   La cible non regardee est le voisin inerte qui prouve que l effet
   appartient au regard et pas au panneau.
   Gele, on verrait deux cibles dont une allumee, sans savoir pourquoi. */
.gzh{position:relative;width:144px;height:80px}
.gzh .tg{position:absolute;display:block;background:var(--s2)}
.gzh .pill{left:10px;top:25px;width:62px;height:30px;border-radius:15px;
  animation:aGazeA 4s cubic-bezier(.3,.9,.3,1) infinite}
.gzh .dot{right:14px;top:23px;width:34px;height:34px;border-radius:50%;
  animation:aGazeB 4s cubic-bezier(.3,.9,.3,1) infinite}
.gzh .eye{position:absolute;left:36px;top:35px;display:block;width:10px;height:10px;border-radius:50%;
  background:var(--ink);box-shadow:0 0 0 5px rgba(var(--ink-rgb),.12);
  animation:aGazeEye 4s ease-in-out infinite}
/* centre de la pastille 41 et 40, centre du disque 127 et 40, donc 86 */
@keyframes aGazeEye{0%,8%{transform:translateX(0)}20%,56%{transform:translateX(86px)}
  68%,100%{transform:translateX(0)}}
/* allumee de 68 a 100 et de 0 a 8, soit quand le regard est dessus */
@keyframes aGazeA{0%,8%{background:var(--s4);transform:translateY(-2px)}
  9%,67%{background:var(--s2);transform:translateY(0)}
  68%,100%{background:var(--s4);transform:translateY(-2px)}}
@keyframes aGazeB{0%,19%{background:var(--s2);transform:translateY(0)}
  20%,56%{background:var(--s4);transform:translateY(-2px)}
  57%,100%{background:var(--s2);transform:translateY(0)}}

/* La VITESSE choisit l index, pas le bord le plus proche. Le glissement
   lent de 8 a 28 pour cent parcourt 52, soit un element. La chiquenaude
   de 40 a 48 pour cent parcourt 104, soit deux elements, dans un temps
   deux fois et demie plus court. Snapper toujours au plus proche
   ramenerait la chiquenaude a un seul pas et rendrait la liste collante.
   Le trait pointille de gauche est la ligne d alignement. Les bords des
   elements viennent s y poser, et un element partiel reste toujours
   visible a droite, ce que la consigne demande.
   Le rail fait 6 fois 52 moins 8, soit 304, pour une fenetre de 138. La
   course utile vaut donc 166 et le palier le plus loin, 156, tient.
   Le retour se fait par de vrais defilements en sens inverse, jamais par
   un rembobinage.
   Gele, on verrait un rail aligne sur un trait. */
.snp{position:relative;width:138px;height:80px;overflow:hidden}
.snp .line{position:absolute;left:0;top:0;bottom:0;display:block;width:1px;z-index:3;
  background:repeating-linear-gradient(180deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.snp .rail{display:flex;gap:8px;animation:aSnapRail 5.6s infinite}
.snp .rail i{display:block;flex:none;width:44px;height:56px;border-radius:9px;background:var(--s3)}
.snp .rail i:first-child{background:var(--lime)}
/* lent, un element. rapide, deux. et un ressort a l arrivee. */
@keyframes aSnapRail{
  0%,8%{transform:translateX(0);animation-timing-function:cubic-bezier(.3,1.4,.35,1)}
  28%,40%{transform:translateX(-52px);animation-timing-function:cubic-bezier(.2,1.5,.3,1)}
  48%,62%{transform:translateX(-156px);animation-timing-function:cubic-bezier(.3,1.4,.35,1)}
  78%,86%{transform:translateX(-104px);animation-timing-function:cubic-bezier(.2,1.5,.3,1)}
  96%,100%{transform:translateX(0)}}

/* ---------------- Material et Android ---------------- */
/* Il faut une LISTE, sinon rien ne dit que la surface etait l un de
   plusieurs elements. Les deux voisines gardent leur place et ne
   bougent jamais.
   Le contenu sortant part VITE, de 12 a 20 pour cent, et l entrant
   arrive LENTEMENT, de 30 a 52, une fois que le conteneur a presque
   toute sa taille. C est la consigne, et la version precedente n avait
   aucun contenu sortant, donc la moitie du croisement manquait.
   Les limites sont interpolees, gauche haut largeur hauteur, pas mises
   a l echelle, sinon le contenu s etirerait.
   Un seul fond partage sous les deux etats, donc rien ne se croise
   derriere le contenu et la surface se lit comme continue.
   Gele, on verrait une carte ouverte au dessus d une liste. */
.ctf{position:relative;width:144px;height:112px}
.ctf .it{position:absolute;left:12px;display:block;width:118px;height:22px;border-radius:11px;
  background:var(--s3)}
.ctf .i1{top:8px}
.ctf .i3{top:68px}
.ctf .box{position:absolute;left:12px;top:38px;display:block;width:118px;height:22px;border-radius:11px;
  background:var(--lime);overflow:hidden;z-index:3;
  animation:aCtf 4.4s cubic-bezier(.2,.9,.25,1) infinite}
.ctf .out{position:absolute;left:11px;top:8px;display:block;width:56px;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.55);animation:aCtfOut 4.4s linear infinite}
.ctf .in{position:absolute;left:11px;display:block;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.42);opacity:0;animation:aCtfIn 4.4s ease infinite}
.ctf .l1{top:22px;width:88px}
.ctf .l2{top:36px;width:56px}
@keyframes aCtf{0%,12%{left:12px;top:38px;width:118px;height:22px;border-radius:11px}
  46%,78%{left:8px;top:8px;width:128px;height:96px;border-radius:14px}
  94%,100%{left:12px;top:38px;width:118px;height:22px;border-radius:11px}}
/* sortant rapide, 8 pour cent de cycle */
@keyframes aCtfOut{0%,12%{opacity:1}20%,86%{opacity:0}94%,100%{opacity:1}}
/* entrant plus lent, 22 pour cent, et il demarre une fois la place faite */
@keyframes aCtfIn{0%,30%{opacity:0}52%,78%{opacity:1}86%,100%{opacity:0}}

/* La DIRECTION est toute l information, il faut donc montrer les deux
   sens. En avant de 10 a 34 pour cent, les deux vues glissent vers la
   gauche. En arriere de 58 a 82, elles glissent vers la DROITE. Rejouer
   la transition avant sur le retour est la facon la plus courante dont
   ce motif ment a l utilisateur, et une scene qui ne montre qu un sens
   ne peut pas le dire.
   Les deux vues bougent du meme cote et du meme decalage, 30, la valeur
   de la consigne, et non d une largeur d ecran, sinon on lirait un
   carrousel.
   Les deux sont en fondu, donc le point de croisement est lisible et
   aucune n est brievement opaque sur l autre.
   Les deux pastilles disent l etape en cours, 1 puis 2 puis 1.
   Gele, on verrait deux vues superposees a mi course. */
.sax{position:relative;width:132px;height:92px;overflow:hidden;border-radius:10px;background:var(--s0)}
.sax .pg{position:absolute;left:0;right:0;top:0;bottom:16px;display:flex;flex-direction:column;
  justify-content:center;gap:9px;padding:0 14px;box-sizing:border-box;border-radius:10px}
.sax .pg i{display:block;height:8px;border-radius:4px}
.sax .a{background:var(--s2);animation:aSaxA 5.2s cubic-bezier(.2,0,0,1) infinite}
.sax .a i{background:var(--s5)}
.sax .a .s{width:56%}
.sax .b{background:var(--lime);animation:aSaxB 5.2s cubic-bezier(.2,0,0,1) infinite}
.sax .b i{background:rgba(var(--on-accent-rgb),.5)}
.sax .b .s{width:44%}
.sax .pips{position:absolute;left:0;right:0;bottom:5px;z-index:4;display:flex;justify-content:center;
  gap:7px}
.sax .pips i{display:block;width:7px;height:7px;border-radius:50%;background:var(--s3)}
.sax .q1{animation:aSaxQ1 5.2s steps(1,end) infinite}
.sax .q2{animation:aSaxQ2 5.2s steps(1,end) infinite}
/* en avant les deux vont a gauche, en arriere les deux vont a droite */
@keyframes aSaxA{0%,10%{transform:translateX(0);opacity:1}
  34%,58%{transform:translateX(-30px);opacity:0}
  82%,100%{transform:translateX(0);opacity:1}}
@keyframes aSaxB{0%,10%{transform:translateX(30px);opacity:0}
  34%,58%{transform:translateX(0);opacity:1}
  82%,100%{transform:translateX(30px);opacity:0}}
@keyframes aSaxQ1{0%,21%{background:var(--lime)}22%,69%{background:var(--s3)}
  70%,100%{background:var(--lime)}}
@keyframes aSaxQ2{0%,21%{background:var(--s3)}22%,69%{background:var(--lime)}
  70%,100%{background:var(--s3)}}

/* Le TROU est le sujet, et il ne se voit qu en face de son contraire.
   A gauche, la vue sortante disparait entierement de 10 a 24 pour cent,
   PERSONNE n est visible de 24 a 30, puis l entrante arrive de 30 a 50
   en passant de 0.92 a 1. A droite, les deux se croisent, ce qui est un
   fondu croise et affirme une continuite entre deux destinations qui
   n en ont aucune.
   L echelle de l entrante vaut 0.92, la valeur de la consigne. La
   version precedente en faisait 0.9 et sa sortante ne revenait jamais,
   d ou une boucle qui sautait.
   La sortante reste a l echelle 1, les mettre toutes les deux a
   l echelle donnerait une seule chose qui zoome.
   Gele, les deux colonnes seraient identiques. */
.fth{display:flex;gap:16px}
.fth .one{display:flex;flex-direction:column;align-items:center;gap:8px}
.fth .stg{position:relative;display:block;width:56px;height:56px}
.fth .stg i{position:absolute;inset:0;display:block;border-radius:14px}
.fth .a{background:var(--s3)}
.fth .b{background:var(--lime)}
.fth em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.fth .ko em{color:var(--a4)}
.fth .ok .a{animation:aFthA 3.6s cubic-bezier(.2,0,0,1) infinite}
.fth .ok .b{animation:aFthB 3.6s cubic-bezier(.2,0,0,1) infinite}
.fth .ko .a{animation:aFthKoA 3.6s cubic-bezier(.2,0,0,1) infinite}
.fth .ko .b{animation:aFthKoB 3.6s cubic-bezier(.2,0,0,1) infinite}
/* sequence, avec un trou de 24 a 30 et un autre de 78 a 84 */
@keyframes aFthA{0%,10%{opacity:1;transform:scale(1)}
  24%,84%{opacity:0;transform:scale(.92)}
  96%,100%{opacity:1;transform:scale(1)}}
@keyframes aFthB{0%,30%{opacity:0;transform:scale(.92)}
  50%,64%{opacity:1;transform:scale(1)}
  78%,100%{opacity:0;transform:scale(.92)}}
/* le piege, les deux se croisent et il n y a aucun trou */
@keyframes aFthKoA{0%,10%{opacity:1}40%,64%{opacity:0}94%,100%{opacity:1}}
@keyframes aFthKoB{0%,10%{opacity:0}40%,64%{opacity:1}94%,100%{opacity:0}}

/* Un jeu remplace TOUS les ressorts a la fois, ce n est ni une courbe
   ni une duree. Chaque rangee anime donc trois choses ensemble, une
   carte qui glisse, une puce qui grossit, un point qui monte, et seule
   la courbe du jeu change d une rangee a l autre.
   La version precedente montrait un point par rangee, ce qui montrait
   deux courbes et pas deux jeux.
   Le trait pointille est la cible de la carte. En standard elle s y
   pose, en expressive elle la depasse puis revient. La difference est
   l amortissement, pas la duree, les deux rangees durent 3.2s.
   Gele, on verrait deux rangees identiques. */
.msc{display:flex;flex-direction:column;gap:12px;width:154px}
.msc .grp{display:flex;flex-direction:column;gap:6px}
.msc em{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.msc .row{position:relative;display:block;height:30px}
.msc .tgt{position:absolute;left:96px;top:-2px;display:block;width:1px;height:34px;
  background:repeating-linear-gradient(180deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.msc .crd{position:absolute;left:0;top:5px;display:block;width:26px;height:20px;border-radius:6px;
  background:var(--lime)}
.msc .chp{position:absolute;left:112px;top:9px;display:block;width:14px;height:12px;border-radius:6px;
  background:var(--a2)}
.msc .dot{position:absolute;left:140px;top:20px;display:block;width:9px;height:9px;border-radius:50%;
  background:var(--s5)}
/* meme duree, meme keyframes, seul l amortissement change */
.msc .std .crd{animation:aMscCard 3.2s cubic-bezier(.2,0,0,1) infinite}
.msc .std .chp{animation:aMscChip 3.2s cubic-bezier(.2,0,0,1) infinite}
.msc .std .dot{animation:aMscDot 3.2s cubic-bezier(.2,0,0,1) infinite}
.msc .exp .crd{animation:aMscCard 3.2s cubic-bezier(.15,1.7,.35,1) infinite}
.msc .exp .chp{animation:aMscChip 3.2s cubic-bezier(.15,1.7,.35,1) infinite}
.msc .exp .dot{animation:aMscDot 3.2s cubic-bezier(.15,1.7,.35,1) infinite}
/* la carte fait 26 et sa cible est a 96, la course vaut donc 70 */
@keyframes aMscCard{0%,12%{transform:translateX(0)}58%,88%{transform:translateX(70px)}
  100%{transform:translateX(0)}}
@keyframes aMscChip{0%,12%{transform:scale(1)}58%,88%{transform:scale(1.7)}100%{transform:scale(1)}}
@keyframes aMscDot{0%,12%{transform:translateY(0)}58%,88%{transform:translateY(-16px)}
  100%{transform:translateY(0)}}

/* Le partage se fait sur CE QUI EST ANIME, pas sur le ressenti. En
   haut une position, elle a le droit de depasser son repere puis de
   revenir, parce qu une chose physique arrivant vite peut passer son
   point de repos. En bas une opacite, portee par une barre de valeur
   avec son plafond a 100 pour cent, et elle arrive PILE dessus.
   Une opacite qui rebondirait serait ecretee, le rebond deviendrait donc
   un palier a fond suivi d un creux, ce qui se lit comme un
   scintillement et pas comme un ressort. C est le Common trap de la
   fiche.
   La version precedente faisait translater un point sur la piste
   effects, ce qui melangeait les deux categories que le terme separe.
   Le bloc fait 30 et son repere est a 108, la course vaut donc 78, et
   le depassement va a 86 avant de revenir a 78.
   Gele, on verrait un bloc et une barre. */
.spr2{display:flex;flex-direction:column;gap:12px;width:154px}
.spr2 .grp{display:flex;flex-direction:column;gap:6px}
.spr2 em{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.spr2 .row{position:relative;display:block;height:26px}
.spr2 .tgt,.spr2 .ceil{position:absolute;top:-2px;display:block;width:1px;height:30px;
  background:repeating-linear-gradient(180deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.spr2 .tgt{left:108px}
.spr2 .ceil{left:138px}
.spr2 .blk{position:absolute;left:0;top:3px;display:block;width:30px;height:20px;border-radius:6px;
  background:var(--lime);animation:aSprSp 3.4s cubic-bezier(.2,0,0,1) infinite}
.spr2 .bar{position:absolute;left:0;top:8px;display:block;width:138px;height:10px;border-radius:5px;
  background:var(--s2);overflow:hidden}
.spr2 .bar b{display:block;width:0;height:10px;border-radius:5px;background:var(--a2);
  animation:aSprEf 3.4s cubic-bezier(.2,0,0,1) infinite}
/* la position depasse son repere de 8 puis revient s y poser */
@keyframes aSprSp{0%,10%{transform:translateX(0)}
  48%{transform:translateX(86px)}
  62%,88%{transform:translateX(78px)}
  100%{transform:translateX(0)}}
/* l opacite arrive pile sur son plafond, elle ne le depasse jamais */
@keyframes aSprEf{0%,10%{width:0}56%,88%{width:100%}100%{width:0}}

/* LES TROIS courbes, puisque la fiche en nomme trois. Les points ont la
   meme duree, la difference de forme est donc directement comparable.
   Emphasized part lentement et arrive sur une longue trainee, decelerate
   n adoucit que l arrivee parce que son depart etait hors ecran, et
   accelerate fait l inverse.
   Les traces sont les vrais points de controle. Pour cubic-bezier de
   0.2, 0, 0, 1 dans un repere ou x va de 2 a 98 et y de 58 a 2, les
   controles tombent a 21.2 sur 58 et a 2 sur 2. La version precedente
   ecrivait 36 sur 2 pour le second, donc elle dessinait une courbe qui
   n etait pas celle du terme.
   Aucune des trois ne depasse sa cible, l emphase vient de la forme du
   timing et non d un rebond.
   Gele, on verrait trois courbes, ce qui est deja utile, mais on ne
   verrait pas laquelle produit quelle arrivee. */
.emp{display:flex;gap:8px}
.emp .col{display:flex;flex-direction:column;align-items:center;gap:7px;width:48px}
.emp .cv{display:block;width:48px;height:32px}
.emp .cv .gri{fill:none;stroke:var(--s3);stroke-width:1.5}
.emp .cv .c{fill:none;stroke:var(--lime);stroke-width:2.6}
.emp em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;letter-spacing:.02em}
.emp .tk{position:relative;display:block;width:44px;height:6px;border-radius:3px;background:var(--s2)}
.emp .tk b{position:absolute;left:0;top:-1px;display:block;width:8px;height:8px;border-radius:4px;
  background:var(--lime)}
.emp .e1{animation:aEmp 2.8s cubic-bezier(.2,0,0,1) infinite}
.emp .e2{animation:aEmp 2.8s cubic-bezier(.05,.7,.1,1) infinite}
.emp .e3{animation:aEmp 2.8s cubic-bezier(.3,0,.8,.15) infinite}
/* la piste fait 44 et le point 8, la course vaut donc 36 */
@keyframes aEmp{0%,10%{transform:translateX(0)}62%,74%{transform:translateX(36px)}
  96%,100%{transform:translateX(0)}}


/* L APERCU est le sujet, et il faut donc une vraie destination
   derriere, pas un fond raye. Son opacite est CALCULEE sur la meme
   progression que l ecran du dessus, memes instants, donc on voit ce
   qu on va obtenir pendant qu on decide.
   Le geste est mene une premiere fois LOIN, jusqu a 46, puis annule.
   C est la consigne de supporter l annulation a n importe quelle valeur
   de progression, y compris bien au dela du point ou l utilisateur
   semble engage.
   L echelle et le rayon suivent la meme progression, 0.88 et 12 au
   grand apercu, 0.93 et 8 au petit.
   Le retour de boucle est une vraie navigation vers l avant, l ecran
   revient par la droite, il n y a donc aucun rembobinage.
   Gele, on verrait un ecran retreci sur un autre, sans savoir si le
   geste va aboutir. */
.pbk{position:relative;width:138px;height:94px;border-radius:11px;overflow:hidden;background:var(--stage)}
.pbk .behind{position:absolute;inset:0;padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:8px;background:var(--s1);opacity:0;
  animation:aPbackBh 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.pbk .behind .bh{display:block;height:10px;width:56%;border-radius:5px;background:var(--ink);opacity:.85}
.pbk .behind .br{display:block;height:7px;border-radius:3px;background:var(--s4)}
.pbk .behind .br.s{width:58%}
.pbk .front{position:absolute;inset:0;padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:9px;background:var(--lime);transform-origin:right center;
  animation:aPback 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.pbk .front .fh{display:block;height:11px;width:62%;border-radius:5px;
  background:rgba(var(--on-accent-rgb),.75)}
.pbk .front .fr{display:block;height:7px;border-radius:3px;background:rgba(var(--on-accent-rgb),.45)}
.pbk .front .fr.s{width:54%}
.pbk .cur{left:8px;top:40px;animation:aPbackCur 5.4s cubic-bezier(.3,.9,.3,1) infinite}
/* grand apercu a 46 puis ANNULE, petit apercu a 26 puis valide */
@keyframes aPback{0%,8%{transform:translateX(0) scale(1);border-radius:0}
  30%,34%{transform:translateX(46px) scale(.88);border-radius:12px}
  46%,54%{transform:translateX(0) scale(1);border-radius:0}
  64%,66%{transform:translateX(26px) scale(.93);border-radius:8px}
  80%,88%{transform:translateX(138px) scale(.86);border-radius:14px}
  100%{transform:translateX(0) scale(1);border-radius:0}}
/* meme progression, l apercu monte et redescend avec le geste */
@keyframes aPbackBh{0%,8%{opacity:0}30%,34%{opacity:.6}46%,54%{opacity:0}
  64%,66%{opacity:.35}80%,88%{opacity:1}100%{opacity:0}}
@keyframes aPbackCur{0%{transform:translateX(0);opacity:0}
  8%{transform:translateX(0);opacity:1}
  30%{transform:translateX(46px);opacity:1}
  34%{transform:translateX(46px);opacity:0}
  53%{transform:translateX(0);opacity:0}
  55%{transform:translateX(0);opacity:1}
  64%{transform:translateX(26px);opacity:1}
  67%{transform:translateX(26px);opacity:0}
  99%,100%{transform:translateX(0);opacity:0}}

/* Trois silhouettes de HUIT points chacune, toutes demarrees en haut et
   enroulees dans le meme sens. C est la consigne, et la version
   precedente avait 6, 8 puis 5 points. L interpolation entre deux
   polygones de comptes differents n est pas definie, la forme sautait
   donc au lieu de se transformer, et la demo etait un diaporama.
   Les points sont calcules, centre 50 et rayon 50 aux huit angles de 45
   degres. La rondeur passe par 41 sur les diagonales, l etoile par 28
   sur les axes.
   Rotation sur 3s et morphing sur 4.4s, sans diviseur commun, la paire
   ne repasse donc jamais par la meme combinaison.
   Gele, on verrait un polygone, ce qui ne dit ni la rotation ni la
   liste de formes. */
.mld{display:grid;place-items:center}
.mld .poly{display:block;width:56px;height:56px;background:var(--lime);
  animation:aMldSpin 3s linear infinite,aMldShape 4.4s ease-in-out infinite}
@keyframes aMldSpin{to{transform:rotate(360deg)}}
@keyframes aMldShape{
  0%,100%{clip-path:polygon(50% 0,85.36% 14.64%,100% 50%,85.36% 85.36%,50% 100%,14.64% 85.36%,0 50%,14.64% 14.64%)}
  33%{clip-path:polygon(50% 0,79% 21%,100% 50%,79% 79%,50% 100%,21% 79%,0 50%,21% 21%)}
  66%{clip-path:polygon(50% 22%,85.36% 14.64%,78% 50%,85.36% 85.36%,50% 78%,14.64% 85.36%,22% 50%,14.64% 14.64%)}}

/* ---------------- Fluent et Windows ---------------- */
/* Le relais se fait AU DESSUS de l echange de pages, et c est ce qui
   manquait. Sans les deux pages qui glissent en dessous, un rectangle
   qui vole ne dit pas qu il y a eu navigation, donc ne dit pas non plus
   que la chose a ete enregistree d un cote et relancee de l autre.
   Les limites sont interpolees, gauche haut largeur hauteur. La version
   precedente utilisait un scale de 1.7 sur 1.69, qui etire tout ce que
   l element contient.
   Les deux cadres pointilles sont les ancrages, ils ne bougent jamais et
   disent d ou a ou.
   Gele, on verrait un element quelque part entre deux cadres. */
.cna{position:relative;width:144px;height:96px;border-radius:10px;overflow:hidden;background:var(--s0)}
.cna .anc{position:absolute;display:block;border-radius:8px;border:1px dashed var(--s5);
  box-sizing:border-box}
.cna .a{left:8px;bottom:10px;width:36px;height:26px}
.cna .b{right:8px;top:8px;width:60px;height:46px}
.cna .pg{position:absolute;inset:0}
.cna .pa{background:var(--s2);animation:aCnaA 4.4s cubic-bezier(.2,0,0,1) infinite}
.cna .pb{background:var(--s1);animation:aCnaB 4.4s cubic-bezier(.2,0,0,1) infinite}
.cna .fly{position:absolute;left:8px;top:60px;width:36px;height:26px;border-radius:8px;
  background:var(--lime);z-index:4;animation:aCna 4.4s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aCnaA{0%,14%{transform:translateX(0)}30%,80%{transform:translateX(-100%)}
  96%,100%{transform:translateX(0)}}
@keyframes aCnaB{0%,14%{transform:translateX(100%)}30%,80%{transform:translateX(0)}
  96%,100%{transform:translateX(100%)}}
/* ancrage A a 8 et 60 en 36 sur 26, ancrage B a 76 et 8 en 60 sur 46 */
@keyframes aCna{0%,14%{left:8px;top:60px;width:36px;height:26px}
  52%,80%{left:76px;top:8px;width:60px;height:46px}
  96%,100%{left:8px;top:60px;width:36px;height:26px}}

/* Une seule ECRITURE, deux reponses. Le repere du bas clignote a
   l instant ou la valeur est ecrite, et les deux points partent au MEME
   instant, celui du haut d un bond parce qu aucune courbe n est
   enregistree sur sa propriete, celui du bas en glissant parce qu il en
   a une. La version precedente faisait partir le glissement AVANT le
   bond, ce qui inversait la cause et l effet.
   Le point du haut n avait aucun fond declare, il etait invisible.
   Deux ecritures par cycle, aller puis retour, donc rien n est rembobine.
   Le rail fait 118 pour un point de 14, la course vaut donc 104.
   Gele, on verrait deux points au meme endroit. */
.ima{position:relative;width:150px;height:82px;display:flex;flex-direction:column;
  justify-content:center;gap:12px}
.ima .lane{display:flex;align-items:center;gap:8px}
.ima em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;width:44px;text-align:right}
.ima .rail{position:relative;display:block;flex:1;height:3px;border-radius:2px;background:var(--s2)}
.ima .rail i{position:absolute;left:0;top:-6px;display:block;width:14px;height:14px;border-radius:50%}
.ima .dj{background:var(--s6);animation:aImaJump 3.4s steps(1,end) infinite}
.ima .dg{background:var(--lime);animation:aImaGlide 3.4s cubic-bezier(.2,.9,.25,1) infinite}
.ima .write{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:center}
.ima .write b{display:block;width:26px;height:4px;border-radius:2px;background:var(--a2);opacity:.2;
  animation:aImaWrite 3.4s steps(1,end) infinite}
/* les deux partent au meme instant, 41 pour cent puis 89 */
@keyframes aImaJump{0%,40%{transform:translateX(0)}41%,88%{transform:translateX(104px)}
  89%,100%{transform:translateX(0)}}
@keyframes aImaGlide{0%,40%{transform:translateX(0)}62%,88%{transform:translateX(104px)}
  100%{transform:translateX(0)}}
@keyframes aImaWrite{0%,40%{opacity:.2}41%,44%{opacity:1}45%,88%{opacity:.2}
  89%,92%{opacity:1}93%,100%{opacity:.2}}

/* La lumiere allume le FOND de la tuile sous le pointeur et seulement
   les BORDURES de ses voisines, ce qui est la consigne et ce que la
   version precedente ne faisait pas, ses tuiles ne reagissaient pas du
   tout.
   Le rayon de la bordure est plus large que celui du fond, 34 contre 16,
   les deux portees sont donc distinguables.
   La lumiere est posee sur le pointeur, memes instants, elle n a pas de
   timeline a elle.
   Quatre tuiles de 28 avec 8 de gouttiere et 8 de marge font exactement
   152. Les centres tombent donc a 22, 58, 94 et 130, atteints a 15, 37,
   59 et 81 pour cent.
   Gele, on verrait quatre tuiles dont une allumee, sans le voisinage. */
.rvh{position:relative;width:152px;height:76px;padding:8px;box-sizing:border-box;display:flex;gap:8px;
  overflow:hidden;border-radius:10px;background:var(--s0)}
.rvh .tile{display:block;width:28px;height:60px;border-radius:9px;box-sizing:border-box;
  background:var(--s1);border:1px solid rgba(var(--accent-rgb),.14)}
.rvh .t1{animation:aRvhT1 5s linear infinite}
.rvh .t2{animation:aRvhT2 5s linear infinite}
.rvh .t3{animation:aRvhT3 5s linear infinite}
.rvh .t4{animation:aRvhT4 5s linear infinite}
.rvh .light{position:absolute;left:-45px;top:-22px;display:block;width:110px;height:120px;
  border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,rgba(var(--accent-rgb),.34),transparent 62%);
  animation:aRvhLight 5s linear infinite}
.rvh .cur{left:3px;top:31px;z-index:4;animation:aRvhLight 5s linear infinite}
/* le pointeur va de 10 a 142, la lumiere est posee dessus */
@keyframes aRvhLight{0%{transform:translateX(0);opacity:0}
  8%{transform:translateX(0);opacity:1}
  88%{transform:translateX(132px);opacity:1}
  96%,100%{transform:translateX(132px);opacity:0}}
/* fond dans un rayon de 16, bordure dans un rayon de 34 */
@keyframes aRvhT1{0%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  6%{border-color:var(--lime)}
  8%,25%{border-color:var(--lime);background:var(--s3)}
  36%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aRvhT2{0%,17%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  27%,47%{border-color:var(--lime);background:var(--s3)}
  58%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aRvhT3{0%,38%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  49%,69%{border-color:var(--lime);background:var(--s3)}
  80%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aRvhT4{0%,60%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  71%,91%{border-color:var(--lime);background:var(--s3)}
  98%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}

