/* ============================================================
   Motionary, exemples taillés sur mesure
   Un bloc .ex-<id> par terme. Voir assets/js/examples.js
   ============================================================ */

.ex{position:relative}
.ex i,.ex b,.ex em,.ex span{display:block;font-style:normal}

/* ---------------- fade ----------------
   Le voile et la boîte apparaissent sans bouger d'un pixel.
   Aucun transform dans cette scène, c'est volontaire. */
/* hauteur calee sur le contenu reel de la boite de dialogue, sinon
   les boutons du bas sont rognes par l'overflow hidden */
.ex-fade{width:136px;height:112px;border-radius:10px;overflow:hidden;background:var(--s0)}
.ex-fade .app{position:absolute;inset:0;padding:9px;display:flex;flex-direction:column;gap:7px}
.ex-fade .app .hd{height:12px;width:56%;border-radius:5px;background:var(--s4)}
.ex-fade .app .ln{height:8px;border-radius:4px;background:var(--s2)}
.ex-fade .app .ln.s{width:72%}
.ex-fade .scrim{position:absolute;inset:0;background:rgba(var(--void-rgb),.66);animation:aExFadeS 4.4s ease-in-out infinite}
.ex-fade .dlg{position:absolute;left:12px;right:12px;top:22px;padding:10px 11px;border-radius:10px;
  background:var(--card-2);border:1px solid var(--line-2);box-shadow:0 12px 26px rgba(var(--shadow-rgb),.5);
  display:flex;flex-direction:column;gap:6px;animation:aExFadeD 4.4s ease-in-out infinite}
.ex-fade .dlg .t{height:8px;width:52%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-fade .dlg .l{height:6px;border-radius:3px;background:var(--s5)}
.ex-fade .dlg .l.s{width:64%}
.ex-fade .btns{display:flex;gap:6px;justify-content:flex-end;margin-top:2px}
.ex-fade .btns b{height:14px;border-radius:5px}
.ex-fade .btns .gh{width:26px;background:var(--s4)}
.ex-fade .btns .pr{width:34px;background:var(--accent)}
@keyframes aExFadeS{0%,12%{opacity:0}34%,80%{opacity:1}96%,100%{opacity:0}}
@keyframes aExFadeD{0%,16%{opacity:0}38%,80%{opacity:1}96%,100%{opacity:0}}

/* ---------------- slide-in ----------------
   Le toast parcourt une distance, la page derrière ne bouge pas. */
.ex-slide-in{width:134px;height:86px;border-radius:10px;overflow:hidden;background:var(--s0)}
.ex-slide-in .app{position:absolute;inset:0;padding:9px;display:flex;flex-direction:column;gap:7px}
.ex-slide-in .app .hd{height:14px;border-radius:5px;background:var(--accent);width:46%}
.ex-slide-in .app .row{height:13px;border-radius:5px;background:var(--s2)}
.ex-slide-in .toa{position:absolute;right:7px;top:11px;width:88px;height:26px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);box-shadow:0 8px 18px rgba(var(--shadow-rgb),.45);
  display:flex;align-items:center;gap:8px;padding:0 9px;
  animation:aExSlide 4s cubic-bezier(.16,1,.3,1) infinite}
.ex-slide-in .toa .ic{width:12px;height:12px;border-radius:6px;background:var(--accent);flex:none}
.ex-slide-in .toa .l{flex:1;height:6px;border-radius:3px;background:var(--s5)}
@keyframes aExSlide{0%,10%{transform:translateX(104px);opacity:0}34%,78%{transform:none;opacity:1}94%,100%{transform:translateX(104px);opacity:0}}

/* ---------------- scale-in ----------------
   Le popover grandit depuis le coin du bouton qui l'a ouvert. */
.ex-scale-in{width:126px;height:88px}
.ex-scale-in .trg{position:absolute;left:0;bottom:0;width:56px;height:22px;border-radius:8px;background:var(--s4)}
.ex-scale-in .pop{position:absolute;left:0;bottom:28px;width:96px;padding:10px;border-radius:11px;
  background:var(--card-2);border:1px solid var(--line-2);box-shadow:0 12px 24px rgba(var(--shadow-rgb),.45);
  display:flex;flex-direction:column;gap:7px;transform-origin:bottom left;
  animation:aExScale 4s cubic-bezier(.16,1,.3,1) infinite}
.ex-scale-in .pop .l{height:7px;border-radius:4px;background:var(--s5)}
.ex-scale-in .pop .l.s{width:66%;background:var(--accent)}
@keyframes aExScale{0%,12%{transform:scale(.94);opacity:0}34%,80%{transform:scale(1);opacity:1}96%,100%{transform:scale(.94);opacity:0}}

/* ---------------- pop-in ----------------
   Le badge dépasse sa taille puis y revient, sur une vraie barre. */
.ex-pop-in{display:grid;place-items:center}
.ex-pop-in .tb{display:flex;gap:11px;padding:10px 13px;border-radius:13px;
  background:var(--card-2);border:1px solid var(--line-2)}
.ex-pop-in .tb b{position:relative;width:22px;height:22px;border-radius:7px;background:var(--s4)}
.ex-pop-in .tb .bell{background:var(--s5)}
.ex-pop-in .bdg{position:absolute;right:-5px;top:-5px;width:13px;height:13px;border-radius:7px;
  background:var(--accent);border:2px solid var(--card-2);
  animation:aExPop 3.4s cubic-bezier(.2,1.5,.3,1) infinite}
@keyframes aExPop{0%,18%{transform:scale(0)}38%{transform:scale(1.14)}50%{transform:scale(.96)}60%,86%{transform:scale(1)}96%,100%{transform:scale(0)}}

/* ---------------- reveal ----------------
   La carte est deja complete, un bord mobile la decouvre. */
.ex-reveal{width:112px}
.ex-reveal .cardx{border-radius:11px;overflow:hidden;background:var(--card-2);border:1px solid var(--line-2);
  padding-bottom:10px;animation:aExReveal 4.2s cubic-bezier(.6,0,.2,1) infinite}
.ex-reveal .img{height:50px;background:linear-gradient(150deg,var(--accent),var(--accent-deep))}
.ex-reveal .cap{height:7px;border-radius:4px;background:var(--s5);margin:9px 10px 0}
.ex-reveal .cap.s{width:52%;margin-top:6px}
@keyframes aExReveal{0%,8%{clip-path:inset(0 0 100% 0)}42%,84%{clip-path:inset(0 0 0 0)}98%,100%{clip-path:inset(0 0 100% 0)}}

/* ---------------- enter-exit ----------------
   Deux animations differentes jouees en meme temps, pas une seule
   a l'endroit et a l'envers. La sortie est plus rapide. */
.ex-enter-exit{width:118px;display:flex;flex-direction:column;gap:8px}
.ex-enter-exit .lst{display:flex;flex-direction:column;gap:8px}
.ex-enter-exit i{height:16px;border-radius:6px;background:var(--s3)}
.ex-enter-exit .out{background:var(--danger);opacity:.75;animation:aExExit 4s cubic-bezier(.5,0,.85,.4) infinite}
.ex-enter-exit .in{background:var(--accent);animation:aExEnter 4s cubic-bezier(.16,1,.3,1) infinite}
@keyframes aExEnter{0%,14%{opacity:0;transform:translateY(12px) scale(.94)}44%,88%{opacity:1;transform:none}96%,100%{opacity:0;transform:none}}
/* la ligne revient avant de repartir, mais elle revient pendant qu'elle
   est invisible, la boucle ne montre donc jamais de marche arriere */
@keyframes aExExit{0%,6%{opacity:0;height:0;transform:translateX(22px)}16%,44%{opacity:.75;height:16px;transform:none}66%,90%{opacity:0;height:0;transform:translateX(22px)}100%{opacity:0;height:0;transform:translateX(22px)}}

/* ---------------- keyframes ----------------
   Trois etats declares, et la frise qui montre ou ils tombent. */
.ex-keyframes{width:126px;display:flex;flex-direction:column;gap:13px}
.ex-keyframes .stg3{display:flex;flex-direction:column;gap:7px;align-items:flex-start;height:34px;justify-content:center}
.ex-keyframes .h{height:14px;width:100%;border-radius:5px;background:var(--ink);opacity:.9;
  animation:aExKf 3.6s infinite}
.ex-keyframes .sub{height:7px;width:58%;border-radius:4px;background:var(--s5);
  animation:aExKf 3.6s infinite;animation-delay:.08s}
.ex-keyframes .tl{position:relative;height:12px;display:flex;justify-content:space-between}
.ex-keyframes .tl::before{content:"";position:absolute;left:3px;right:3px;top:5px;height:2px;background:var(--s2)}
.ex-keyframes .tl b{position:relative;width:6px;height:12px;border-radius:3px;background:var(--s5)}
.ex-keyframes .tl em{position:absolute;left:0;top:1px;width:10px;height:10px;border-radius:5px;
  background:var(--accent);animation:aExKfDot 3.6s infinite}
@keyframes aExKf{
  0%{transform:translateY(14px);opacity:0;animation-timing-function:cubic-bezier(.16,1,.3,1)}
  40%{transform:translateY(-3px);opacity:1;animation-timing-function:cubic-bezier(.4,0,.2,1)}
  62%,86%{transform:none;opacity:1}
  96%,100%{transform:none;opacity:0}}
@keyframes aExKfDot{0%{transform:translateX(0);opacity:0}6%{opacity:1}40%{transform:translateX(55px)}62%,86%{transform:translateX(110px);opacity:1}96%,100%{transform:translateX(110px);opacity:0}}

/* ---------------- tween ----------------
   Les images intermediaires rendues visibles par des fantomes. */
/* ---------------- momentum ----------------
   Le doigt lache tot, la liste continue et ralentit. Sans cette
   partie la, un flick se lit comme un arret brutal. */
.ex-momentum{position:relative;width:118px;height:110px}
.ex-momentum .vp5{position:absolute;inset:0;border-radius:11px;overflow:hidden;background:var(--s0);border:1px solid var(--line)}
.ex-momentum .lst5{position:absolute;left:9px;right:9px;top:9px;display:flex;flex-direction:column;gap:8px;
  animation:aExMom 4.4s cubic-bezier(.05,.85,.15,1) infinite}
.ex-momentum .lst5 i{height:22px;border-radius:7px;background:var(--s2);flex:none}
.ex-momentum .lst5 i:nth-child(3){background:var(--accent)}
.ex-momentum .cur{left:76px;top:74px;animation:aExMomC 4.4s cubic-bezier(.05,.85,.15,1) infinite}
@keyframes aExMom{0%,8%{transform:translateY(0)}22%{transform:translateY(-28px)}74%,90%{transform:translateY(-92px)}100%{transform:translateY(0)}}
@keyframes aExMomC{0%{transform:translateY(0);opacity:0}6%,8%{opacity:1}22%{transform:translateY(-28px);opacity:1}30%,100%{transform:translateY(-38px);opacity:0}}

/* ---------------- velocity ----------------
   La fleche porte la vitesse au moment du lacher. Plus elle est
   longue, plus le panneau part loin. */
.ex-velocity{position:relative;width:132px;height:104px}
.ex-velocity .fr3{position:absolute;inset:0;border-radius:11px;overflow:hidden;background:var(--s0);border:1px solid var(--line)}
.ex-velocity .sh4{position:absolute;left:0;right:0;bottom:0;height:70px;border-radius:11px 11px 0 0;
  background:var(--accent);animation:aExVel 4s cubic-bezier(.08,.85,.2,1) infinite}
.ex-velocity .vec2{position:absolute;left:50%;bottom:22px;margin-left:-2px;width:4px;border-radius:2px;
  background:var(--ink);transform-origin:bottom center;height:34px;
  animation:aExVelV 4s cubic-bezier(.08,.85,.2,1) infinite}
.ex-velocity .cur{left:60px;bottom:26px;top:auto;animation:aExVelC 4s cubic-bezier(.08,.85,.2,1) infinite}
@keyframes aExVel{0%,10%{transform:translateY(0)}26%{transform:translateY(-14px)}72%,88%{transform:translateY(-46px)}100%{transform:translateY(0)}}
@keyframes aExVelV{0%,10%{transform:scaleY(.1);opacity:0}18%{opacity:1}26%{transform:scaleY(1);opacity:1}40%{transform:scaleY(.1);opacity:0}100%{transform:scaleY(.1);opacity:0}}
@keyframes aExVelC{0%{transform:translateY(0);opacity:0}8%,10%{opacity:1}26%{transform:translateY(-14px);opacity:1}34%,100%{transform:translateY(-20px);opacity:0}}

/* ---------------- interruptible ----------------
   Second clic avant la fin du premier. Le menu repart de sa position
   du moment, il ne revient jamais a zero. */
.ex-interruptible{position:relative;width:126px;height:104px}
.ex-interruptible .trg3{position:absolute;left:0;top:0;width:56px;height:20px;border-radius:8px;background:var(--accent)}
.ex-interruptible .mn4{position:absolute;left:0;top:26px;width:98px;border-radius:10px;background:var(--card-2);
  border:1px solid var(--line-2);padding:9px 10px;display:flex;flex-direction:column;gap:7px;
  transform-origin:top center;animation:aExItr 4.4s cubic-bezier(.2,1.3,.3,1) infinite}
.ex-interruptible .mn4 i{height:7px;border-radius:4px;background:var(--s5)}
.ex-interruptible .mn4 i:last-child{width:64%}
.ex-interruptible .cur{left:24px;top:6px;animation:aExItrC 4.4s ease infinite}
@keyframes aExItr{0%,8%{transform:scaleY(0);opacity:0}30%{transform:scaleY(.72);opacity:1}52%{transform:scaleY(.28);opacity:1}80%,92%{transform:scaleY(1);opacity:1}100%{transform:scaleY(0);opacity:0}}
@keyframes aExItrC{0%,4%{opacity:0;transform:scale(1)}10%,16%{opacity:1;transform:scale(.72)}22%,40%{opacity:1;transform:scale(1)}46%,52%{opacity:1;transform:scale(.72)}60%,100%{opacity:0;transform:scale(1)}}

/* ---------------- marquee ----------------
   Deux bandes en sens opposes. Le contenu est ecrit deux fois dans
   chacune, donc le moins 50 pour cent tombe pile. */
.ex-marquee{display:flex;flex-direction:column;gap:10px;width:100%;overflow:hidden}
.ex-marquee .band4{width:100%;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.ex-marquee .band4 b{display:inline-block;white-space:nowrap;font:800 14px/26px var(--font);
  letter-spacing:.05em;color:var(--accent);animation:aExMq 9s linear infinite}
.ex-marquee .r b{color:var(--s6);animation:aExMqR 11s linear infinite}
@keyframes aExMq{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@keyframes aExMqR{from{transform:translateX(-50%)}to{transform:translateX(0)}}

/* ---------------- loop ----------------
   Deux iterations puis plus rien. Le fill-mode forwards garde la
   derniere image, sinon la coche disparaitrait a la fin. */
.ex-loop{display:grid;place-items:center;width:150px;height:80px}
.ex-loop .card9{display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:11px;
  background:var(--card-2);border:1px solid var(--line-2);width:132px}
.ex-loop .ck2{width:24px;height:24px;border-radius:13px;background:var(--accent);flex:none;
  animation:aExLoop 4.4s cubic-bezier(.2,1.4,.3,1) infinite}
/* La carte se resout APRES le second passage, a 42 pour cent. Deux
   fois exactement, puis un etat acquis. Une coche qui rebondit
   au dessus d'une carte inerte ne montrerait que le rebond. */
.ex-loop .card9{animation:aExLoopCard 4.4s ease infinite}
.ex-loop .tx4 b{animation:aExLoopTx 4.4s ease infinite}
@keyframes aExLoopCard{0%,42%{border-color:var(--line-2)}52%,92%{border-color:var(--accent)}
  98%,100%{border-color:var(--line-2)}}
@keyframes aExLoopTx{0%,42%{opacity:.55}52%,92%{opacity:1}98%,100%{opacity:.55}}
.ex-loop .tx4{flex:1;display:flex;flex-direction:column;gap:6px}
.ex-loop .tx4 b{height:6px;border-radius:3px;background:var(--s5)}
.ex-loop .tx4 b.s{width:56%}
@keyframes aExLoop{0%,6%{transform:scale(1)}14%{transform:scale(1.28)}24%{transform:scale(1)}32%{transform:scale(1.28)}42%,92%{transform:scale(1)}100%{transform:scale(1)}}

/* ---------------- alternate ----------------
   A gauche le point revient par le chemin qu'il a pris. A droite il
   se teleporte au depart a chaque tour, et ca se voit. */
.ex-alternate{display:flex;flex-direction:column;gap:16px;width:150px}
.ex-alternate .one{display:flex;align-items:center;gap:10px}
.ex-alternate .lb{width:52px;font:600 8.5px/1 var(--mono);color:var(--ink-3);font-style:normal;flex:none}
.ex-alternate .tk3{position:relative;flex:1;height:8px;border-radius:4px;background:var(--s2)}
.ex-alternate .tk3 b{position:absolute;left:0;top:-3px;width:14px;height:14px;border-radius:7px;
  background:var(--accent);display:block}
.ex-alternate .yo2{animation:aExAlt 2.2s ease-in-out infinite alternate}
.ex-alternate .jp2{background:var(--s6);animation:aExAlt 2.2s ease-in-out infinite}
@keyframes aExAlt{from{transform:translateX(0)}to{transform:translateX(72px)}}

/* ---------------- orbit ----------------
   Le conteneur tourne, la pastille contre tourne a la meme vitesse
   et reste droite. Sans la contre rotation elle finirait a l'envers. */
.ex-orbit{position:relative;display:grid;place-items:center;width:104px;height:104px}
.ex-orbit .av3{width:44px;height:44px;border-radius:24px;background:var(--s4)}
.ex-orbit .ring4{position:absolute;inset:18px;border-radius:50%;border:1px dashed var(--s5)}
.ex-orbit .sat{position:absolute;inset:18px;animation:aExOrb 5s linear infinite}
.ex-orbit .sat i{position:absolute;top:-9px;left:50%;margin-left:-9px;width:18px;height:18px;border-radius:6px;
  background:var(--accent);display:block;animation:aExOrbC 5s linear infinite}
@keyframes aExOrb{from{transform:rotate(0)}to{transform:rotate(360deg)}}
@keyframes aExOrbC{from{transform:rotate(0)}to{transform:rotate(-360deg)}}

/* ---------------- pulse ----------------
   Trois battements puis plus rien. Un halo permanent finit par
   devenir invisible, c'est le budget d'attention qui se vide. */
.ex-pulse{display:grid;place-items:center;width:140px;height:80px}
.ex-pulse .nav5{display:flex;gap:16px;padding:11px 14px;border-radius:13px;background:var(--card-2);
  border:1px solid var(--line-2)}
.ex-pulse .nav5 i{position:relative;width:24px;height:24px;border-radius:7px;background:var(--s4);display:block}
.ex-pulse .bell2{background:var(--s5)}
.ex-pulse .bdg3{position:absolute;right:-5px;top:-5px;width:13px;height:13px;border-radius:7px;
  background:var(--accent);border:2px solid var(--card-2);display:block;z-index:2;
  animation:aExPul 5s ease-in-out infinite}
.ex-pulse .halo2{position:absolute;right:-5px;top:-5px;width:13px;height:13px;border-radius:7px;
  border:2px solid var(--accent);display:block;animation:aExPulH 5s cubic-bezier(.2,.7,.3,1) infinite}
@keyframes aExPul{0%,6%{transform:scale(1)}14%{transform:scale(1.18)}24%{transform:scale(1)}34%{transform:scale(1.18)}44%,100%{transform:scale(1)}}
@keyframes aExPulH{0%{transform:scale(1);opacity:0}6%{opacity:.7}22%{transform:scale(2.4);opacity:0}30%{transform:scale(1);opacity:.7}46%,100%{transform:scale(2.4);opacity:0}}

/* ---------------- cubic-bezier ----------------
   Les poignees, la courbe et l'apercu decrivent la meme valeur. C'est
   exactement l'editeur des devtools, et il ne peut pas mentir. */
.ex-cubic-bezier{display:flex;flex-direction:column;align-items:center;gap:11px;width:150px}
.ex-cubic-bezier .ed{width:130px;padding:9px;border-radius:10px;background:var(--s0);border:1px solid var(--line)}
.ex-cubic-bezier svg{width:112px;height:60px;display:block}
.ex-cubic-bezier .grid2{stroke:var(--s3);stroke-width:1.4;fill:none}
.ex-cubic-bezier .h2{stroke:var(--s6);stroke-width:1.1;stroke-dasharray:3 3;fill:none}
.ex-cubic-bezier .c3{stroke:var(--accent);stroke-width:2.6;fill:none}
.ex-cubic-bezier .cp2{fill:var(--ink);stroke:none}
.ex-cubic-bezier .prev2{display:flex;flex-direction:column;align-items:center;gap:8px}
.ex-cubic-bezier .tk2{position:relative;display:block;width:118px;height:8px;border-radius:4px;background:var(--s2)}
.ex-cubic-bezier .tk2 b{position:absolute;left:0;top:-2px;width:12px;height:12px;border-radius:6px;
  background:var(--accent);animation:aExCbz 2.8s cubic-bezier(.25,.1,.25,1) infinite}
.ex-cubic-bezier .num{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
@keyframes aExCbz{0%,6%{transform:translateX(0)}52%,60%{transform:translateX(106px)}96%,100%{transform:translateX(0)}}

/* ---------------- asymmetric-easing ----------------
   Meme distance, meme duree. Celui de gauche a fait les trois quarts
   du chemin quand celui de droite en est a la moitie. */
.ex-asymmetric-easing{display:flex;gap:16px;align-items:flex-end}
.ex-asymmetric-easing .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.ex-asymmetric-easing .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-asymmetric-easing .sh2{width:56px;height:70px;border-radius:10px;background:var(--s2);border:1px solid var(--line);
  overflow:hidden;position:relative}
.ex-asymmetric-easing .sh2::after{content:"";position:absolute;left:0;right:0;bottom:0;height:100%;
  background:var(--accent);transform:translateY(100%)}
.ex-asymmetric-easing .a::after{animation:aExAsyA 3.4s cubic-bezier(.05,.9,.1,1) infinite}
.ex-asymmetric-easing .b::after{animation:aExAsyA 3.4s cubic-bezier(.65,0,.35,1) infinite;background:var(--s5)}
@keyframes aExAsyA{0%,10%{transform:translateY(100%)}56%,86%{transform:translateY(0)}96%,100%{transform:translateY(100%)}}

/* ---------------- spring ----------------
   Le panneau part vers la cible du haut, le pointeur change d'avis en
   plein vol et il repart vers celle du bas sans repasser par zero.
   C'est ce que seul un ressort sait faire. */
.ex-spring{position:relative;width:150px;height:96px}
.ex-spring .rail5{position:absolute;right:0;top:0;bottom:0;display:flex;flex-direction:column;justify-content:space-between}
.ex-spring .rail5 i{width:52px;height:38px;border-radius:9px;border:1.5px dashed var(--s5);box-sizing:border-box}
.ex-spring .pn2{position:absolute;left:0;top:0;width:52px;height:38px;border-radius:9px;background:var(--accent);
  animation:aExSpr 4.4s cubic-bezier(.2,1.5,.3,1) infinite}
.ex-spring .cur{left:40px;top:16px;animation:aExSprC 4.4s ease infinite}
@keyframes aExSpr{0%,10%{transform:translate(0,0)}38%{transform:translate(62px,4px)}72%,88%{transform:translate(98px,58px)}98%,100%{transform:translate(0,0)}}
@keyframes aExSprC{0%,24%{opacity:0}34%,44%{opacity:1;transform:translate(46px,0) scale(.72)}54%,100%{opacity:0}}

/* ---------------- stiffness ----------------
   Meme course, meme cycle. Le plus raide est deja arrive quand le
   plus souple est encore en route, et il depasse davantage. */
.ex-stiffness,.ex-damping,.ex-mass{display:flex;gap:18px;align-items:center}
.ex-stiffness .one,.ex-damping .one,.ex-mass .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.ex-stiffness .lb,.ex-damping .lb,.ex-mass .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-stiffness .sw3{position:relative;width:56px;height:28px;border-radius:15px;background:var(--s2)}
.ex-stiffness .sw3 b{position:absolute;top:3px;left:3px;width:22px;height:22px;border-radius:12px;background:var(--accent)}
.ex-stiffness .soft3{animation:aExStf 3.4s cubic-bezier(.25,1.25,.5,1) infinite}
.ex-stiffness .hard3{animation:aExStf 3.4s cubic-bezier(.1,2.1,.3,1) infinite}
@keyframes aExStf{0%,12%{transform:translateX(0)}58%,86%{transform:translateX(28px)}100%{transform:translateX(0)}}

/* ---------------- damping ----------------
   Le panneau de gauche oscille autour de sa position finale, celui de
   droite y arrive une fois et s'arrete. */
.ex-damping .bx{position:relative;width:58px;height:72px;border-radius:10px;background:var(--s0);
  border:1px solid var(--line);overflow:hidden}
.ex-damping .bx b{position:absolute;left:0;right:0;bottom:0;height:44px;border-radius:9px 9px 0 0;background:var(--accent)}
.ex-damping .lo{animation:aExDmpL 3.8s cubic-bezier(.4,0,.4,1) infinite}
.ex-damping .hi{animation:aExDmpH 3.8s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExDmpL{0%,10%{transform:translateY(46px)}32%{transform:translateY(-8px)}46%{transform:translateY(5px)}58%{transform:translateY(-3px)}68%{transform:translateY(1px)}78%,90%{transform:translateY(0)}100%{transform:translateY(46px)}}
@keyframes aExDmpH{0%,10%{transform:translateY(46px)}36%{transform:translateY(1px)}44%,90%{transform:translateY(0)}100%{transform:translateY(46px)}}

/* ---------------- mass ----------------
   Meme ressort sur les deux. Le panneau lourd demarre plus mollement
   et ne depasse presque pas. */
.ex-mass .ch2{width:44px;height:22px;border-radius:11px;background:var(--accent);
  animation:aExMasL 3.6s cubic-bezier(.15,1.7,.3,1) infinite}
.ex-mass .sh3{width:66px;height:64px;border-radius:11px;background:var(--accent);
  animation:aExMasH 3.6s cubic-bezier(.45,.95,.5,1) infinite}
@keyframes aExMasL{0%,10%{transform:translateY(20px);opacity:0}42%,86%{transform:none;opacity:1}96%,100%{transform:translateY(20px);opacity:0}}
@keyframes aExMasH{0%,10%{transform:translateY(20px);opacity:0}70%,86%{transform:none;opacity:1}96%,100%{transform:translateY(20px);opacity:0}}

/* ---------------- bounce ----------------
   Le badge depasse sa position finale puis y revient. Le depassement
   est de 8 pour cent, la consigne plafonne a 10. */
.ex-bounce{display:grid;place-items:center;width:140px;height:76px}
.ex-bounce .bar6{display:flex;gap:14px;padding:11px 14px;border-radius:13px;background:var(--card-2);
  border:1px solid var(--line-2)}
.ex-bounce .bar6 i{position:relative;width:24px;height:24px;border-radius:7px;background:var(--s4);display:block}
.ex-bounce .cart{background:var(--s5)}
.ex-bounce .bdg2{position:absolute;right:-6px;top:-6px;width:15px;height:15px;border-radius:8px;
  background:var(--accent);border:2px solid var(--card-2);display:block;
  animation:aExBnc 3.2s cubic-bezier(.2,1.6,.3,1) infinite}
@keyframes aExBnc{0%,18%{transform:scale(0)}40%{transform:scale(1.08)}52%{transform:scale(.97)}62%,86%{transform:scale(1)}96%,100%{transform:scale(0)}}

/* ---------------- perceptual-duration ----------------
   En haut, la seconde etape attend la fin mathematique et un trou
   apparait. En bas, elle demarre a la fin percue et ca s'enchaine. */
.ex-perceptual-duration{display:flex;flex-direction:column;gap:14px;width:158px}
.ex-perceptual-duration .one{display:flex;align-items:center;gap:10px}
.ex-perceptual-duration .lb{width:52px;font:600 8.5px/1 var(--mono);color:var(--ink-3);font-style:normal;flex:none}
.ex-perceptual-duration .tl2{position:relative;flex:1;height:10px;border-radius:5px;background:var(--s2);overflow:hidden}
.ex-perceptual-duration .tl2 b{position:absolute;top:0;bottom:0;border-radius:5px;display:block}
.ex-perceptual-duration .s1{left:0;width:52%;background:var(--accent);animation:aExPd1 4s linear infinite}
.ex-perceptual-duration .late{left:76%;width:24%;background:var(--s5);animation:aExPd2 4s linear infinite}
.ex-perceptual-duration .good{left:52%;width:24%;background:var(--s5);animation:aExPd3 4s linear infinite}
@keyframes aExPd1{0%,6%{opacity:0;transform:scaleX(0);transform-origin:left}20%,88%{opacity:1;transform:scaleX(1)}96%,100%{opacity:0;transform:scaleX(0)}}
@keyframes aExPd2{0%,52%{opacity:0;transform:scaleX(0);transform-origin:left}64%,88%{opacity:1;transform:scaleX(1)}96%,100%{opacity:0;transform:scaleX(0)}}
@keyframes aExPd3{0%,30%{opacity:0;transform:scaleX(0);transform-origin:left}42%,88%{opacity:1;transform:scaleX(1)}96%,100%{opacity:0;transform:scaleX(0)}}

/* ---------------- rubber-band ----------------
   La zone de rafraichissement apparait parce que le contenu a ete
   tire au dela de sa limite, puis tout revient. */
.ex-rubber-band{width:132px;height:112px;border-radius:11px;overflow:hidden;background:var(--s0);
  border:1px solid var(--line);display:flex;flex-direction:column}
.ex-rubber-band .hd5{height:22px;background:var(--s4);display:flex;align-items:center;padding:0 9px;flex:none;z-index:3}
.ex-rubber-band .hd5 i{height:7px;width:40px;border-radius:4px;background:var(--s6)}
.ex-rubber-band .pull{height:0;display:grid;place-items:center;overflow:hidden;flex:none;
  animation:aExRbP 4.4s cubic-bezier(.3,.8,.3,1) infinite}
.ex-rubber-band .sp{width:14px;height:14px;border-radius:50%;border:2px solid var(--s4);
  border-top-color:var(--accent);animation:aExRbS 1s linear infinite}
.ex-rubber-band .lst4{display:flex;flex-direction:column;gap:8px;padding:9px}
.ex-rubber-band .lst4 i{height:20px;border-radius:7px;background:var(--s2)}
.ex-rubber-band .lst4 i:first-child{background:var(--accent)}
@keyframes aExRbP{0%,10%{height:0}30%{height:18px}46%,56%{height:26px}72%,100%{height:0}}
@keyframes aExRbS{to{transform:rotate(360deg)}}

/* ---------------- shake ----------------
   La secousse et le message arrivent ensemble. Le mouvement seul ne
   dit rien a qui ne le voit pas. */
.ex-shake{width:138px;display:flex;flex-direction:column;gap:8px}
.ex-shake .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-shake .fld6{height:32px;border-radius:9px;background:var(--s0);border:1.5px solid var(--s4);
  display:flex;align-items:center;padding:0 11px;animation:aExShk 3.6s ease-in-out infinite}
.ex-shake .dots3{height:6px;width:56px;border-radius:3px;background:var(--s5)}
.ex-shake .err{height:6px;width:88px;border-radius:3px;background:var(--danger);opacity:0;
  animation:aExShkE 3.6s ease infinite}
.ex-shake .btn6{height:24px;width:62px;border-radius:8px;background:var(--accent);margin-top:2px}
@keyframes aExShk{0%,44%{transform:translateX(0);border-color:var(--s4)}48%{transform:translateX(-7px);border-color:var(--danger)}52%{transform:translateX(7px)}56%{transform:translateX(-5px)}60%{transform:translateX(5px)}64%{transform:translateX(-2px)}68%,92%{transform:translateX(0);border-color:var(--danger)}98%,100%{transform:translateX(0);border-color:var(--s4)}}
@keyframes aExShkE{0%,46%{opacity:0}56%,92%{opacity:1}98%,100%{opacity:0}}

/* ---------------- ripple ----------------
   L'onde nait sous le doigt, pas au centre de la ligne. Le point de
   contact est la seule chose que l'onde a a dire. */
.ex-ripple{position:relative;width:146px;display:flex;flex-direction:column;gap:8px}
.ex-ripple .row4{position:relative;display:flex;align-items:center;gap:11px;height:38px;padding:0 11px;
  border-radius:9px;background:var(--s2);overflow:hidden}
.ex-ripple .dmm{opacity:.5}
.ex-ripple .av2{width:22px;height:22px;border-radius:12px;background:var(--accent);flex:none}
.ex-ripple .tx3{flex:1;display:flex;flex-direction:column;gap:6px}
.ex-ripple .tx3 b{height:6px;border-radius:3px;background:var(--s5)}
.ex-ripple .tx3 b.s{width:52%}
.ex-ripple .wave{position:absolute;left:104px;top:26px;width:8px;height:8px;margin:-4px 0 0 -4px;
  border-radius:50%;background:rgba(var(--accent-rgb),.5);animation:aExRip 3s cubic-bezier(.2,.7,.3,1) infinite}
.ex-ripple .cur{left:100px;top:22px;animation:aExRipC 3s ease-out infinite}
@keyframes aExRip{0%,30%{transform:scale(0);opacity:.7}78%,100%{transform:scale(26);opacity:0}}
@keyframes aExRipC{0%,26%{transform:scale(1)}32%,42%{transform:scale(.72)}56%,100%{transform:scale(1)}}

/* ---------------- easing ----------------
   Meme distance, meme duree, deux courbes. La carte de gauche est
   arrivee avant la moitie du temps, celle de droite non. */
.ex-easing{display:flex;gap:14px;align-items:flex-end}
.ex-easing .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.ex-easing .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-easing .c2{width:58px;height:64px;border-radius:10px;background:var(--s2);border:1px solid var(--line)}
.ex-easing .c2.a{background:var(--accent);border-color:transparent;animation:aExEsA 3.2s cubic-bezier(.16,1,.3,1) infinite}
.ex-easing .c2.b{animation:aExEsB 3.2s linear infinite}
@keyframes aExEsA{0%,8%{transform:translateY(22px);opacity:0}44%,86%{transform:none;opacity:1}96%,100%{transform:translateY(22px);opacity:0}}
@keyframes aExEsB{0%,8%{transform:translateY(22px);opacity:0}44%,86%{transform:none;opacity:1}96%,100%{transform:translateY(22px);opacity:0}}

/* ---------------- ease-out ----------------
   Le menu a fait les trois quarts du chemin avant le tiers du temps.
   C'est ce depart la qui dit que le clic a ete recu. */
.ex-ease-out{width:132px;display:flex;flex-direction:column;gap:8px}
.ex-ease-out .trg2{height:22px;width:66px;border-radius:8px;background:var(--accent)}
.ex-ease-out .mn3{border-radius:10px;background:var(--card-2);border:1px solid var(--line-2);
  display:flex;flex-direction:column;gap:7px;padding:10px 11px;transform-origin:top center;
  animation:aExEo 3.2s cubic-bezier(.16,1,.3,1) infinite}
.ex-ease-out .mn3 i{height:7px;border-radius:4px;background:var(--s5)}
.ex-ease-out .mn3 i:nth-child(2){width:74%}.ex-ease-out .mn3 i:nth-child(3){width:56%}
@keyframes aExEo{0%,10%{transform:scaleY(.2);opacity:0}34%,84%{transform:none;opacity:1}96%,100%{transform:scaleY(.2);opacity:0}}

/* ---------------- ease-in ----------------
   Le toast hesite puis part vite. Sur une sortie c'est juste, sur une
   entree ce serait de la latence. */
.ex-ease-in{position:relative;width:140px;height:92px;overflow:hidden}
.ex-ease-in .pg3{display:flex;flex-direction:column;gap:8px;padding-top:6px}
.ex-ease-in .pg3 i{height:16px;border-radius:6px;background:var(--s2)}
.ex-ease-in .ts2{position:absolute;left:8px;right:8px;bottom:6px;height:26px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);display:flex;align-items:center;padding:0 10px;
  animation:aExEi 3.6s cubic-bezier(.7,0,.84,0) infinite}
.ex-ease-in .ts2 b{height:6px;width:52px;border-radius:3px;background:var(--accent)}
/* La derniere ligne de la page est cachee par le toast. Elle
   redevient lisible exactement quand il part, donc le depart porte
   une consequence au lieu d'etre un simple aller retour. */
.ex-ease-in .pg3 i:last-child{animation:aExEiRow 3.6s cubic-bezier(.7,0,.84,0) infinite}
@keyframes aExEi{0%{transform:translateY(40px);opacity:0}10%,52%{transform:translateY(0);opacity:1}86%,100%{transform:translateY(40px);opacity:0}}
@keyframes aExEiRow{0%{opacity:1}10%,52%{opacity:.28}86%,100%{opacity:1}}

/* ---------------- ease-in-out ----------------
   L'objet est deja a l'ecran, il doit demarrer et s'arreter la ou on
   le regarde. Les deux bouts sont donc adoucis. */
.ex-ease-in-out{position:relative;width:146px;height:88px}
.ex-ease-in-out .cols2{position:absolute;inset:0;display:flex;gap:10px}
.ex-ease-in-out .cl{flex:1;border-radius:10px;background:var(--s0);border:1px solid var(--line)}
.ex-ease-in-out .mv2{position:absolute;left:8px;top:10px;width:52px;height:30px;border-radius:8px;
  background:var(--accent);animation:aExEio 3.6s cubic-bezier(.65,0,.35,1) infinite}
@keyframes aExEio{0%,14%{transform:translate(0,0)}50%,64%{transform:translate(78px,38px)}96%,100%{transform:translate(0,0)}}

/* ---------------- linear ----------------
   Ni l'un ni l'autre n'a de debut ni de fin visible, c'est la seule
   condition pour qu'une vitesse constante passe inapercue. */
.ex-linear{display:flex;flex-direction:column;gap:16px;width:150px}
.ex-linear .one{display:flex;flex-direction:column;align-items:center;gap:8px}
.ex-linear .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-linear .spin3{width:28px;height:28px;border-radius:50%;border:3px solid var(--s2);
  border-top-color:var(--accent);animation:aExLinS .9s linear infinite}
.ex-linear .mq3{width:150px;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 14%,#000 86%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 14%,#000 86%,transparent)}
.ex-linear .mq3 b{display:inline-block;white-space:nowrap;font:800 13px/1 var(--font);letter-spacing:.05em;
  color:var(--accent);animation:aExLinM 7s linear infinite}
@keyframes aExLinS{to{transform:rotate(360deg)}}
@keyframes aExLinM{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ---------------- page-transition ----------------
   L'en-tete et la barre d'onglets ne bougent pas d'un pixel. Seule la
   fenetre centrale echange son contenu. */
.ex-page-transition{width:140px;height:112px;display:flex;flex-direction:column;border-radius:11px;
  overflow:hidden;background:var(--s0);border:1px solid var(--line)}
.ex-page-transition .hd4{height:22px;background:var(--s4);display:flex;align-items:center;padding:0 9px;flex:none}
.ex-page-transition .hd4 i{height:7px;width:44px;border-radius:4px;background:var(--s6)}
.ex-page-transition .win4{position:relative;flex:1;overflow:hidden}
.ex-page-transition .v{position:absolute;inset:0;padding:10px;display:flex;flex-direction:column;gap:7px}
.ex-page-transition .v i{height:9px;border-radius:4px;background:rgba(var(--ink-rgb),.28)}
.ex-page-transition .v i.s{width:56%}
.ex-page-transition .v.a{background:var(--s2);animation:aExPtA 4s cubic-bezier(.4,0,.2,1) infinite}
.ex-page-transition .v.b{background:var(--accent);animation:aExPtB 4s cubic-bezier(.4,0,.2,1) infinite}
.ex-page-transition .v.b i{background:rgba(var(--on-accent-rgb),.35)}
.ex-page-transition .tabs2{height:24px;background:var(--s4);display:flex;align-items:center;
  justify-content:space-around;flex:none}
.ex-page-transition .tabs2 b{width:16px;height:7px;border-radius:4px;background:var(--s6)}
.ex-page-transition .tabs2 .on{background:var(--accent)}
@keyframes aExPtA{0%,16%{transform:none;opacity:1}46%,72%{transform:translateX(-26%);opacity:0}94%,100%{transform:none;opacity:1}}
@keyframes aExPtB{0%,16%{transform:translateX(100%);opacity:0}46%,72%{transform:none;opacity:1}94%,100%{transform:translateX(100%);opacity:0}}

/* ---------------- view-transition ----------------
   Le fond des deux vues se croise en fondu, et la photo nommee voyage
   par dessus. Ces deux choses sont faites par le navigateur. */
.ex-view-transition{position:relative;width:140px;height:104px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-view-transition .pv{position:absolute;inset:0;padding:10px}
.ex-view-transition .pv.a{animation:aExVtA 4.2s ease-in-out infinite}
.ex-view-transition .pv.b{padding-top:56px;display:flex;flex-direction:column;gap:8px;animation:aExVtB 4.2s ease-in-out infinite}
.ex-view-transition .g3{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.ex-view-transition .g3 i{height:38px;border-radius:8px;background:var(--s2)}
.ex-view-transition .ln{height:8px;border-radius:4px;background:var(--s5)}
.ex-view-transition .ln.s{width:58%}
.ex-view-transition .ph2{position:absolute;left:10px;top:10px;width:57px;height:38px;border-radius:8px;
  background:linear-gradient(150deg,var(--accent),var(--accent-2));z-index:3;transform-origin:top left;
  animation:aExVtP 4.2s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExVtA{0%,20%{opacity:1}50%,76%{opacity:0}100%{opacity:1}}
@keyframes aExVtB{0%,20%{opacity:0}50%,76%{opacity:1}100%{opacity:0}}
@keyframes aExVtP{0%,16%{transform:translate(0,0) scale(1)}52%,78%{transform:translate(0,0) scale(2.105,.947);border-radius:4px}96%,100%{transform:translate(0,0) scale(1)}}

/* ---------------- hover ----------------
   L'etat dure tant que le pointeur est la, et se retire plus lentement
   qu'il n'est arrive. */
.ex-hover{position:relative;width:146px;height:92px}
.ex-hover .rows2{display:flex;flex-direction:column;gap:8px}
.ex-hover .rows2 i{height:24px;border-radius:8px;background:var(--s2);border:1px solid var(--line)}
.ex-hover .hot{animation:aExHov 4s infinite}
.ex-hover .cur{left:16px;top:74px;animation:aExHovC 4s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExHov{
  0%,26%{background:var(--s2);transform:none;box-shadow:none;animation-timing-function:cubic-bezier(.16,1,.3,1)}
  40%,68%{background:var(--accent);transform:translateY(-2px);box-shadow:0 6px 14px rgba(var(--shadow-rgb),.3);animation-timing-function:cubic-bezier(.4,0,.4,1)}
  94%,100%{background:var(--s2);transform:none;box-shadow:none}}
@keyframes aExHovC{0%,12%{transform:translate(0,0)}38%,68%{transform:translate(58px,-40px)}94%,100%{transform:translate(0,0)}}

/* ---------------- press ----------------
   La touche rapetisse sur place. Elle ne se deplace jamais, le doigt
   est deja dessus. */
.ex-press{position:relative;display:grid;place-items:center;width:132px;height:92px}
.ex-press .pad{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.ex-press .pad b{width:30px;height:30px;border-radius:9px;background:var(--s3)}
.ex-press .k{background:var(--accent);animation:aExPrs 2.8s cubic-bezier(.2,.9,.25,1) infinite}
.ex-press .cur{left:52px;top:70px;animation:aExPrsC 2.8s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExPrs{0%,36%{transform:scale(1);filter:brightness(1)}48%,58%{transform:scale(.9);filter:brightness(.82)}72%,100%{transform:scale(1);filter:brightness(1)}}
@keyframes aExPrsC{0%,16%{transform:translate(0,0);opacity:.6}42%{transform:translate(0,-24px);opacity:1}48%,58%{transform:translate(0,-24px) scale(.72)}74%,100%{transform:translate(0,0);opacity:.6}}

/* ---------------- hold-confirm ----------------
   Le remplissage est le contrat. L'action ne part qu'a la fin, et la
   fin est marquee par autre chose que la barre. */
.ex-hold-confirm{position:relative;display:grid;place-items:center;width:146px;height:80px}
.ex-hold-confirm .btn5{position:relative;width:112px;height:34px;border-radius:10px;background:var(--s3);overflow:hidden}
.ex-hold-confirm .fillb{position:absolute;inset:0;background:var(--accent);transform-origin:left;
  transform:scaleX(0);animation:aExHc 4s cubic-bezier(.4,0,.6,1) infinite}
.ex-hold-confirm .lb{position:absolute;left:50%;top:50%;margin:-3px 0 0 -22px;width:44px;height:7px;
  border-radius:4px;background:var(--ink);opacity:.75;z-index:2}
.ex-hold-confirm .cur{left:70px;top:44px;animation:aExHcC 4s ease infinite}
@keyframes aExHc{0%,10%{transform:scaleX(0)}62%,86%{transform:scaleX(1)}94%,100%{transform:scaleX(0)}}
@keyframes aExHcC{0%,6%{transform:scale(1);opacity:.5}14%,62%{transform:scale(.72);opacity:1}72%,100%{transform:scale(1);opacity:.5}}

/* ---------------- drag ----------------
   Le pointeur lache a 46 pour cent, la carte continue seule jusqu'a
   72. C'est cette partie la qui est animee. */
.ex-drag{position:relative;width:146px;height:92px}
.ex-drag .vp4{position:absolute;inset:0;border-radius:11px;overflow:hidden;background:var(--s0);border:1px solid var(--line)}
.ex-drag .map{position:absolute;left:-40px;top:-40px;width:230px;height:180px;
  background-image:linear-gradient(var(--s2) 1px,transparent 1px),linear-gradient(90deg,var(--s2) 1px,transparent 1px);
  background-size:22px 22px;animation:aExDrg 4.4s cubic-bezier(.1,.85,.25,1) infinite}
.ex-drag .pin2{position:absolute;left:50%;top:50%;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:7px;
  background:var(--accent);z-index:3}
.ex-drag .cur{left:96px;top:62px;animation:aExDrgC 4.4s cubic-bezier(.1,.85,.25,1) infinite}
@keyframes aExDrg{0%,10%{transform:translate(0,0)}46%{transform:translate(-34px,-18px)}72%,88%{transform:translate(-46px,-24px)}98%,100%{transform:translate(0,0)}}
@keyframes aExDrgC{0%{transform:translate(0,0);opacity:0}8%,10%{opacity:1}46%{transform:translate(-34px,-18px);opacity:1}54%{opacity:0}98%,100%{transform:translate(0,0);opacity:0}}

/* ---------------- drag-reorder ----------------
   L'element tenu suit le pointeur sans animation, l'emplacement vide
   se deplace en s'animant. Deux comportements simultanes. */
.ex-drag-reorder{position:relative;width:140px;height:92px}
.ex-drag-reorder .lst3{display:flex;flex-direction:column;gap:8px}
.ex-drag-reorder .t{height:24px;border-radius:8px;background:var(--s2);border:1px solid var(--line)}
.ex-drag-reorder .slotr{height:24px;border-radius:8px;border:1.5px dashed var(--s5);box-sizing:border-box;
  animation:aExDrS 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-drag-reorder .held{position:absolute;left:10px;right:10px;top:32px;height:24px;border-radius:8px;
  background:var(--accent);box-shadow:0 8px 16px rgba(var(--shadow-rgb),.45);z-index:3;
  animation:aExDrH 4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExDrH{0%,12%{transform:translateY(0) scale(1)}22%{transform:translateY(0) scale(1.03)}52%,74%{transform:translateY(32px) scale(1.03)}90%,100%{transform:translateY(0) scale(1)}}
@keyframes aExDrS{0%,22%{transform:translateY(0)}52%,74%{transform:translateY(32px)}90%,100%{transform:translateY(0)}}

/* ---------------- swipe-dismiss ----------------
   Premier geste sous le seuil, la ligne revient. Second geste au dela,
   le systeme termine la sortie tout seul et l'action apparait. */
.ex-swipe-dismiss{position:relative;width:150px;height:52px;border-radius:11px;overflow:hidden}
.ex-swipe-dismiss .under{position:absolute;inset:0;background:var(--danger);display:flex;align-items:center;
  justify-content:flex-end;padding-right:14px}
.ex-swipe-dismiss .act{width:18px;height:18px;border-radius:5px;background:rgba(var(--ink-rgb),.85)}
.ex-swipe-dismiss .row3{position:absolute;inset:0;display:flex;align-items:center;gap:11px;padding:0 12px;
  background:var(--card-2);animation:aExSwd 5s cubic-bezier(.3,.8,.3,1) infinite}
.ex-swipe-dismiss .av{width:24px;height:24px;border-radius:13px;background:var(--accent);flex:none}
.ex-swipe-dismiss .tx2{flex:1;display:flex;flex-direction:column;gap:6px}
.ex-swipe-dismiss .tx2 b{height:6px;border-radius:3px;background:var(--s5)}
.ex-swipe-dismiss .tx2 b.s{width:56%}
@keyframes aExSwd{0%{transform:translateX(0);opacity:0}6%,10%{transform:translateX(0);opacity:1}24%{transform:translateX(-22px)}40%,50%{transform:translateX(0)}70%{transform:translateX(-58px)}86%{transform:translateX(-150px);opacity:1}92%,100%{transform:translateX(-150px);opacity:0}}

/* ---------------- morph ----------------
   La silhouette change, la pilule n'est ni deplacee ni agrandie au
   sens d'un scale, elle est redessinee. Le contenu apparait en fondu
   quand la place existe, il ne s'etire jamais avec le conteneur. */
.ex-morph{position:relative;width:158px;height:92px;display:flex;justify-content:center;padding-top:8px}
.ex-morph .bar2{position:absolute;left:0;right:0;top:0;display:flex;justify-content:space-between}
.ex-morph .bar2 i{height:7px;border-radius:4px;background:var(--s3)}
.ex-morph .bar2 .l{width:26px}.ex-morph .bar2 .r{width:34px}
.ex-morph .pill3{display:flex;align-items:center;gap:9px;overflow:hidden;background:var(--void);
  animation:aExMor 4.4s cubic-bezier(.3,1.05,.35,1) infinite}
.ex-morph .art{width:26px;height:26px;border-radius:7px;flex:none;
  background:linear-gradient(150deg,var(--accent),var(--accent-2));
  animation:aExMorI 4.4s ease infinite}
.ex-morph .tx{display:flex;flex-direction:column;gap:6px;flex:1;animation:aExMorI 4.4s ease infinite;animation-delay:.06s}
.ex-morph .tx b{height:6px;border-radius:3px;background:var(--s5)}
.ex-morph .tx b.s{width:56%}
.ex-morph .eq{width:14px;height:14px;border-radius:4px;background:var(--accent);flex:none;
  animation:aExMorI 4.4s ease infinite;animation-delay:.12s}
@keyframes aExMor{0%,12%{width:54px;height:18px;border-radius:9px;padding:0}48%,84%{width:150px;height:52px;border-radius:18px;padding:0 12px}96%,100%{width:54px;height:18px;border-radius:9px;padding:0}}
@keyframes aExMorI{0%,26%{opacity:0}44%,84%{opacity:1}94%,100%{opacity:0}}

/* ---------------- shared-element ----------------
   Une seule vignette existe dans les deux vues. Elle n'est pas
   dupliquee ni fondue, elle voyage. */
.ex-shared-element{position:relative;width:158px;height:104px}
.ex-shared-element .gr{position:absolute;left:0;top:0;display:flex;gap:7px}
.ex-shared-element .gr i{width:46px;height:38px;border-radius:8px;background:var(--s2)}
.ex-shared-element .pg2{position:absolute;left:0;right:0;top:58px;display:flex;flex-direction:column;gap:7px;
  animation:aExSeP 4.4s ease infinite}
.ex-shared-element .ln{height:7px;border-radius:4px;background:var(--s5)}
.ex-shared-element .ln.s{width:62%}
.ex-shared-element .ctb{height:18px;width:58px;border-radius:7px;background:var(--accent);margin-top:2px}
.ex-shared-element .hero2{position:absolute;left:0;top:0;width:46px;height:38px;border-radius:8px;
  background:linear-gradient(150deg,var(--accent),var(--accent-2));transform-origin:top left;
  animation:aExSe 4.4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExSe{0%,12%{transform:translate(0,0) scale(1);border-radius:8px}50%,84%{transform:translate(0,0) scale(3.435,1.263);border-radius:2.3px}96%,100%{transform:translate(0,0) scale(1);border-radius:8px}}
@keyframes aExSeP{0%,30%{opacity:0;transform:translateY(6px)}52%,84%{opacity:1;transform:none}94%,100%{opacity:0;transform:translateY(6px)}}

/* ---------------- layout-animation ----------------
   La ligne retiree disparait, et les trois autres remontent. C'est le
   deplacement des voisins qui est le sujet, pas la suppression. */
.ex-layout-animation{width:150px}
.ex-layout-animation .lst2{display:flex;flex-direction:column;gap:8px}
.ex-layout-animation .t{height:20px;border-radius:7px;background:var(--s2);border:1px solid var(--line)}
.ex-layout-animation .t:nth-child(3),.ex-layout-animation .t:nth-child(4){
  animation:aExLaN 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-layout-animation .out{background:var(--danger);opacity:.7;animation:aExLaO 4s cubic-bezier(.3,.9,.3,1) infinite}
/* la ligne est retiree puis restauree, les voisins font l'aller et le
   retour. La boucle est donc fermee et montre les deux sens du meme
   mecanisme, ce qui vaut mieux qu'un retour escamote. */
@keyframes aExLaO{0%,10%{opacity:.7;height:20px;transform:none}40%,66%{opacity:0;height:0;transform:translateX(24px)}92%,100%{opacity:.7;height:20px;transform:none}}
@keyframes aExLaN{0%,10%{transform:translateY(0);background:var(--s2)}40%,66%{transform:translateY(-28px);background:var(--accent)}92%,100%{transform:translateY(0);background:var(--s2)}}

/* ---------------- accordion ----------------
   La reponse s'ouvre a la hauteur exacte de son contenu, les questions
   suivantes sont poussees vers le bas. */
.ex-accordion{width:150px;display:flex;flex-direction:column;gap:7px}
.ex-accordion .q{display:flex;align-items:center;justify-content:space-between;gap:9px;height:20px;
  padding:0 9px;border-radius:7px;background:var(--s2)}
.ex-accordion .ttl{height:7px;width:62px;border-radius:4px;background:var(--s5)}
.ex-accordion .ttl.s{width:44px}
.ex-accordion .cv{width:5px;height:5px;border-right:1.5px solid var(--ink-2);border-bottom:1.5px solid var(--ink-2);
  transform:rotate(-45deg)}
.ex-accordion .q:first-child .cv{animation:aExAccCv 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-accordion .ans{display:flex;flex-direction:column;gap:6px;overflow:hidden;height:0;
  animation:aExAcc 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-accordion .ans i{height:6px;border-radius:3px;background:var(--accent);opacity:.85}
.ex-accordion .ans i.s{width:52%}
@keyframes aExAcc{0%,12%{height:0}44%,84%{height:30px}96%,100%{height:0}}
@keyframes aExAccCv{0%,12%{transform:rotate(-45deg)}44%,84%{transform:rotate(45deg)}96%,100%{transform:rotate(-45deg)}}

/* ---------------- direction-aware ----------------
   Meme transition, axe inverse au retour. Les pastilles disent ou on
   en est, la fenetre montre le sens. */
.ex-direction-aware{display:flex;flex-direction:column;align-items:center;gap:12px;width:150px}
.ex-direction-aware .dots2{display:flex;gap:8px}
.ex-direction-aware .dots2 b{width:8px;height:8px;border-radius:5px;background:var(--s4)}
.ex-direction-aware .dots2 b:nth-child(1){animation:aExDaD1 5s steps(1,end) infinite}
.ex-direction-aware .dots2 b:nth-child(2){animation:aExDaD2 5s steps(1,end) infinite}
.ex-direction-aware .win2{position:relative;width:132px;height:58px;border-radius:10px;overflow:hidden;background:var(--s0)}
.ex-direction-aware .win2 i{position:absolute;inset:0;border-radius:10px}
.ex-direction-aware .s1{background:var(--s3);animation:aExDaA 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-direction-aware .s2{background:var(--accent);animation:aExDaB 5s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExDaA{0%,14%{transform:translateX(0)}38%,58%{transform:translateX(-100%)}82%,100%{transform:translateX(0)}}
@keyframes aExDaB{0%,14%{transform:translateX(100%)}38%,58%{transform:translateX(0)}82%,100%{transform:translateX(100%)}}
@keyframes aExDaD1{0%,16%{background:var(--accent)}18%,80%{background:var(--s4)}84%,100%{background:var(--accent)}}
@keyframes aExDaD2{0%,16%{background:var(--s4)}18%,80%{background:var(--accent)}84%,100%{background:var(--s4)}}

/* ---------------- scroll-reveal ----------------
   Chaque bloc se revele en franchissant le seuil, et ne rejoue plus
   ensuite. La ligne pointillee est le seuil de declenchement. */
.ex-scroll-reveal{width:126px;height:112px}
.ex-scroll-reveal .vp2{position:relative;width:100%;height:100%;border-radius:10px;overflow:hidden;
  background:var(--s0);border:1px solid var(--line)}
.ex-scroll-reveal .fold{position:absolute;left:0;right:0;top:64px;height:0;
  border-top:1px dashed rgba(var(--accent-rgb),.5);z-index:3}
.ex-scroll-reveal .doc4{position:absolute;left:9px;right:9px;top:0;display:flex;flex-direction:column;gap:9px;
  animation:aExSrDoc 6s linear infinite}
.ex-scroll-reveal .doc4 i{height:18px;border-radius:6px;background:var(--s3);flex:none;
  animation:aExSrIt 6s linear infinite}
.ex-scroll-reveal .doc4 .h{height:24px;background:var(--s4)}
.ex-scroll-reveal .doc4 .cd{height:30px;background:var(--accent)}
.ex-scroll-reveal .doc4 i:nth-child(2){animation-delay:-.5s}
.ex-scroll-reveal .doc4 i:nth-child(3){animation-delay:-1s}
.ex-scroll-reveal .doc4 i:nth-child(4){animation-delay:-1.5s}
.ex-scroll-reveal .doc4 i:nth-child(5){animation-delay:-2s}
/* le document remonte une fois puis s'efface, il ne redescend jamais
   sous les yeux du lecteur. Les blocs reveles restent reveles, c'est
   la difference avec une animation pilotee par le scroll. */
@keyframes aExSrDoc{0%{transform:translateY(84px);opacity:0}8%{opacity:1}72%,88%{transform:translateY(-48px);opacity:1}96%,100%{transform:translateY(-48px);opacity:0}}
@keyframes aExSrIt{0%,40%{opacity:0;transform:translateY(10px)}52%,92%{opacity:1;transform:none}100%{opacity:0;transform:translateY(10px)}}

/* ---------------- scroll-driven ----------------
   La barre monte et redescend avec le document. Elle n'a pas d'horloge
   propre, c'est toute la difference avec une apparition au scroll. */
.ex-scroll-driven{display:flex;flex-direction:column;gap:10px;width:132px}
.ex-scroll-driven .pbar{height:6px;border-radius:3px;background:var(--s2);overflow:hidden}
.ex-scroll-driven .pbar i{display:block;height:100%;background:var(--accent);transform-origin:left;
  animation:aExSdB 4.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-scroll-driven .vp3{position:relative;height:92px;border-radius:10px;overflow:hidden;
  background:var(--s0);border:1px solid var(--line)}
.ex-scroll-driven .doc5{position:absolute;left:9px;right:9px;top:8px;height:200px;border-radius:6px;
  background:repeating-linear-gradient(var(--s3) 0 10px,transparent 10px 20px);
  animation:aExSdD 4.6s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExSdB{0%,6%{transform:scaleX(.04)}54%,64%{transform:scaleX(1)}96%,100%{transform:scaleX(.04)}}
@keyframes aExSdD{0%,6%{transform:translateY(0)}54%,64%{transform:translateY(-116px)}96%,100%{transform:translateY(0)}}

/* ---------------- parallax ----------------
   Trois calques, trois distances pour un meme defilement. Le ciel
   parcourt 8px, les collines 20px, le premier plan 44px. */
.ex-parallax{position:relative;width:150px;height:100px;border-radius:11px;overflow:hidden}
.ex-parallax .sky2{position:absolute;left:0;right:0;top:-10px;height:120px;
  background:linear-gradient(var(--s1),var(--s0));animation:aExPxS 5s cubic-bezier(.4,0,.4,1) infinite}
.ex-parallax .hill{position:absolute;left:-12px;right:-12px;bottom:22px;height:52px;background:var(--s3);
  border-radius:50% 50% 0 0/70% 70% 0 0;animation:aExPxH 5s cubic-bezier(.4,0,.4,1) infinite}
.ex-parallax .fg{position:absolute;left:14px;right:14px;bottom:12px;display:flex;flex-direction:column;gap:7px;
  animation:aExPxF 5s cubic-bezier(.4,0,.4,1) infinite}
.ex-parallax .fg i{height:11px;border-radius:5px;background:var(--accent)}
.ex-parallax .fg i.s{width:58%}
@keyframes aExPxS{0%,8%{transform:translateY(0)}56%,66%{transform:translateY(-8px)}96%,100%{transform:translateY(0)}}
@keyframes aExPxH{0%,8%{transform:translateY(0)}56%,66%{transform:translateY(-20px)}96%,100%{transform:translateY(0)}}
@keyframes aExPxF{0%,8%{transform:translateY(0)}56%,66%{transform:translateY(-44px)}96%,100%{transform:translateY(0)}}

/* ---------------- rotate ----------------
   La rotation ne sert a rien ici sinon a dire qu'un travail est en
   cours. Elle tourne d'un tour complet, donc le raccord est invisible. */
.ex-rotate{width:150px;display:flex;flex-direction:column;gap:11px}
.ex-rotate .tb{display:flex;align-items:center;justify-content:space-between;gap:10px}
.ex-rotate .ttl{height:9px;width:56px;border-radius:4px;background:var(--s5)}
.ex-rotate .ic{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--accent);
  border-right-color:transparent;animation:aExRot 1.2s linear infinite}
.ex-rotate .rows{display:flex;flex-direction:column;gap:8px}
.ex-rotate .rows i{height:15px;border-radius:6px;background:var(--s2);animation:aExRotR 3.6s ease infinite}
.ex-rotate .rows i:nth-child(2){animation-delay:.18s}
.ex-rotate .rows i:nth-child(3){animation-delay:.36s;width:72%}
@keyframes aExRot{to{transform:rotate(360deg)}}
@keyframes aExRotR{0%,20%{opacity:.35}44%,86%{opacity:1}96%,100%{opacity:.35}}

/* ---------------- skew ----------------
   Le cisaillement suit la vitesse. A l'arret la carte est droite,
   c'est ce qui distingue un skew d'une carte penchee en decor. */
.ex-skew{width:158px;overflow:hidden}
.ex-skew .rail{display:flex;gap:9px;animation:aExSkwR 3.4s cubic-bezier(.5,0,.3,1) infinite}
.ex-skew .rail i{flex:none;width:46px;height:58px;border-radius:9px;background:var(--s2);border:1px solid var(--line)}
.ex-skew .rail .m{background:linear-gradient(150deg,var(--accent),var(--accent-2));border-color:transparent;
  animation:aExSkw 3.4s cubic-bezier(.5,0,.3,1) infinite}
@keyframes aExSkwR{0%,12%{transform:translateX(0)}56%,88%{transform:translateX(-55px)}100%{transform:translateX(0)}}
@keyframes aExSkw{0%,12%{transform:skewY(0)}30%{transform:skewY(-7deg)}56%,88%{transform:skewY(0)}100%{transform:skewY(0)}}

/* ---------------- tilt3d ----------------
   Deux faces reelles, la seconde n'apparait qu'apres 90 degres. */
.ex-tilt3d{perspective:520px;width:112px;height:76px}
.ex-tilt3d .card8{position:relative;width:100%;height:100%;transform-style:preserve-3d;
  animation:aExFlip 4.6s cubic-bezier(.4,0,.2,1) infinite}
.ex-tilt3d .f,.ex-tilt3d .b{position:absolute;inset:0;border-radius:11px;backface-visibility:hidden;
  display:flex;flex-direction:column;justify-content:center;gap:7px;padding:0 13px}
.ex-tilt3d .f{background:var(--accent)}
.ex-tilt3d .f .big{height:20px;width:46px;border-radius:5px;background:var(--on-accent)}
.ex-tilt3d .f .lb{height:6px;width:66%;border-radius:3px;background:rgba(var(--on-accent-rgb),.45)}
.ex-tilt3d .b{background:var(--card-2);border:1px solid var(--line-2);transform:rotateY(180deg)}
.ex-tilt3d .b i{height:7px;border-radius:4px;background:var(--s5)}
.ex-tilt3d .b i.s{width:62%}
@keyframes aExFlip{0%,16%{transform:rotateY(0)}58%,88%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}

/* ---------------- perspective ----------------
   Rotation identique des deux cotes. Seule la distance de l'oeil
   change, et la deformation avec elle. */
.ex-perspective{display:flex;gap:18px}
.ex-perspective .one{display:flex;flex-direction:column;align-items:center;gap:9px}
.ex-perspective .lb{font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-perspective .c{width:56px;height:64px;border-radius:10px;
  background:linear-gradient(150deg,var(--accent),var(--accent-2));
  animation:aExPsp 4s cubic-bezier(.4,0,.2,1) infinite}
.ex-perspective .nrb{perspective:200px}
.ex-perspective .far{perspective:1200px}
@keyframes aExPsp{0%,100%{transform:rotateY(-32deg)}50%{transform:rotateY(32deg)}}

/* ---------------- transform-origin ----------------
   Deux panneaux, deux ancres opposees. Le point blanc marque le point
   fixe, tout le reste s'en eloigne. */
.ex-transform-origin{display:flex;gap:16px;align-items:flex-start}
.ex-transform-origin .one{position:relative;display:flex;flex-direction:column;gap:6px;width:64px}
.ex-transform-origin .trg{height:18px;border-radius:7px;background:var(--s4)}
.ex-transform-origin .pn{border-radius:9px;background:var(--accent);padding:8px 9px;
  display:flex;flex-direction:column;gap:6px;overflow:hidden}
.ex-transform-origin .pn i{height:6px;border-radius:3px;background:rgba(var(--on-accent-rgb),.45)}
.ex-transform-origin .dn{transform-origin:top center;animation:aExTo 3.6s cubic-bezier(.16,1,.3,1) infinite}
.ex-transform-origin .up{transform-origin:bottom center;animation:aExTo 3.6s cubic-bezier(.16,1,.3,1) infinite;animation-delay:.2s}
.ex-transform-origin .pin{position:absolute;left:50%;margin-left:-4px;width:8px;height:8px;border-radius:5px;
  background:var(--ink);box-shadow:0 0 0 3px rgba(var(--ink-rgb),.18);z-index:3;top:22px}
.ex-transform-origin .pin.p2{top:auto;bottom:22px}
@keyframes aExTo{0%,10%{transform:scaleY(0);opacity:0}34%,84%{transform:scaleY(1);opacity:1}96%,100%{transform:scaleY(0);opacity:0}}

/* ---------------- origin-aware ----------------
   Le meme menu, deux lignes, deux ancres. L'origine est calculee, pas
   ecrite en dur. */
.ex-origin-aware{position:relative;width:158px;height:92px}
.ex-origin-aware .tbl{position:absolute;left:0;top:0;width:96px;display:flex;flex-direction:column;gap:8px}
.ex-origin-aware .r{height:22px;border-radius:7px;background:var(--s2)}
.ex-origin-aware .r1{animation:aExOaR 5s steps(1,end) infinite}
.ex-origin-aware .r3{animation:aExOaR3 5s steps(1,end) infinite}
.ex-origin-aware .mn{position:absolute;right:0;width:58px;border-radius:9px;background:var(--card-2);
  border:1px solid var(--line-2);box-shadow:0 8px 18px rgba(var(--shadow-rgb),.45);
  padding:8px;display:flex;flex-direction:column;gap:6px}
.ex-origin-aware .mn b{height:6px;border-radius:3px;background:var(--accent)}
.ex-origin-aware .m1{top:0;transform-origin:top left;animation:aExOaM 5s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-origin-aware .m3{top:60px;transform-origin:bottom left;animation:aExOaM3 5s cubic-bezier(.2,1.1,.3,1) infinite}
@keyframes aExOaM{0%,4%{transform:scale(.1);opacity:0}18%,42%{transform:scale(1);opacity:1}52%,100%{transform:scale(.1);opacity:0}}
@keyframes aExOaM3{0%,54%{transform:scale(.1);opacity:0}68%,92%{transform:scale(1);opacity:1}98%,100%{transform:scale(.1);opacity:0}}
@keyframes aExOaR{0%,2%{background:var(--s2)}4%,46%{background:var(--s4)}50%,100%{background:var(--s2)}}
@keyframes aExOaR3{0%,52%{background:var(--s2)}54%,94%{background:var(--s4)}98%,100%{background:var(--s2)}}

/* ---------------- crossfade ----------------
   Les deux images se croisent au meme endroit. La plaque opaque sous
   elles evite le fond qui transparait a mi-transition. */
.ex-crossfade{display:flex;flex-direction:column;align-items:center;gap:11px}
.ex-crossfade .fr2{position:relative;width:126px;height:70px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-crossfade .im{position:absolute;inset:0}
.ex-crossfade .im.a{background:linear-gradient(140deg,var(--accent),var(--accent-deep));animation:aExXfA 3.6s ease-in-out infinite}
.ex-crossfade .im.b{background:linear-gradient(140deg,var(--a2),var(--a3));animation:aExXfB 3.6s ease-in-out infinite}
.ex-crossfade .dots{display:flex;gap:6px}
.ex-crossfade .dots b{width:7px;height:7px;border-radius:4px;background:var(--s4)}
.ex-crossfade .dots b:first-child{animation:aExXfD 3.6s ease-in-out infinite}
.ex-crossfade .dots b:last-child{animation:aExXfD2 3.6s ease-in-out infinite}
@keyframes aExXfA{0%,26%{opacity:1}54%,74%{opacity:0}100%{opacity:1}}
@keyframes aExXfB{0%,26%{opacity:0}54%,74%{opacity:1}100%{opacity:0}}
@keyframes aExXfD{0%,26%{background:var(--accent)}54%,74%{background:var(--s4)}100%{background:var(--accent)}}
@keyframes aExXfD2{0%,26%{background:var(--s4)}54%,74%{background:var(--accent)}100%{background:var(--s4)}}

/* ---------------- continuity ----------------
   La pochette garde exactement la meme taille et la meme place. C'est
   la seule chose qui dit que c'est le meme morceau. */
.ex-continuity{display:grid;place-items:center;width:158px;height:104px}
.ex-continuity .pl{display:flex;align-items:center;gap:11px;padding:10px;border-radius:12px;
  background:var(--card-2);border:1px solid var(--line-2);width:132px;
  animation:aExCont 4.2s cubic-bezier(.3,.9,.3,1) infinite}
.ex-continuity .art{width:38px;height:38px;border-radius:8px;flex:none;
  background:linear-gradient(150deg,var(--accent),var(--accent-2))}
.ex-continuity .txt{flex:1;display:flex;flex-direction:column;gap:7px}
.ex-continuity .txt b{height:7px;border-radius:4px;background:var(--s5)}
.ex-continuity .txt b.s{width:58%}
@keyframes aExCont{0%,12%{width:132px;height:58px;flex-direction:row}48%,84%{width:132px;height:96px;flex-direction:column}96%,100%{width:132px;height:58px;flex-direction:row}}

/* ---------------- orchestration ----------------
   Quatre elements, quatre offsets serres, une seule intention. Le
   dernier arrive 120ms apres le premier, pas assez pour lire une
   cascade, assez pour que l'ensemble ait du poids. */
.ex-orchestration{position:relative;width:150px;height:96px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-orchestration .scrim{position:absolute;inset:0;background:rgba(var(--void-rgb),.5);animation:aExOrcS 4s ease infinite}
.ex-orchestration .dlg{position:absolute;left:14px;right:14px;top:16px;padding:11px 12px;border-radius:11px;
  background:var(--card-2);border:1px solid var(--line-2);box-shadow:0 12px 26px rgba(var(--shadow-rgb),.5);
  display:flex;flex-direction:column;gap:7px;transform-origin:center;animation:aExOrc 4s cubic-bezier(.16,1,.3,1) infinite}
.ex-orchestration .t{height:9px;width:56%;border-radius:4px;background:var(--ink);opacity:.85;animation:aExOrcI 4s cubic-bezier(.16,1,.3,1) infinite;animation-delay:.05s}
.ex-orchestration .l{height:6px;border-radius:3px;background:var(--s5);animation:aExOrcI 4s cubic-bezier(.16,1,.3,1) infinite;animation-delay:.09s}
.ex-orchestration .l.s{width:62%;animation-delay:.13s}
.ex-orchestration .btns{display:flex;gap:6px;justify-content:flex-end;margin-top:2px;animation:aExOrcI 4s cubic-bezier(.16,1,.3,1) infinite;animation-delay:.17s}
.ex-orchestration .btns b{height:15px;border-radius:5px}
.ex-orchestration .btns .gh{width:28px;background:var(--s4)}
.ex-orchestration .btns .pr{width:36px;background:var(--accent)}
@keyframes aExOrcS{0%,10%{opacity:0}30%,84%{opacity:1}96%,100%{opacity:0}}
@keyframes aExOrc{0%,10%{opacity:0;transform:scale(.92)}30%,84%{opacity:1;transform:none}96%,100%{opacity:0;transform:scale(.92)}}
@keyframes aExOrcI{0%,14%{opacity:0;transform:translateY(6px)}34%,84%{opacity:1;transform:none}96%,100%{opacity:0;transform:none}}

/* ---------------- delay ----------------
   Le panneau s'ouvre tout de suite, les entrees attendent. Le crochet
   materialise l'attente, sans lui le delai ne se distingue pas d'une
   animation lente. */
.ex-delay{position:relative;width:148px;height:104px}
.ex-delay .trg{position:absolute;left:0;top:0;width:58px;height:20px;border-radius:8px;background:var(--accent)}
.ex-delay .mn{position:absolute;left:0;top:28px;width:104px;border-radius:11px;background:var(--card-2);
  border:1px solid var(--line-2);display:flex;flex-direction:column;gap:7px;padding:10px 11px;
  animation:aExDlyP 4s cubic-bezier(.16,1,.3,1) infinite}
.ex-delay .mn i{height:8px;border-radius:4px;background:var(--s4);animation:aExDlyI 4s cubic-bezier(.16,1,.3,1) infinite}
.ex-delay .mn i:nth-child(2){width:78%}.ex-delay .mn i:nth-child(3){width:88%}.ex-delay .mn i:nth-child(4){width:60%}
.ex-delay .wt{position:absolute;right:0;top:28px;width:30px;height:22px;border-radius:5px;
  background:repeating-linear-gradient(-45deg,var(--s4) 0 3px,transparent 3px 6px);
  animation:aExDlyW 4s steps(1,end) infinite}
@keyframes aExDlyP{0%,8%{opacity:0;transform:scaleY(.5);transform-origin:top}24%,84%{opacity:1;transform:none}96%,100%{opacity:0;transform:scaleY(.5)}}
@keyframes aExDlyI{0%,30%{opacity:0;transform:translateX(-8px)}46%,84%{opacity:1;transform:none}96%,100%{opacity:0;transform:translateX(-8px)}}
@keyframes aExDlyW{0%,23%{opacity:0}25%,44%{opacity:1}46%,100%{opacity:0}}

/* ---------------- duration ----------------
   Meme vitesse ressentie, distances differentes, donc durees
   differentes. C'est la regle, pas deux reglages arbitraires. */
.ex-duration{display:flex;flex-direction:column;gap:14px;width:150px}
.ex-duration .one{display:flex;align-items:center;gap:10px}
.ex-duration .lb{width:38px;font:600 9px/1 var(--mono);color:var(--ink-3);font-style:normal;flex:none}
.ex-duration .chp{width:44px;height:20px;border-radius:10px;background:var(--accent);
  animation:aExDurA 3.4s cubic-bezier(.16,1,.3,1) infinite}
.ex-duration .pg{flex:1;height:44px;border-radius:9px;background:var(--s2);border:1px solid var(--line);
  display:flex;flex-direction:column;justify-content:center;gap:6px;padding:0 10px;
  animation:aExDurB 3.4s cubic-bezier(.16,1,.3,1) infinite}
.ex-duration .pg b{height:6px;border-radius:3px;background:var(--s5)}
.ex-duration .pg b:last-child{width:58%}
@keyframes aExDurA{0%,8%{opacity:0;transform:translateY(8px)}22%,86%{opacity:1;transform:none}96%,100%{opacity:0;transform:translateY(8px)}}
@keyframes aExDurB{0%,8%{opacity:0;transform:translateY(20px)}44%,86%{opacity:1;transform:none}96%,100%{opacity:0;transform:translateY(20px)}}

/* ---------------- fill-mode ----------------
   La barre du haut garde sa derniere image, celle du bas repart a
   zero des la fin, ce qui se lit comme un bug. */
.ex-fill-mode{display:flex;flex-direction:column;gap:12px;width:158px}
.ex-fill-mode .fr{display:flex;align-items:center;gap:9px}
.ex-fill-mode .nm{width:34px;height:7px;border-radius:4px;background:var(--s5);flex:none}
.ex-fill-mode .nm.s{width:26px}
.ex-fill-mode .pb{flex:1;height:8px;border-radius:4px;background:var(--s2);overflow:hidden}
.ex-fill-mode .pb b{display:block;height:100%;background:var(--accent);transform-origin:left}
.ex-fill-mode .keep{animation:aExFmK 3.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-fill-mode .snap{animation:aExFmS 3.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-fill-mode .ok,.ex-fill-mode .ko{width:11px;height:11px;border-radius:6px;flex:none}
.ex-fill-mode .ok{background:var(--accent);animation:aExFmOk 3.6s steps(1,end) infinite}
.ex-fill-mode .ko{border:1.5px dashed var(--danger);box-sizing:border-box}
@keyframes aExFmK{0%{transform:scaleX(0);opacity:0}6%{opacity:1}54%,88%{transform:scaleX(1);opacity:1}96%,100%{transform:scaleX(1);opacity:0}}
@keyframes aExFmS{0%{transform:scaleX(0);opacity:0}6%{opacity:1}54%{transform:scaleX(1);opacity:1}56%,88%{transform:scaleX(0);opacity:1}96%,100%{transform:scaleX(0);opacity:0}}
@keyframes aExFmOk{0%,54%{opacity:0}58%,88%{opacity:1}96%,100%{opacity:0}}

/* ---------------- stepped ----------------
   L'anneau et le chiffre avancent par crans, jamais en continu.
   Cinq paliers, quatre bonds, steps(5) sur les deux. */
/* Le compte a rebours se reinitialise par nature. Le saut de 1 vers 5
   est donc legitime, mais il se produit pendant que le widget entier
   est en fondu, on ne le voit jamais. */
/* Le compte a rebours se reinitialise par nature. Le saut de 1 vers 5
   est donc legitime, mais il se produit pendant que le widget entier
   est en fondu, on ne le voit jamais.
   Point cle du calcul, cinq chiffres de 34px veulent une course de
   170px avec steps(5, end), qui donne 0, -34, -68, -102, -136. Une
   course de 136px aurait donne des paliers de 27px et coupe les
   chiffres en deux, ce qui etait le defaut de la version precedente. */
.ex-stepped{position:relative;display:grid;place-items:center;width:104px;height:104px;
  animation:aExStpFade 5s ease-in-out infinite}
@keyframes aExStpFade{0%,4%{opacity:0}10%,88%{opacity:1}96%,100%{opacity:0}}
.ex-stepped .ring{width:98px;height:98px}
.ex-stepped .ring .bg{stroke:var(--s2);stroke-width:6}
.ex-stepped .ring .fg{stroke:var(--accent);stroke-width:6;stroke-dasharray:164;stroke-dashoffset:0;
  stroke-linecap:round;transform:rotate(-90deg);transform-origin:center;
  animation:aExStp 5s steps(5,end) infinite}
.ex-stepped .win{position:absolute;height:34px;overflow:hidden;width:26px;display:grid}
.ex-stepped .reel{display:flex;flex-direction:column;font:800 28px/34px var(--mono);color:var(--ink);
  text-align:center;animation:aExStpR 5s steps(5,end) infinite}
.ex-stepped .reel b{height:34px}
@keyframes aExStp{0%{stroke-dashoffset:0}94%,100%{stroke-dashoffset:164}}
@keyframes aExStpR{0%{transform:translateY(0)}94%,100%{transform:translateY(-170px)}}

/* ---------------- translate ----------------
   La carte franchit d'abord la distance horizontale, puis se pose.
   Les colonnes ne bougent pas, seule la carte est translatee. */
.ex-translate{position:relative;display:flex;gap:10px;width:158px}
.ex-translate .col{flex:1;display:flex;flex-direction:column;gap:7px;padding:8px;border-radius:10px;
  background:var(--s0);border:1px solid var(--line)}
.ex-translate .hd{height:6px;width:60%;border-radius:3px;background:var(--s5)}
.ex-translate .c{height:22px;border-radius:6px;background:var(--s3)}
.ex-translate .mv{position:absolute;left:8px;top:22px;width:56px;height:22px;border-radius:6px;
  background:var(--accent);box-shadow:0 6px 14px rgba(var(--shadow-rgb),.4);
  animation:aExTr 4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExTr{0%,10%{transform:translate(0,0)}46%{transform:translate(84px,0)}70%,86%{transform:translate(84px,29px)}96%,100%{transform:translate(0,0)}}

/* ---------------- scale ----------------
   Seule la vignette survolee change de taille, la grille ne bouge pas.
   C'est ce qui separe scale d'une animation de largeur. */
.ex-scale{position:relative;width:132px;height:96px}
.ex-scale .gr{position:absolute;inset:0;display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ex-scale .gr i{border-radius:9px;background:var(--s2);border:1px solid var(--line)}
.ex-scale .gr .z{background:linear-gradient(150deg,var(--accent),var(--accent-deep));border-color:transparent;
  position:relative;z-index:2;animation:aExScl 3.6s cubic-bezier(.16,1,.3,1) infinite}
.ex-scale .cur{left:96px;top:22px;animation:aExSclC 3.6s cubic-bezier(.16,1,.3,1) infinite}
@keyframes aExScl{0%,18%{transform:scale(1)}42%,80%{transform:scale(1.14)}96%,100%{transform:scale(1)}}
@keyframes aExSclC{0%,10%{transform:translate(16px,10px);opacity:.5}34%,80%{transform:none;opacity:1}96%,100%{transform:translate(16px,10px);opacity:.5}}

/* ---------------- stagger ----------------
   Trois familles d'elements entrent en une seule vague diagonale.
   Le decalage suit la somme ligne plus colonne, ce qui donne la
   diagonale, et non un simple decalage par index. */
.ex-stagger{width:172px;display:flex;flex-direction:column;gap:10px}
.ex-stagger .bar{display:flex;align-items:center;gap:8px;height:16px}
.ex-stagger .bar .dt{width:16px;height:16px;border-radius:5px;background:var(--accent);flex:none}
.ex-stagger .bar .w{flex:1;height:8px;border-radius:4px;background:var(--s4)}
.ex-stagger .cols{display:flex;gap:9px}
.ex-stagger .side{width:46px;display:flex;flex-direction:column;gap:8px;flex:none}
.ex-stagger .side i{height:11px;border-radius:4px;background:var(--s3)}
.ex-stagger .grd{flex:1;display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.ex-stagger .grd i{height:31px;border-radius:7px;background:var(--s2);border:1px solid var(--line)}
.ex-stagger .grd i:nth-child(1),.ex-stagger .grd i:nth-child(5){background:var(--accent);border-color:transparent}
.ex-stagger .bar,.ex-stagger .side i,.ex-stagger .grd i{
  animation:aExStag 4.4s cubic-bezier(.16,1,.3,1) infinite}
.ex-stagger .side i:nth-child(1){animation-delay:.05s}
.ex-stagger .side i:nth-child(2){animation-delay:.12s}
.ex-stagger .side i:nth-child(3){animation-delay:.19s}
.ex-stagger .side i:nth-child(4){animation-delay:.26s}
.ex-stagger .grd i:nth-child(1){animation-delay:.10s}
.ex-stagger .grd i:nth-child(2){animation-delay:.17s}
.ex-stagger .grd i:nth-child(3){animation-delay:.24s}
.ex-stagger .grd i:nth-child(4){animation-delay:.17s}
.ex-stagger .grd i:nth-child(5){animation-delay:.24s}
.ex-stagger .grd i:nth-child(6){animation-delay:.31s}
@keyframes aExStag{0%,12%{opacity:0;transform:translateY(10px) scale(.96)}34%,86%{opacity:1;transform:none}96%,100%{opacity:0;transform:none}}

/* ---------------- tween ---------------- */
.ex-tween{width:132px;height:82px}
.ex-tween .grd{position:absolute;left:0;top:0;display:grid;grid-template-columns:repeat(2,26px);gap:6px}
.ex-tween .grd i{height:22px;border-radius:6px;background:var(--s2)}
.ex-tween .slot{position:absolute;right:0;bottom:0;width:66px;height:44px;border-radius:9px;border:1.5px dashed var(--s5)}
/* Trois images intermediaires, posees a leur position ET a leur taille
   interpolees, qui apparaissent au passage de la vignette. Un box-shadow
   rigide donnerait une trainee identique a l'arret et n'interpolerait
   pas la taille, ce qui est pourtant la moitie du sujet ici. */
.ex-tween .on{position:absolute;border-radius:7px;background:var(--accent);opacity:0}
.ex-tween .g1{left:17px;top:10px;width:36px;height:28px;animation:aExTwG1 4s linear infinite}
.ex-tween .g2{left:33px;top:19px;width:46px;height:33px;animation:aExTwG2 4s linear infinite}
.ex-tween .g3{left:50px;top:29px;width:56px;height:39px;animation:aExTwG3 4s linear infinite}
@keyframes aExTwG1{0%,18%{opacity:0}23%,86%{opacity:.24}94%,100%{opacity:0}}
@keyframes aExTwG2{0%,31%{opacity:0}36%,86%{opacity:.24}94%,100%{opacity:0}}
@keyframes aExTwG3{0%,43%{opacity:0}48%,86%{opacity:.24}94%,100%{opacity:0}}
.ex-tween .mv{position:absolute;left:0;top:0;width:26px;height:22px;border-radius:6px;background:var(--accent);
  z-index:2;transform-origin:top left;animation:aExTween 4s linear infinite}
@keyframes aExTween{0%,6%{transform:translate(0,0) scale(1);opacity:0}12%{opacity:1}56%,86%{transform:translate(66px,38px) scale(2.54,2);opacity:1}94%,100%{transform:translate(66px,38px) scale(2.54,2);opacity:0}}

/* ---------------- float ----------------
   Deux elements flottants sur une carte, un bouton d'action et une
   fiche, chacun avec son ombre au sol qui retrecit quand il monte.
   Les deux tournent sur le meme cycle de 3.6s mais la fiche porte un
   delai negatif, un groupe synchronise se lirait comme un bloc rigide. */
.ex-float{width:168px;height:132px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-float .map{position:absolute;inset:0;background:var(--s1)}
.ex-float .rd{position:absolute;background:var(--s3)}
.ex-float .r1{left:0;right:0;top:46px;height:7px}
.ex-float .r2{left:0;right:0;top:94px;height:5px}
.ex-float .r3{top:0;bottom:0;left:64px;width:6px}
.ex-float .blk{position:absolute;border-radius:3px;background:var(--s2)}
.ex-float .b1{left:8px;top:8px;width:46px;height:30px}
.ex-float .b2{left:80px;top:10px;width:52px;height:28px}
.ex-float .b3{left:12px;top:62px;width:40px;height:24px}
.ex-float .pin{position:absolute;width:9px;height:9px;border-radius:50%;background:var(--a2);
  box-shadow:0 0 0 3.5px rgba(var(--a2-rgb),.2)}
.ex-float .p1{left:98px;top:62px}
.ex-float .p2{left:36px;top:102px}
.ex-float .sh{position:absolute;border-radius:50%;background:rgba(var(--shadow-rgb),.55);filter:blur(4px)}
.ex-float .sf{right:16px;bottom:8px;width:26px;height:8px;animation:aExFltShA 3.6s ease-in-out infinite}
.ex-float .fab{position:absolute;right:12px;bottom:14px;width:34px;height:34px;border-radius:50%;
  background:var(--lime);animation:aExFltFab 3.6s ease-in-out infinite}
.ex-float .fab i{position:absolute;left:50%;top:50%;background:rgba(var(--on-accent-rgb),.8);border-radius:2px;
  width:14px;height:3px;margin:-1.5px 0 0 -7px}
.ex-float .fab .v{width:3px;height:14px;margin:-7px 0 0 -1.5px}
.ex-float .sc{left:19px;top:63px;width:62px;height:9px;background:rgba(var(--shadow-rgb),.42);
  animation:aExFltShB 3.6s ease-in-out -1.4s infinite}
.ex-float .crd{position:absolute;left:11px;top:14px;width:78px;padding:9px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);display:flex;flex-direction:column;gap:5px;
  box-shadow:0 6px 14px rgba(var(--shadow-rgb),.28);animation:aExFltCrd 3.6s ease-in-out -1.4s infinite}
.ex-float .crd .t{height:7px;width:60%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-float .crd .l{height:5px;border-radius:3px;background:var(--s5)}
.ex-float .crd .l.s{width:66%}
@keyframes aExFltFab{0%,100%{transform:translateY(-3px)}50%{transform:translateY(-13px)}}
@keyframes aExFltShA{0%,100%{transform:scale(1);opacity:.5}50%{transform:scale(.68);opacity:.24}}
@keyframes aExFltCrd{0%,100%{transform:translateY(-2px)}50%{transform:translateY(-9px)}}
@keyframes aExFltShB{0%,100%{transform:scale(1);opacity:.4}50%{transform:scale(.76);opacity:.2}}

/* ---------------- idle ----------------
   Le formulaire est rempli, les trois champs sont valides, plus rien
   ne se passe. La piste sous le bouton mesure ce temps mort, et au
   moment exact ou elle arrive au bout le bouton se signale. Meme
   duree de 4s pour les deux, sinon la relation se perd. */
.ex-idle{width:156px;height:152px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column}
.ex-idle .frm{display:flex;flex-direction:column;gap:6px}
.ex-idle .ttl{height:8px;width:54%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-idle .fld{position:relative;height:18px;border-radius:6px;background:var(--s1);border:1px solid var(--line)}
.ex-idle .fld i{position:absolute;left:7px;top:6px;height:5px;width:52%;border-radius:3px;background:var(--s5)}
.ex-idle .tick{position:absolute;right:6px;top:4px;width:9px;height:9px;border-radius:50%;background:var(--lime)}
.ex-idle .ctb{margin-top:auto;height:26px;border-radius:8px;background:var(--lime);
  display:grid;place-items:center;animation:aExIdle 4s ease-in-out infinite}
.ex-idle .ctb i{width:44px;height:6px;border-radius:3px;background:rgba(var(--on-accent-rgb),.45)}
.ex-idle .wait{position:relative;height:3px;border-radius:2px;background:var(--s2);overflow:hidden;margin-top:7px}
.ex-idle .wait b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;background:var(--ink-3);
  animation:aExIdleWait 4s linear infinite}
@keyframes aExIdle{0%,84%,100%{transform:scale(1) rotate(0)}88%{transform:scale(1.04) rotate(-1deg)}
  92%{transform:scale(1.03) rotate(1deg)}96%{transform:scale(1) rotate(0)}}
/* la remise a zero est instantanee et se produit sur une barre de
   largeur nulle, donc invisible. Depart et arrivee sont identiques. */
@keyframes aExIdleWait{0%{width:0}84%{width:100%}84.001%,100%{width:0}}

/* ---------------- blur ----------------
   Les deux proprietes au travail dans la meme galerie. La vignette
   sensible porte un filter, son propre contenu part dans le flou avec
   elle. La barre du haut porte un backdrop-filter, son titre reste net
   pendant que la rangee de photos qui passe dessous se brouille. */
.ex-blur{width:170px;height:124px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-blur .gal{position:absolute;inset:0;padding:9px;display:grid;grid-template-columns:repeat(3,1fr);
  gap:7px;align-content:start}
.ex-blur .th{position:relative;height:49px;border-radius:6px;background:linear-gradient(135deg,var(--s3),var(--s2))}
.ex-blur .th:nth-child(2){background:linear-gradient(135deg,var(--a2),var(--a3))}
.ex-blur .th:nth-child(5){background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-blur .th:nth-child(6){background:linear-gradient(135deg,var(--a4),var(--a3))}
.ex-blur .spoil{background:linear-gradient(135deg,var(--a4),var(--warn));display:grid;place-items:center;
  animation:aExBlurF 3.8s ease-in-out infinite}
.ex-blur .spoil em{font:700 9px/1 var(--mono);color:var(--void);margin-top:16px}
.ex-blur .bar{position:absolute;left:0;right:0;top:0;height:27px;display:flex;align-items:center;gap:7px;
  padding:0 10px;background:rgba(var(--void-rgb),.3);border-bottom:1px solid var(--line);
  animation:aExBlurB 3.8s ease-in-out infinite}
.ex-blur .bar .l{height:7px;width:52px;border-radius:4px;background:var(--ink);opacity:.9}
.ex-blur .bar .d{width:7px;height:7px;border-radius:50%;background:var(--s6)}
.ex-blur .bar .l+.d{margin-left:auto}
@keyframes aExBlurF{0%,10%{filter:blur(0)}46%,72%{filter:blur(5px)}96%,100%{filter:blur(0)}}
@keyframes aExBlurB{0%,10%{-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}
  46%,72%{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
  96%,100%{-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}}

/* ---------------- clip-path ----------------
   La couverture se devoile derriere une coupe en biais. Quatre points
   a chaque etat, x en haut et x moins 28 pour cent en bas, ce qui donne
   une diagonale de 31 degres sur une boite de 160 sur 74. Le lisere
   suit exactement le milieu de cette diagonale, de -14 a 114 pour cent,
   et rappelle que le bord ne se degrade jamais. */
.ex-clip-path{width:160px;height:134px;border-radius:11px;overflow:hidden;
  background:var(--card-2);border:1px solid var(--line-2)}
.ex-clip-path .crd{position:absolute;inset:0;display:flex;flex-direction:column}
.ex-clip-path .cov{position:relative;height:74px;overflow:hidden;flex:0 0 auto}
.ex-clip-path .ph{position:absolute;inset:0;background:var(--s2)}
.ex-clip-path .cut{position:absolute;inset:0;
  background:repeating-linear-gradient(45deg,var(--lime) 0 9px,var(--accent-2) 9px 18px);
  animation:aExClip 4.4s cubic-bezier(.4,0,.2,1) infinite}
.ex-clip-path .meta{padding:10px 11px;display:flex;flex-direction:column;gap:6px}
.ex-clip-path .meta .t{height:8px;width:62%;border-radius:4px;background:var(--ink);opacity:.88}
.ex-clip-path .meta .l{height:5px;width:88%;border-radius:3px;background:var(--s5)}
.ex-clip-path .tags{display:flex;gap:5px;margin-top:2px}
.ex-clip-path .tags em{width:26px;height:11px;border-radius:5px;background:var(--s3)}
.ex-clip-path .tags em:first-child{background:rgba(var(--accent-rgb),.3)}
.ex-clip-path .edge{position:absolute;top:0;height:74px;width:2px;margin-left:-1px;background:var(--ink);
  transform:skewX(-31deg);animation:aExClipEdge 4.4s cubic-bezier(.4,0,.2,1) infinite}
@keyframes aExClip{
  0%,8%{clip-path:polygon(0 0,0 0,-28% 100%,0 100%)}
  50%,74%{clip-path:polygon(0 0,128% 0,100% 100%,0 100%)}
  96%,100%{clip-path:polygon(0 0,0 0,-28% 100%,0 100%)}}
@keyframes aExClipEdge{0%,8%{left:-14%;opacity:0}14%{opacity:1}50%,74%{left:114%;opacity:1}
  82%{opacity:0}96%,100%{left:-14%;opacity:0}}

/* ---------------- mask ----------------
   Le cas d'usage le plus courant du masque, les bords d'un carrousel de
   filtres. Le degrade coupe a 14 et 86 pour cent, donc une puce qui sort
   du cadre s'efface progressivement au lieu d'etre sectionnee. Un
   clip-path ferait la meme chose avec un bord franc, et la difference se
   voit precisement ici. */
.ex-mask{width:168px;height:132px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:9px}
.ex-mask .hd{display:flex;align-items:center;gap:8px}
.ex-mask .hd .t{height:8px;width:56px;border-radius:4px;background:var(--ink);opacity:.88}
.ex-mask .hd .a{width:18px;height:18px;border-radius:6px;background:var(--s3);margin-left:auto}
.ex-mask .win{position:relative;height:22px;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 14%,#000 86%,transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 14%,#000 86%,transparent 100%)}
.ex-mask .rail{position:absolute;left:0;top:0;display:flex;gap:6px;
  animation:aExMask 5s cubic-bezier(.4,0,.2,1) infinite}
.ex-mask .rail i{flex:0 0 auto;padding:0 9px;height:22px;border-radius:11px;background:var(--s2);
  font:600 9px/22px var(--mono);color:var(--ink-2);white-space:nowrap}
.ex-mask .rail .on{background:var(--lime);color:var(--on-accent)}
.ex-mask .lst{display:flex;flex-direction:column;gap:7px}
.ex-mask .lst b{height:13px;border-radius:5px;background:var(--s1)}
.ex-mask .lst b:nth-child(2){width:78%}
.ex-mask .lst b:nth-child(3){width:62%}
@keyframes aExMask{0%,8%{transform:translateX(0)}48%,62%{transform:translateX(-70px)}
  96%,100%{transform:translateX(0)}}

/* ---------------- before-after ----------------
   Un editeur photo. La couche retouchee est celle de droite, decoupee
   par un inset gauche, et la poignee porte exactement la meme valeur
   avec une marge de -1px pour tomber sur son centre et pas sur son
   bord. Les deux couches ne bougent ni ne se redimensionnent jamais. */
.ex-before-after{width:170px;height:134px;border-radius:11px;overflow:hidden;background:var(--s0);
  display:flex;flex-direction:column}
.ex-before-after .tb{display:flex;align-items:center;gap:6px;padding:0 9px;height:24px;flex:0 0 auto;
  background:var(--panel);border-bottom:1px solid var(--line)}
.ex-before-after .tb .ic{width:13px;height:13px;border-radius:4px;background:var(--s4)}
.ex-before-after .tb .ic.on{background:var(--lime)}
.ex-before-after .tb .sp{width:1px;height:13px;background:var(--line-2)}
.ex-before-after .cv{position:relative;flex:1;overflow:hidden}
.ex-before-after .raw{position:absolute;inset:0;
  background:repeating-linear-gradient(45deg,var(--s2) 0 8px,var(--s1) 8px 16px)}
.ex-before-after .fix{position:absolute;inset:0;background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExBa 4.4s cubic-bezier(.4,0,.2,1) infinite}
.ex-before-after .hdl{position:absolute;top:0;bottom:0;width:2px;margin-left:-1px;background:var(--ink);
  z-index:2;animation:aExBaHdl 4.4s cubic-bezier(.4,0,.2,1) infinite}
.ex-before-after .hdl::after{content:"";position:absolute;top:50%;left:50%;width:18px;height:18px;
  margin:-9px 0 0 -9px;border-radius:50%;background:var(--ink);box-shadow:0 2px 6px rgba(var(--shadow-rgb),.5)}
.ex-before-after .tag{position:absolute;bottom:6px;padding:3px 5px;border-radius:4px;
  font:600 8px/1 var(--mono);background:rgba(var(--void-rgb),.66);color:var(--ink-2)}
.ex-before-after .ta{left:6px}
.ex-before-after .tz{right:6px;color:var(--lime)}
.ex-before-after .film{display:flex;gap:6px;padding:7px 9px;flex:0 0 auto;
  background:var(--panel);border-top:1px solid var(--line)}
.ex-before-after .film b{flex:1;height:16px;border-radius:4px;background:var(--s2)}
.ex-before-after .film b.on{background:var(--s4);box-shadow:0 0 0 1.5px var(--lime)}
@keyframes aExBa{0%,8%{clip-path:inset(0 0 0 74%)}52%,68%{clip-path:inset(0 0 0 16%)}
  96%,100%{clip-path:inset(0 0 0 74%)}}
@keyframes aExBaHdl{0%,8%{left:74%}52%,68%{left:16%}96%,100%{left:74%}}

/* ---------------- line-drawing ----------------
   Une signature au bas d'un contrat. pathLength a 1 pour que le
   dasharray ne depende plus de la longueur reelle du trace, on peut
   redessiner la courbe sans retoucher une seule valeur. */
.ex-line-drawing{width:164px;height:152px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-line-drawing .doc{display:flex;flex-direction:column;gap:6px}
.ex-line-drawing .doc .h{height:8px;width:52%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-line-drawing .doc .p{height:5px;border-radius:3px;background:var(--s3)}
.ex-line-drawing .doc .p.s{width:64%}
.ex-line-drawing .pad{position:relative;margin-top:auto;height:52px;border-radius:8px;
  background:var(--s0);border:1px dashed var(--line-2)}
.ex-line-drawing .pad svg{position:absolute;left:9px;top:7px;width:120px;height:34px}
.ex-line-drawing .pad path{fill:none;stroke:var(--lime);stroke-width:2.5;stroke-linecap:round;
  stroke-linejoin:round;stroke-dasharray:1;stroke-dashoffset:1;
  animation:aExLine 4.4s cubic-bezier(.5,0,.2,1) infinite}
.ex-line-drawing .rule{position:absolute;left:9px;right:9px;bottom:12px;height:1px;background:var(--line-2)}
.ex-line-drawing .lb{position:absolute;left:9px;bottom:3px;font:600 7px/1 var(--mono);color:var(--ink-3)}
@keyframes aExLine{0%,6%{stroke-dashoffset:1}58%,86%{stroke-dashoffset:0}98%,100%{stroke-dashoffset:1}}

/* ---------------- text-morph ----------------
   PAUSED devient PLAYED sur l'etiquette d'etat d'un lecteur. Le P, le E
   et le D sont communs aux deux mots, ils restent gris et parfaitement
   immobiles. Seules les trois lettres qui different roulent, decalees de
   70ms. Boite de 14px, glyphe de 14px, translation de -14px. */
.ex-text-morph{width:168px;height:136px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-text-morph .pl{display:flex;align-items:center;gap:9px}
.ex-text-morph .art{width:38px;height:38px;border-radius:8px;flex:0 0 auto;
  background:linear-gradient(135deg,var(--a3),var(--a4))}
.ex-text-morph .mt{display:flex;flex-direction:column;gap:6px;flex:1}
.ex-text-morph .mt .t{height:7px;width:76%;border-radius:4px;background:var(--ink);opacity:.88}
.ex-text-morph .mt .a{height:5px;width:52%;border-radius:3px;background:var(--s5)}
.ex-text-morph .wav{display:flex;align-items:flex-end;gap:3px;height:26px}
.ex-text-morph .wav b{flex:1;border-radius:2px;height:34%;background:var(--s3)}
.ex-text-morph .wav b:nth-child(3n+1){height:64%}
.ex-text-morph .wav b:nth-child(4n){height:88%}
.ex-text-morph .wav b:nth-child(5n+2){height:48%}
.ex-text-morph .wav b:nth-child(-n+5){background:var(--lime)}
.ex-text-morph .ft{display:flex;align-items:center;gap:9px;margin-top:auto}
.ex-text-morph .chp{display:flex;align-items:center;gap:1px;padding:4px 7px;border-radius:6px;
  background:var(--s1);border:1px solid var(--line)}
.ex-text-morph .chp .c{width:8px;height:14px;text-align:center;font:700 11px/14px var(--mono)}
.ex-text-morph .chp .st{color:var(--ink-3)}
.ex-text-morph .chp .ro{overflow:hidden;color:var(--lime)}
.ex-text-morph .chp .ro em{height:14px;animation:aExTmorph 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-text-morph .chp .k2 em{animation-delay:.07s}
.ex-text-morph .chp .k3 em{animation-delay:.14s}
.ex-text-morph .tr{display:flex;align-items:center;gap:8px;margin-left:auto}
.ex-text-morph .tr .ic{width:12px;height:12px;border-radius:3px;background:var(--s4)}
.ex-text-morph .tr .pp{width:18px;height:18px;border-radius:50%;background:var(--lime)}
@keyframes aExTmorph{0%,26%{transform:translateY(0)}46%,82%{transform:translateY(-14px)}
  96%,100%{transform:translateY(0)}}

/* ---------------- skeleton ----------------
   Le gabarit et le vrai contenu portent exactement la meme geometrie,
   avatar 22px, deux lignes, vignette 26px, aux memes coordonnees. Ils
   ne font que se croiser en opacite, donc rien ne peut bouger a
   l'arrivee du contenu. C'est le premier point de la consigne, et il
   ne se demontre qu'en superposant les deux etats au pixel pres. */
.ex-skeleton{width:170px;height:144px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:9px}
.ex-skeleton .hd{display:flex;align-items:center;gap:8px}
.ex-skeleton .hd .t{height:8px;width:58px;border-radius:4px;background:var(--ink);opacity:.88}
.ex-skeleton .hd .a{width:16px;height:16px;border-radius:5px;background:var(--s3);margin-left:auto}
.ex-skeleton .rw{position:relative;height:26px}
.ex-skeleton .ph,.ex-skeleton .rl{position:absolute;inset:0}
.ex-skeleton .av{position:absolute;left:0;top:2px;width:22px;height:22px;border-radius:50%}
.ex-skeleton .l1{position:absolute;left:29px;top:4px;width:52px;height:7px;border-radius:4px}
.ex-skeleton .l2{position:absolute;left:29px;top:16px;width:36px;height:5px;border-radius:3px}
.ex-skeleton .th{position:absolute;right:0;top:0;width:26px;height:26px;border-radius:6px}
.ex-skeleton .ph i{background:var(--s2)}
.ex-skeleton .rl .av{background:linear-gradient(135deg,var(--a2),var(--a3))}
.ex-skeleton .rl .l1{background:var(--ink);opacity:.82}
.ex-skeleton .rl .l2{background:var(--s5)}
.ex-skeleton .rl .th{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-skeleton .ph{animation:aExSkPh 5.2s ease-in-out infinite}
.ex-skeleton .rl{animation:aExSkRl 5.2s ease-in-out infinite}
.ex-skeleton .w2 .ph,.ex-skeleton .w2 .rl{animation-delay:.16s}
.ex-skeleton .w3 .ph,.ex-skeleton .w3 .rl{animation-delay:.32s}
/* le reflet ne balaie que pendant que le gabarit est visible */
.ex-skeleton .sheen{position:absolute;left:0;top:44px;height:92px;width:54px;
  background:linear-gradient(90deg,transparent,rgba(var(--accent-rgb),.26),transparent);
  animation:aExSkSh 5.2s linear infinite}
@keyframes aExSkPh{0%,44%{opacity:1}54%,90%{opacity:0}96%,100%{opacity:1}}
@keyframes aExSkRl{0%,44%{opacity:0}54%,90%{opacity:1}96%,100%{opacity:0}}
/* Le reflet s'eteint la ou il est arrive, PUIS revient a sa position
   de depart, jamais l'inverse. Un retour a opacite non nulle donnerait
   un reflet qui traverse la scene a l'envers en un dixieme de seconde. */
@keyframes aExSkSh{0%{transform:translateX(-60px);opacity:0}
  3%{opacity:1}
  33%{transform:translateX(190px);opacity:1}
  35%{transform:translateX(190px);opacity:0}
  35.001%,100%{transform:translateX(-60px);opacity:0}}

/* ---------------- number-ticker ----------------
   Trois colonnes, trois distances. Les centaines montent d'un cran,
   les dizaines de cinq, les unites de neuf. Glyphe de 26px, donc
   -26px, -130px et -234px, tous multiples exacts de 26. La valeur
   finale se lit 2 8 9. */
.ex-number-ticker{width:166px;height:134px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-number-ticker .hd{display:flex;align-items:center;gap:8px}
.ex-number-ticker .hd .t{height:7px;width:62px;border-radius:4px;background:var(--s5)}
.ex-number-ticker .hd .dot{width:7px;height:7px;border-radius:50%;background:var(--lime);margin-left:auto}
.ex-number-ticker .val{display:flex;align-items:flex-end;gap:2px}
.ex-number-ticker .w{display:block;width:19px;height:26px;overflow:hidden}
.ex-number-ticker .r{display:block;font:800 20px/26px var(--mono);font-variant-numeric:tabular-nums;
  color:var(--ink);text-align:center}
.ex-number-ticker .r b{display:block;height:26px}
.ex-number-ticker .ra{animation:aExNtA 4s cubic-bezier(.2,.9,.25,1) infinite}
.ex-number-ticker .rb{animation:aExNtB 4s cubic-bezier(.2,.9,.25,1) infinite}
.ex-number-ticker .rc{animation:aExNtC 4s cubic-bezier(.2,.9,.25,1) infinite}
.ex-number-ticker .u{font:700 11px/16px var(--mono);color:var(--ink-3);margin-left:3px;margin-bottom:2px}
.ex-number-ticker .chp{display:flex;align-items:center;gap:5px;align-self:flex-start;
  padding:3px 7px;border-radius:9px;background:rgba(var(--accent-rgb),.18)}
.ex-number-ticker .chp .ar{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;
  border-bottom:6px solid var(--lime)}
.ex-number-ticker .chp b{width:22px;height:5px;border-radius:3px;background:var(--lime)}
.ex-number-ticker .spk{display:flex;align-items:flex-end;gap:3px;height:26px;margin-top:auto}
.ex-number-ticker .spk i{flex:1;border-radius:2px;background:var(--s3);height:32%}
.ex-number-ticker .spk i:nth-child(3n){height:58%}
.ex-number-ticker .spk i:nth-child(4n+2){height:76%}
.ex-number-ticker .spk i:nth-child(9),.ex-number-ticker .spk i:nth-child(10){background:var(--lime);height:100%}
@keyframes aExNtA{0%,10%{transform:translateY(0)}66%,90%{transform:translateY(-26px)}98%,100%{transform:translateY(0)}}
@keyframes aExNtB{0%,10%{transform:translateY(0)}70%,90%{transform:translateY(-130px)}98%,100%{transform:translateY(0)}}
@keyframes aExNtC{0%,10%{transform:translateY(0)}74%,90%{transform:translateY(-234px)}98%,100%{transform:translateY(0)}}

/* ---------------- tabular-numbers ----------------
   Un classement. Les temps sont en chasse tabulaire, donc les deux
   points et les separateurs decimaux tombent sur la meme verticale
   d'une ligne a l'autre, et le trait de repere le montre. Le temps de
   la deuxieme ligne alterne entre deux valeurs de chiffres tres
   differents, 1:11.07 et 1:00.00, sans que la colonne bouge. */
.ex-tabular-numbers{width:170px;height:148px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:7px}
.ex-tabular-numbers .hd .t{height:8px;width:54px;border-radius:4px;background:var(--ink);opacity:.88}
.ex-tabular-numbers .rw{display:flex;align-items:center;gap:7px;height:22px}
.ex-tabular-numbers .hot{background:rgba(var(--accent-rgb),.1);border-radius:6px;margin:0 -4px;padding:0 4px}
.ex-tabular-numbers .rk{width:9px;flex:0 0 auto;font:700 9px/1 var(--mono);color:var(--ink-3);text-align:center}
.ex-tabular-numbers .av{width:16px;height:16px;border-radius:50%;flex:0 0 auto;background:var(--s3)}
.ex-tabular-numbers .rw:nth-child(3) .av{background:var(--lime)}
.ex-tabular-numbers .nm{flex:1;height:6px;border-radius:3px;background:var(--s4)}
.ex-tabular-numbers .nm.s{width:60%;flex:0 0 auto}
.ex-tabular-numbers .tm{position:relative;flex:0 0 auto;font:700 11px/22px var(--mono);
  font-variant-numeric:tabular-nums;color:var(--ink-2)}
.ex-tabular-numbers .hot .tm{color:var(--lime)}
.ex-tabular-numbers .stk{width:46px;height:22px}
.ex-tabular-numbers .stk u{position:absolute;left:0;top:0;text-decoration:none;white-space:nowrap}
.ex-tabular-numbers .stk .v1{animation:aExTnA 3.2s steps(1,end) infinite}
.ex-tabular-numbers .stk .v2{animation:aExTnB 3.2s steps(1,end) infinite}
.ex-tabular-numbers .gd{position:absolute;right:15px;top:30px;bottom:10px;width:1px;
  background:repeating-linear-gradient(180deg,var(--line-2) 0 3px,transparent 3px 6px)}
@keyframes aExTnA{0%,2%{opacity:1}50%,52%{opacity:0}98%,100%{opacity:1}}
@keyframes aExTnB{0%,2%{opacity:0}50%,52%{opacity:1}98%,100%{opacity:0}}

/* ---------------- typewriter ----------------
   Vingt et un caracteres, vingt et un pas, largeur en ch. Le caret vit
   sur son propre rythme de 900ms, comme un vrai curseur, il ne se cale
   pas sur la frappe. */
.ex-typewriter{width:168px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-typewriter .msg{display:flex;gap:6px}
.ex-typewriter .u{flex-direction:column;align-items:flex-end;gap:5px}
.ex-typewriter .u b{height:7px;border-radius:4px;background:var(--s3);width:76px}
.ex-typewriter .u .s{width:48px}
.ex-typewriter .a .av{width:16px;height:16px;border-radius:6px;flex:0 0 auto;background:var(--lime)}
.ex-typewriter .bd{display:flex;align-items:center;gap:3px;padding:8px 9px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);font:600 9px/15px var(--mono)}
.ex-typewriter .tx{display:block;overflow:hidden;white-space:nowrap;color:var(--ink);width:0;
  animation:aExTwTx 5s steps(17,end) infinite}
.ex-typewriter .cr{display:block;flex:0 0 auto;width:2px;height:12px;background:var(--lime);
  animation:aExTwCr .9s steps(1,end) infinite}
.ex-typewriter .tb{display:flex;align-items:center;gap:7px;margin-top:auto}
.ex-typewriter .tb .st{width:34px;height:15px;border-radius:8px;background:var(--s3)}
.ex-typewriter .tb .d{width:6px;height:6px;border-radius:50%;background:var(--s4)}
@keyframes aExTwTx{0%{width:0}60%,84%{width:17ch}96%,100%{width:0}}
@keyframes aExTwCr{0%,50%{opacity:1}51%,100%{opacity:0}}

/* ---------------- fps ----------------
   Une liste qui defile a vitesse constante et le compteur en incruste.
   Les dix barres du graphe sont a la meme hauteur, ce qui est la seule
   chose honnete a montrer pour une cadence stable. La carte Jank
   reprend le meme decor et c'est la que la difference se lit. */
.ex-fps{position:relative;width:170px;height:128px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-fps .vp{position:absolute;inset:0;overflow:hidden;padding:10px}
.ex-fps .lst{display:flex;flex-direction:column;gap:8px;animation:aExFpsScroll 4.4s linear infinite}
.ex-fps .lst i{height:20px;border-radius:6px;background:var(--s2)}
.ex-fps .lst i:nth-child(2n){background:var(--s1)}
.ex-fps .hud{position:absolute;right:9px;top:9px;padding:6px 8px;border-radius:8px;
  background:rgba(var(--void-rgb),.8);display:flex;align-items:flex-end;gap:5px}
.ex-fps .hud .n{font:800 15px/1 var(--mono);color:var(--lime)}
.ex-fps .hud em{font:600 8px/1 var(--mono);color:var(--ink-3);margin-bottom:2px}
/* Le graphe defile a cadence constante, une barre entre toutes les
   1.1s pour dix barres sur 11s. La regularite est donc PRODUITE par
   l'animation et non affirmee par dix barres figees a la meme
   hauteur, ce qui serait un decor qui pretend mesurer. */
.ex-fps .gr{display:flex;align-items:flex-end;gap:2px;height:15px;margin-left:3px;
  width:47px;overflow:hidden}
.ex-fps .gr i{flex:0 0 auto;width:3px;height:100%;border-radius:1px;background:var(--lime);
  animation:aExFpsBar 11s linear infinite}
/* Convention marquee du projet, le contenu est duplique en deux
   moities identiques et on translate de -50 pour cent. Vingt barres de
   5px de pas font 100px pour une fenetre de 47, donc la seconde moitie
   couvre toujours la fenetre et le raccord ne se voit jamais. */
@keyframes aExFpsBar{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
@keyframes aExFpsScroll{0%{transform:translateY(0)}50%{transform:translateY(-84px)}100%{transform:translateY(0)}}

/* ---------------- jank ----------------
   Meme liste que la carte fps, meme amplitude de 84px, meme aller
   retour. Seule la regularite change. Ici elle avance par a coups de
   8, 26, 18 puis 32px avec des paliers ou rien ne bouge, et chaque
   palier tombe sur une tache longue du fil principal en dessous.
   Cause et effet dans le meme cadre. */
.ex-jank{position:relative;width:170px;height:136px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:7px}
.ex-jank .vp{position:relative;height:78px;overflow:hidden;border-radius:7px}
.ex-jank .lst{display:flex;flex-direction:column;gap:8px;animation:aExJnkScroll 3.2s linear infinite}
.ex-jank .lst i{height:20px;border-radius:6px;background:var(--s2)}
.ex-jank .lst i:nth-child(2n){background:var(--s1)}
.ex-jank .lbl{font:600 8px/1 var(--mono);color:var(--ink-3)}
.ex-jank .mt{display:flex;align-items:flex-end;gap:3px;height:13px}
.ex-jank .mt b{flex:1;height:7px;border-radius:2px;background:var(--s2)}
.ex-jank .mt .lg{height:13px;animation:aExJnkTask 3.2s steps(1,end) infinite}
.ex-jank .mt b:nth-child(2){animation-delay:.42s}
.ex-jank .mt b:nth-child(4){animation-delay:1.41s}
.ex-jank .mt b:nth-child(7){animation-delay:2.43s}
@keyframes aExJnkScroll{0%{transform:translateY(0)}
  10%,13%{transform:translateY(-8px)}
  24%,26%{transform:translateY(-34px)}
  38%,44%{transform:translateY(-52px)}
  50%{transform:translateY(-84px)}
  60%,63%{transform:translateY(-58px)}
  74%,76%{transform:translateY(-40px)}
  88%,92%{transform:translateY(-12px)}
  100%{transform:translateY(0)}}
@keyframes aExJnkTask{0%,2%{background:var(--s2)}5%,16%{background:var(--danger)}20%,100%{background:var(--s2)}}

/* ---------------- dropped-frame ----------------
   La piste des images. Sept images a une unite de large et celle qui
   a manque son echeance a deux, exactement le double, parce que c'est
   la seule chose qui se passe, l'image precedente reste affichee deux
   battements. Neuf unites en haut, neuf graduations vsync en bas, la
   large en couvre donc deux pile. 2 fois 16.7 donne bien 33 ms. */
.ex-dropped-frame{position:relative;width:172px;height:122px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-dropped-frame .hd{display:flex;align-items:center;gap:8px}
.ex-dropped-frame .hd .t{height:7px;width:54px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-dropped-frame .hd .lg{width:26px;height:11px;border-radius:4px;background:var(--s2);margin-left:auto}
.ex-dropped-frame .tl{display:flex;align-items:flex-end;gap:2px;height:40px}
.ex-dropped-frame .tl i{flex:1;height:25px;border-radius:3px 3px 0 0;background:var(--lime)}
.ex-dropped-frame .tl .wide{flex:2;height:40px;background:var(--danger)}
.ex-dropped-frame .ax{display:flex;gap:2px}
.ex-dropped-frame .ax u{flex:1;height:4px;border-radius:1px;background:var(--s3);text-decoration:none}
.ex-dropped-frame .tip{position:absolute;left:76px;top:38px;padding:3px 6px;border-radius:5px;
  background:var(--danger);color:var(--void);font:700 8px/1 var(--mono);
  animation:aExDfTip 3.4s ease-in-out infinite}
@keyframes aExDfTip{0%,14%{opacity:0;transform:translateY(3px)}26%,74%{opacity:1;transform:translateY(0)}
  86%,100%{opacity:0;transform:translateY(3px)}}

/* ---------------- compositing ----------------
   Les deux boites partagent la MEME keyframe et la meme duree, donc le
   deplacement est rigoureusement identique a l'oeil. Seule la jauge
   change, elle se remplit sur le panneau qui anime top et reste vide
   sur celui qui anime transform. Le mouvement est le meme, le cout ne
   l'est pas, et c'est tout ce que le terme a a dire. */
.ex-compositing{width:172px;height:150px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:8px}
.ex-compositing .pn{display:flex;flex-direction:column;gap:5px;padding:8px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2)}
.ex-compositing .pn .hd{font:700 8px/1 var(--mono)}
.ex-compositing .a .hd{color:var(--danger)}
.ex-compositing .b .hd{color:var(--lime)}
.ex-compositing .cv{position:relative;height:18px;border-radius:5px;background:var(--s1);overflow:hidden}
.ex-compositing .bx{position:absolute;left:3px;top:3px;width:12px;height:12px;border-radius:3px;
  animation:aExCpMv 3s cubic-bezier(.45,0,.25,1) infinite}
.ex-compositing .a .bx{background:var(--danger)}
.ex-compositing .b .bx{background:var(--lime)}
.ex-compositing .mtr{height:5px;border-radius:3px;background:var(--s2);overflow:hidden}
/* La jauge du panneau transform reste a ZERO et n a aucune animation.
   Ce n est pas un oubli, c est la demonstration. Animer transform ne
   coute rien au fil principal, le compositeur s en charge, donc le
   compteur de cout ne monte jamais. Seul le panneau du haut se remplit. */
.ex-compositing .mtr b{display:block;height:5px;width:0;border-radius:3px;background:var(--danger)}
.ex-compositing .fl{animation:aExCpFill 3s linear infinite}
@keyframes aExCpMv{0%,8%{transform:translateX(0)}50%,58%{transform:translateX(116px)}96%,100%{transform:translateX(0)}}
@keyframes aExCpFill{0%{width:0}54%{width:100%}96%,100%{width:0}}

/* ---------------- will-change ----------------
   L'ordre est tout. La couche apparait a 22 pour cent, le mouvement
   demarre a 30, se termine a 94, et la couche n'est rendue qu'a 96.
   Annoncee avant, retiree apres, jamais promue en pleine course.
   La couche est un enfant de la carte, elle voyage donc avec elle. */
.ex-will-change{width:168px;height:152px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:grid;grid-template-columns:1fr 1fr;gap:9px;align-content:start}
.ex-will-change .cd{position:relative;height:52px;border-radius:8px;background:var(--card-2);
  border:1px solid var(--line-2);padding:7px;display:flex;flex-direction:column;gap:5px}
.ex-will-change .cd i{height:5px;border-radius:3px;background:var(--s4)}
.ex-will-change .cd i:first-child{height:18px;border-radius:5px;background:var(--s2)}
.ex-will-change .hot{z-index:2;animation:aExWcLift 3.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-will-change .hot i:first-child{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-will-change .lay{position:absolute;inset:-4px;border-radius:12px;border:1.5px dashed var(--lime);
  opacity:0;animation:aExWcLay 3.6s ease infinite}
.ex-will-change .tip{grid-column:1/-1;font:600 8px/1 var(--mono);color:var(--lime);
  animation:aExWcLay 3.6s ease infinite}
@keyframes aExWcLay{0%,10%{opacity:0}22%,88%{opacity:1}96%,100%{opacity:0}}
@keyframes aExWcLift{0%,30%{transform:translateY(0) scale(1)}
  56%,74%{transform:translateY(-5px) scale(1.04)}94%,100%{transform:translateY(0) scale(1)}}

/* ---------------- layout-thrashing ----------------
   Le curseur bleu mesure une ligne, puis on ecrit, puis on mesure la
   suivante. Quatre lignes, quatre recalculs, chacun sous la mesure qui
   l'a declenche. Pas de ligne de 14px plus 6px d'ecart, donc 20px, et
   les paliers du curseur valent 0, 20, 40 et 60 exactement. */
.ex-layout-thrashing{width:170px;height:132px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:8px}
.ex-layout-thrashing .hd{display:flex;align-items:center;gap:8px}
.ex-layout-thrashing .hd .t{height:7px;width:48px;border-radius:4px;background:var(--s5)}
.ex-layout-thrashing .fps{margin-left:auto;font:700 9px/1 var(--mono);color:var(--danger)}
.ex-layout-thrashing .lst{position:relative;display:flex;flex-direction:column;gap:6px}
.ex-layout-thrashing .lst i{height:14px;border-radius:5px;background:var(--s2)}
.ex-layout-thrashing .sel{position:absolute;left:-3px;right:-3px;top:-3px;height:20px;border-radius:6px;
  border:1.5px solid var(--a2);animation:aExLtCur 3s steps(1,end) infinite}
.ex-layout-thrashing .lay{display:flex;gap:4px;height:8px}
.ex-layout-thrashing .lay b{flex:1;border-radius:3px;background:var(--s1);
  animation:aExLtLay 3s steps(1,end) infinite}
.ex-layout-thrashing .lay b:nth-child(2){animation-delay:.75s}
.ex-layout-thrashing .lay b:nth-child(3){animation-delay:1.5s}
.ex-layout-thrashing .lay b:nth-child(4){animation-delay:2.25s}
@keyframes aExLtCur{0%,2%{transform:translateY(0)}25%{transform:translateY(20px)}
  50%{transform:translateY(40px)}75%{transform:translateY(60px)}98%,100%{transform:translateY(0)}}
@keyframes aExLtLay{0%,2%{background:var(--s1)}5%,20%{background:var(--danger)}24%,100%{background:var(--s1)}}

/* ---------------- purposeful ----------------
   Le fichier ne se contente pas de bouger, il quitte une liste et se
   pose dans un dossier dont le compteur passe de 7 a 8 au moment exact
   ou il arrive. Le mouvement a repondu a la question, ou est passee la
   chose, et la consequence le confirme. */
.ex-purposeful{position:relative;width:168px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-purposeful .hd .t{height:8px;width:52px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-purposeful .lst{display:flex;flex-direction:column;gap:6px}
.ex-purposeful .lst i{height:17px;border-radius:5px;background:var(--s2)}
.ex-purposeful .mv{background:var(--lime);animation:aExPurMv 3.6s cubic-bezier(.4,0,.2,1) infinite}
.ex-purposeful .fld{margin-top:auto;display:flex;align-items:center;gap:9px;padding:8px 10px;
  border-radius:9px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-purposeful .ic{width:22px;height:18px;border-radius:4px;background:var(--s4)}
.ex-purposeful .cnt{position:relative;width:12px;height:16px;margin-left:auto;
  font:700 12px/16px var(--mono);color:var(--lime)}
.ex-purposeful .cnt u{position:absolute;left:0;top:0;text-decoration:none}
.ex-purposeful .c1{animation:aExPurA 3.6s steps(1,end) infinite}
.ex-purposeful .c2{animation:aExPurB 3.6s steps(1,end) infinite}
@keyframes aExPurMv{0%{transform:translate(0,0) scale(1);opacity:0}
  6%{opacity:1}
  16%{transform:translate(0,0) scale(1)}
  56%,76%{transform:translate(-52px,52px) scale(.34,.9);opacity:1}
  82%{transform:translate(-52px,52px) scale(.34,.9);opacity:0}
  82.001%,100%{transform:translate(0,0) scale(1);opacity:0}}
@keyframes aExPurA{0%,54%{opacity:1}56%,96%{opacity:0}98%,100%{opacity:1}}
@keyframes aExPurB{0%,54%{opacity:0}56%,96%{opacity:1}98%,100%{opacity:0}}

/* ---------------- antic ----------------
   Le bouton se comprime a 30 pour cent puis se detend a 42, et le
   message ne part qu'apres, a 46. L'elan precede toujours le depart,
   sinon ce n'est plus une preparation mais un rebond. */
.ex-antic{position:relative;width:168px;height:134px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-antic .th{display:flex;flex-direction:column;gap:6px;align-items:flex-end}
.ex-antic .th b{height:14px;width:74%;border-radius:7px;background:var(--s2)}
.ex-antic .th .s{width:52%}
.ex-antic .fly{position:absolute;right:11px;bottom:52px;width:64px;height:14px;border-radius:7px;
  background:var(--lime);animation:aExAnFly 3.4s cubic-bezier(.3,0,.2,1) infinite}
.ex-antic .bar{margin-top:auto;display:flex;align-items:center;gap:8px;padding:6px;
  border-radius:10px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-antic .inp{flex:1;height:8px;border-radius:4px;background:var(--s3)}
.ex-antic .btn{flex:0 0 auto;width:26px;height:26px;border-radius:50%;background:var(--lime);
  animation:aExAnBtn 3.4s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExAnBtn{0%,20%{transform:scale(1)}30%{transform:scale(.86)}
  42%{transform:scale(1.12)}56%,100%{transform:scale(1)}}
@keyframes aExAnFly{0%,40%{transform:translateY(0);opacity:0}
  46%{transform:translateY(0);opacity:1}
  68%,86%{transform:translateY(-46px);opacity:1}
  92%{transform:translateY(-46px);opacity:0}
  92.001%,100%{transform:translateY(0);opacity:0}}

/* ---------------- follow-through ----------------
   La feuille se pose a 48 pour cent, son contenu ne se stabilise qu'a
   74, apres un depassement de 3px et un seul retour. Le corps arrive,
   les parties suivent. La vignette exagere le retard pour qu'il se
   voie, la vraie fourchette est dans le prompt. */
.ex-follow-through{position:relative;width:168px;height:142px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-follow-through .app{position:absolute;inset:0;padding:11px;display:flex;flex-direction:column;gap:7px}
.ex-follow-through .app i{height:9px;border-radius:4px;background:var(--s2)}
.ex-follow-through .app .hd{height:14px;width:56%;background:var(--s3)}
.ex-follow-through .sheet{position:absolute;left:0;right:0;bottom:0;height:88px;
  border-radius:14px 14px 0 0;background:var(--card-2);border:1px solid var(--line-2);border-bottom:0;
  padding:9px 12px;display:flex;flex-direction:column;gap:7px;
  box-shadow:0 -8px 22px rgba(var(--shadow-rgb),.4);
  animation:aExFtSheet 3.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-follow-through .grip{width:32px;height:4px;border-radius:2px;background:var(--s4);align-self:center}
.ex-follow-through .l1,.ex-follow-through .l2{height:7px;border-radius:4px;background:var(--s4);
  animation:aExFtInner 3.6s cubic-bezier(.2,1.5,.3,1) infinite}
.ex-follow-through .l2{width:64%;animation-delay:.06s}
.ex-follow-through .btns{display:flex;gap:7px;justify-content:flex-end;margin-top:auto;
  animation:aExFtInner 3.6s cubic-bezier(.2,1.5,.3,1) infinite;animation-delay:.12s}
.ex-follow-through .btns b{width:30px;height:16px;border-radius:6px;background:var(--s3)}
.ex-follow-through .btns .p{background:var(--lime)}
@keyframes aExFtSheet{0%,10%{transform:translateY(88px)}48%,86%{transform:translateY(0)}98%,100%{transform:translateY(88px)}}
@keyframes aExFtInner{0%,16%{transform:translateY(20px);opacity:0}
  30%{transform:translateY(10px);opacity:1}
  62%{transform:translateY(-3px);opacity:1}
  74%,86%{transform:translateY(0);opacity:1}
  94%{transform:translateY(0);opacity:0}
  98%,100%{transform:translateY(20px);opacity:0}}

/* ---------------- squash-stretch ----------------
   Volume conserve a chaque etape. .85 x 1.18 = 1.003, .89 x 1.12 =
   0.997, 1.28 x .781 = 0.9997, .88 x 1.14 = 1.003, .86 x 1.16 = 0.998.
   Origine sur le bas de la pastille, sinon elle traverse l'icone au
   moment precis ou elle est censee la heurter. */
.ex-squash-stretch{position:relative;width:166px;height:120px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;display:flex;flex-direction:column;gap:20px}
.ex-squash-stretch .hd .t{height:8px;width:50px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-squash-stretch .row{display:flex;gap:12px;margin-top:auto}
.ex-squash-stretch .ic{flex:1;height:38px;border-radius:11px;background:var(--s2)}
.ex-squash-stretch .ic.on{background:linear-gradient(135deg,var(--a3),var(--a4))}
.ex-squash-stretch .bdg{position:absolute;left:88px;top:64px;width:20px;height:20px;border-radius:50%;
  background:var(--danger);transform-origin:center bottom;
  animation:aExSsBdg 2.4s cubic-bezier(.4,0,.6,1) infinite}
@keyframes aExSsBdg{
  0%{transform:translateY(-40px) scale(.85,1.18)}
  40%{transform:translateY(-4px) scale(.89,1.12)}
  48%{transform:translateY(0) scale(1.28,.781)}
  58%{transform:translateY(-7px) scale(.88,1.14)}
  84%{transform:translateY(-36px) scale(.86,1.16)}
  100%{transform:translateY(-40px) scale(.85,1.18)}}

/* ---------------- perceived-performance ----------------
   Le message s'affiche a 12 pour cent, des la pression. Le reseau, lui,
   ne repond qu'a 72, et la barre grise le prouve. Rien n'a ete acceleree,
   seule la reponse a la geste est arrivee tout de suite. */
.ex-perceived-performance{position:relative;width:168px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-perceived-performance .th{display:flex;flex-direction:column;gap:6px}
.ex-perceived-performance .th b{height:14px;width:70%;border-radius:7px;background:var(--s2)}
.ex-perceived-performance .th .me{align-self:flex-end;width:54%;background:var(--s3)}
.ex-perceived-performance .th .s{width:46%}
.ex-perceived-performance .sent{align-self:flex-end;display:flex;align-items:center;gap:7px;
  padding:5px 8px;border-radius:8px;background:var(--s3);
  animation:aExPpSent 3.8s cubic-bezier(.2,.9,.25,1) infinite}
.ex-perceived-performance .txt{width:46px;height:6px;border-radius:3px;background:var(--s5)}
.ex-perceived-performance .tick{width:9px;height:9px;border-radius:50%;background:var(--s5);
  animation:aExPpTick 3.8s steps(1,end) infinite}
.ex-perceived-performance .net{position:relative;height:4px;border-radius:2px;background:var(--s2);overflow:hidden}
.ex-perceived-performance .net b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:var(--ink-3);animation:aExPpNet 3.8s linear infinite}
.ex-perceived-performance .bar{margin-top:auto;display:flex;align-items:center;gap:8px;padding:6px;
  border-radius:10px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-perceived-performance .inp{flex:1;height:8px;border-radius:4px;background:var(--s3)}
.ex-perceived-performance .btn{flex:0 0 auto;width:24px;height:24px;border-radius:50%;background:var(--lime)}
@keyframes aExPpSent{0%,6%{opacity:0;transform:translateY(6px)}
  12%,92%{opacity:1;transform:translateY(0)}
  97%,100%{opacity:0;transform:translateY(6px)}}
@keyframes aExPpTick{0%,70%{background:var(--s5)}74%,92%{background:var(--lime)}96%,100%{background:var(--s5)}}
@keyframes aExPpNet{0%,6%{width:0}72%,92%{width:100%}96%,100%{width:0}}

/* ---------------- frequency ----------------
   Le menu tourne en 1.1s et la carte d'accueil en 3.3s, rapport de
   trois exactement, donc on voit litteralement trois ouvertures pour
   une apparition. La jauge du bas cumule le temps passe a attendre le
   menu, elle monte d'un tiers a chaque ouverture.
   Le menu s'ouvre depuis SON declencheur, la troisieme icone a 69px,
   pas depuis un coin arbitraire. */
.ex-frequency{position:relative;width:168px;height:136px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-frequency .tb{display:flex;gap:7px}
.ex-frequency .tb .ic{width:22px;height:20px;border-radius:6px;background:var(--s2)}
.ex-frequency .tb .dd{background:var(--lime)}
.ex-frequency .menu{position:absolute;left:69px;top:36px;width:64px;padding:6px;border-radius:8px;
  background:var(--card-2);border:1px solid var(--line-2);display:flex;flex-direction:column;gap:5px;
  z-index:2;transform-origin:top left;animation:aExFrqMenu 1.1s cubic-bezier(.2,.9,.25,1) infinite}
.ex-frequency .menu i{height:6px;border-radius:3px;background:var(--s4)}
.ex-frequency .one{margin-top:auto;padding:10px;border-radius:9px;background:var(--card-2);
  border:1px solid var(--line-2);display:flex;flex-direction:column;gap:6px;
  animation:aExFrqOne 3.3s cubic-bezier(.2,1.2,.3,1) infinite}
.ex-frequency .one .t{height:8px;width:56%;border-radius:4px;background:var(--lime)}
.ex-frequency .one .l{height:5px;border-radius:3px;background:var(--s4)}
.ex-frequency .cost{position:relative;height:4px;border-radius:2px;background:var(--s2);overflow:hidden}
.ex-frequency .cost b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:var(--warn);animation:aExFrqCost 3.3s steps(1,end) infinite}
@keyframes aExFrqMenu{0%,4%{opacity:0;transform:scale(.9)}14%,62%{opacity:1;transform:scale(1)}
  74%,100%{opacity:0;transform:scale(.9)}}
@keyframes aExFrqOne{0%,8%{opacity:0;transform:scale(.94)}22%,84%{opacity:1;transform:scale(1)}
  94%,100%{opacity:0;transform:scale(.94)}}
@keyframes aExFrqCost{0%{width:0}30%{width:33%}63%{width:66%}96%{width:100%}97%,100%{width:0}}

/* ---------------- spatial-consistency ----------------
   La fiche entre par la droite et repart par la droite. Un seul
   translateX de 100 pour cent, jamais son inverse, donc la carte
   mentale de l'utilisateur reste valide d'un aller retour a l'autre. */
.ex-spatial-consistency{position:relative;width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0)}
.ex-spatial-consistency .nav{position:absolute;left:0;right:0;top:0;height:26px;z-index:3;
  background:var(--panel);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:8px;padding:0 10px}
.ex-spatial-consistency .bk{width:8px;height:8px;border-radius:2px;background:var(--lime)}
.ex-spatial-consistency .ti{height:7px;width:54px;border-radius:4px;background:var(--s5)}
.ex-spatial-consistency .lst{position:absolute;left:0;right:0;top:26px;bottom:0;padding:10px;
  display:flex;flex-direction:column;gap:7px}
.ex-spatial-consistency .lst i{height:18px;border-radius:6px;background:var(--s2)}
.ex-spatial-consistency .det{position:absolute;left:0;right:0;top:26px;bottom:0;z-index:2;
  background:var(--card-2);border-left:1px solid var(--line-2);padding:11px;
  display:flex;flex-direction:column;gap:8px;box-shadow:-10px 0 22px rgba(var(--shadow-rgb),.4);
  animation:aExScDet 4s cubic-bezier(.4,0,.2,1) infinite}
.ex-spatial-consistency .det .h{height:30px;border-radius:7px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-spatial-consistency .det .l{height:6px;border-radius:3px;background:var(--s4)}
.ex-spatial-consistency .det .l.s{width:64%}
@keyframes aExScDet{0%,10%{transform:translateX(100%)}44%,72%{transform:translateX(0)}96%,100%{transform:translateX(100%)}}

/* ---------------- hardware-acceleration ----------------
   La liste elle meme est le sujet. Le curseur descend les cinq entrees,
   deux badgees compositor et trois badgees layout ou paint. Ligne de
   17px plus 3px d'ecart, donc un pas de 20px exactement. */
.ex-hardware-acceleration{width:172px;height:148px;border-radius:11px;overflow:hidden;background:var(--void);
  padding:10px;display:flex;flex-direction:column;gap:8px}
.ex-hardware-acceleration .q{font:600 9px/1 var(--mono);color:var(--ink-2)}
.ex-hardware-acceleration .ac{position:relative;display:flex;flex-direction:column;gap:3px;
  padding:4px;border-radius:7px;background:var(--s0);border:1px solid var(--line-2)}
.ex-hardware-acceleration .ac i{position:relative;z-index:2;display:flex;align-items:center;
  justify-content:space-between;height:17px;padding:0 6px;border-radius:5px;
  font:600 8px/17px var(--mono);color:var(--ink-2)}
.ex-hardware-acceleration .ac em{font-style:normal}
.ex-hardware-acceleration .ac u{text-decoration:none;font:600 7px/1 var(--mono);padding:3px 5px;
  border-radius:4px;background:rgba(var(--danger-rgb),.16);color:var(--danger)}
.ex-hardware-acceleration .ac .on{color:var(--lime)}
.ex-hardware-acceleration .ac .on u{background:rgba(var(--accent-rgb),.2);color:var(--lime)}
.ex-hardware-acceleration .sel{position:absolute;left:4px;right:4px;top:4px;height:17px;border-radius:5px;
  background:rgba(var(--ink-rgb),.09);animation:aExHaCur 3.5s steps(1,end) infinite}
@keyframes aExHaCur{0%,2%{transform:translateY(0)}20%{transform:translateY(20px)}
  40%{transform:translateY(40px)}60%{transform:translateY(60px)}80%{transform:translateY(80px)}
  98%,100%{transform:translateY(0)}}

/* ---------------- reduced-motion ----------------
   L'interrupteur bascule a la moitie du cycle. Avant, la carte voyage
   de 110px. Apres, elle ne fait plus que passer d'invisible a visible,
   aux memes instants et sur la meme duree. Le mouvement part, pas
   l'information. */
.ex-reduced-motion{position:relative;width:168px;height:142px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-reduced-motion .set{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2)}
.ex-reduced-motion .set .l{flex:1;height:6px;border-radius:3px;background:var(--s5)}
.ex-reduced-motion .sw{position:relative;flex:0 0 auto;width:30px;height:16px;border-radius:9px;
  background:var(--s3);animation:aExRmSw 5s steps(1,end) infinite}
.ex-reduced-motion .sw i{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;
  background:var(--ink);animation:aExRmKn 5s steps(1,end) infinite}
.ex-reduced-motion .app{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden;padding:10px}
.ex-reduced-motion .crd{position:absolute;left:10px;right:10px;top:10px;height:32px;border-radius:8px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExRmCard 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-reduced-motion .app .r{position:absolute;left:10px;right:10px;height:7px;border-radius:4px;background:var(--s3)}
.ex-reduced-motion .app .r{top:50px}
.ex-reduced-motion .app .r.s{top:64px;right:56px}
@keyframes aExRmSw{0%,48%{background:var(--s3)}50%,98%{background:var(--lime)}99%,100%{background:var(--s3)}}
@keyframes aExRmKn{0%,48%{transform:translateX(0)}50%,98%{transform:translateX(14px)}99%,100%{transform:translateX(0)}}
@keyframes aExRmCard{0%,8%{transform:translateX(110px);opacity:0}
  24%,40%{transform:translateX(0);opacity:1}
  46%{transform:translateX(110px);opacity:0}
  50%,58%{transform:translateX(0);opacity:0}
  74%,90%{transform:translateX(0);opacity:1}
  96%{transform:translateX(0);opacity:0}
  100%{transform:translateX(110px);opacity:0}}

/* ---------------- staging ----------------
   La grille tombe a 28 pour cent d'opacite pendant que la photo
   s'ouvre. L'origine de l'agrandissement est calee sur la vignette du
   milieu de la premiere rangee, a 22.5 pour cent de la hauteur du
   heros, sinon la photo naitrait d'un point ou l'utilisateur n'a
   jamais clique. */
.ex-staging{position:relative;width:170px;height:118px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:10px;display:grid;grid-template-columns:repeat(3,1fr);gap:8px;align-content:start}
.ex-staging .th{height:36px;border-radius:7px;background:var(--s2);animation:aExStgDim 4s ease infinite}
.ex-staging .th:nth-child(3n){background:var(--s3)}
.ex-staging .hero{position:absolute;left:10px;right:10px;top:10px;height:80px;border-radius:9px;z-index:2;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));transform-origin:center 22.5%;
  box-shadow:0 10px 26px rgba(var(--shadow-rgb),.45);
  animation:aExStgHero 4s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aExStgDim{0%,14%{opacity:1}44%,80%{opacity:.28}94%,100%{opacity:1}}
@keyframes aExStgHero{0%,14%{transform:scale(.34);opacity:0}
  44%,80%{transform:scale(1);opacity:1}94%,100%{transform:scale(.34);opacity:0}}

/* ---------------- secondary-action ----------------
   Le coeur est l'action principale, il passe de 1 a 1.24 puis revient,
   soit 24 pour cent d'amplitude. Le compteur, action secondaire, monte
   de 8 pour cent seulement, un tiers exact, et part 96ms plus tard.
   Les deux barres du bas portent le meme rapport. */
.ex-secondary-action{position:relative;width:166px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-secondary-action .post{display:flex;flex-direction:column;gap:6px}
.ex-secondary-action .img{height:44px;border-radius:8px;background:linear-gradient(135deg,var(--a3),var(--a4))}
.ex-secondary-action .l1{height:6px;width:78%;border-radius:3px;background:var(--s4)}
.ex-secondary-action .l2{height:5px;width:52%;border-radius:3px;background:var(--s3)}
.ex-secondary-action .bar{display:flex;align-items:center;gap:9px}
.ex-secondary-action .hrt{width:18px;height:18px;border-radius:6px 6px 9px 9px;background:var(--danger);
  flex:0 0 auto;animation:aExSaHrt 3.2s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-secondary-action .cnt{position:relative;width:26px;height:14px;flex:0 0 auto;
  font:700 10px/14px var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-2);
  animation:aExSaCnt 3.2s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-secondary-action .cnt u{position:absolute;left:0;top:0;text-decoration:none}
.ex-secondary-action .c1{animation:aExSaA 3.2s steps(1,end) infinite}
.ex-secondary-action .c2{animation:aExSaB 3.2s steps(1,end) infinite}
.ex-secondary-action .ic{width:15px;height:15px;border-radius:5px;background:var(--s3)}
.ex-secondary-action .ic:nth-of-type(2){margin-left:auto}
.ex-secondary-action .amp{display:flex;flex-direction:column;gap:4px;margin-top:auto}
.ex-secondary-action .amp b{display:block;height:4px;border-radius:2px}
.ex-secondary-action .amp .p{width:100%;background:var(--danger)}
.ex-secondary-action .amp .s{width:33.3%;background:var(--ink-3)}
@keyframes aExSaHrt{0%,20%{transform:scale(1)}44%{transform:scale(1.24)}62%,100%{transform:scale(1)}}
@keyframes aExSaCnt{0%,23%{transform:scale(1)}47%{transform:scale(1.08)}65%,100%{transform:scale(1)}}
@keyframes aExSaA{0%,44%{opacity:1}47%,96%{opacity:0}98%,100%{opacity:1}}
@keyframes aExSaB{0%,44%{opacity:0}47%,96%{opacity:1}98%,100%{opacity:0}}

/* ---------------- arc-motion ----------------
   viewBox 0 0 148 96 rendu en 148 sur 96 pixels, une unite vaut donc
   un pixel et le path() du CSS decrit exactement le rail dessine.
   Corde de 18,78 vers 132,30, longueur 124. Sommet de la courbe a
   y = 39, milieu de corde a 54, fleche de 15, soit 12 pour cent. */
.ex-arc-motion{position:relative;width:170px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px}
.ex-arc-motion .cd{position:absolute;left:11px;bottom:11px;width:70px;padding:8px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);display:flex;flex-direction:column;gap:6px}
.ex-arc-motion .img{height:26px;border-radius:6px;background:linear-gradient(135deg,var(--a2),var(--a3))}
.ex-arc-motion .cd .l{height:5px;width:70%;border-radius:3px;background:var(--s4)}
.ex-arc-motion .btn{height:13px;border-radius:5px;background:var(--lime)}
.ex-arc-motion .cart{position:absolute;right:11px;top:11px;display:flex;align-items:flex-start;gap:3px}
.ex-arc-motion .cart .ic{width:24px;height:22px;border-radius:5px;background:var(--s3)}
.ex-arc-motion .bdg{position:relative;width:12px;height:14px;font:700 10px/14px var(--mono);
  color:var(--lime);font-style:normal}
.ex-arc-motion .bdg u{position:absolute;left:0;top:0;text-decoration:none}
.ex-arc-motion .c1{animation:aExArcA 3.4s steps(1,end) infinite}
.ex-arc-motion .c2{animation:aExArcB 3.4s steps(1,end) infinite}
.ex-arc-motion .rail{position:absolute;left:11px;top:11px;width:148px;height:96px;
  fill:none;overflow:visible}
.ex-arc-motion .rail path{fill:none;stroke:var(--s3);stroke-width:1.5;stroke-dasharray:3 4}
.ex-arc-motion .fly{position:absolute;left:11px;top:11px;width:15px;height:15px;border-radius:5px;
  background:var(--a2);offset-path:path("M18 78C44 38 100 22 132 22");offset-rotate:auto;
  animation:aExArcFly 3.4s cubic-bezier(.4,0,.25,1) infinite}
@keyframes aExArcFly{0%,12%{offset-distance:0%;opacity:0}
  18%{offset-distance:6%;opacity:1}
  62%{offset-distance:100%;opacity:1}
  70%{offset-distance:100%;opacity:0}
  70.001%,100%{offset-distance:0%;opacity:0}}
@keyframes aExArcA{0%,62%{opacity:1}64%,96%{opacity:0}98%,100%{opacity:1}}
@keyframes aExArcB{0%,62%{opacity:0}64%,96%{opacity:1}98%,100%{opacity:0}}

/* ---------------- exaggeration ----------------
   Les deux lignes partent au meme instant, sortent par la meme droite
   et disparaissent au meme instant. Seule la route change, la premiere
   glisse a plat, la seconde se souleve de 12px et s'incline de 12
   degres. Le point d'arrivee est identique, ce n'est pas un rebond. */
.ex-exaggeration{width:168px;height:146px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-exaggeration .rw{position:relative;display:flex;align-items:center;gap:8px;height:24px}
.ex-exaggeration .tag{flex:0 0 auto;width:14px;font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-exaggeration .bd{flex:1;height:24px;border-radius:7px;background:var(--s2)}
.ex-exaggeration .st .bd{background:var(--s1)}
.ex-exaggeration .a .bd{background:var(--s3);animation:aExExgLit 3.4s cubic-bezier(.5,0,.75,1) infinite}
.ex-exaggeration .b .bd{background:var(--danger);animation:aExExgAmp 3.4s cubic-bezier(.5,0,.75,1) infinite}
/* La ligne s'eteint la ou elle est sortie PUIS revient a sa place,
   jamais l'inverse, sinon on la voit se teleporter en pleine vue. */
@keyframes aExExgLit{0%{transform:translate(0,0) rotate(0);opacity:0}
  6%{opacity:1}
  20%{transform:translate(0,0) rotate(0)}
  62%,78%{transform:translate(180px,0) rotate(0);opacity:1}
  84%{transform:translate(180px,0) rotate(0);opacity:0}
  84.001%,100%{transform:translate(0,0) rotate(0);opacity:0}}
@keyframes aExExgAmp{0%{transform:translate(0,0) rotate(0);opacity:0}
  6%{opacity:1}
  20%{transform:translate(0,0) rotate(0)}
  62%,78%{transform:translate(180px,-12px) rotate(12deg);opacity:1}
  84%{transform:translate(180px,-12px) rotate(12deg);opacity:0}
  84.001%,100%{transform:translate(0,0) rotate(0);opacity:0}}

/* ---------------- micro-interaction ----------------
   Les quatre parties s'allument dans l'ordre, 240ms d'ecart, pendant
   que le bouton enregistrer fait son travail. mode reste allume apres
   le retour du bouton, parce que c'est l'etat dans lequel le systeme
   a ete laisse, pas un evenement. */
.ex-micro-interaction{width:168px;height:144px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-micro-interaction .art{display:flex;flex-direction:column;gap:6px}
.ex-micro-interaction .art .t{height:9px;width:64%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-micro-interaction .art .l{height:6px;border-radius:3px;background:var(--s3)}
.ex-micro-interaction .art .l.s{width:58%}
.ex-micro-interaction .btn{display:flex;align-items:center;gap:8px;align-self:flex-start;
  padding:7px 11px;border-radius:9px;background:var(--card-2);border:1px solid var(--line-2);
  animation:aExMiBtn 3.2s cubic-bezier(.2,1.3,.3,1) infinite}
.ex-micro-interaction .bm{width:11px;height:14px;border-radius:2px;background:var(--s5);
  animation:aExMiBm 3.2s steps(1,end) infinite}
.ex-micro-interaction .lb{width:34px;height:6px;border-radius:3px;background:var(--s4)}
.ex-micro-interaction .parts{display:flex;gap:5px;margin-top:auto}
.ex-micro-interaction .parts b{display:block;flex:1;padding:5px 0;border-radius:6px;background:var(--s1);
  color:var(--ink-3);font:600 7px/1 var(--mono);text-align:center;
  animation:aExMiPart 3.2s steps(1,end) infinite}
.ex-micro-interaction .p2{animation-delay:.24s}
.ex-micro-interaction .p3{animation-delay:.48s}
.ex-micro-interaction .p4{animation-delay:.72s}
@keyframes aExMiBtn{0%,14%{transform:scale(1)}22%{transform:scale(.94)}34%,100%{transform:scale(1)}}
@keyframes aExMiBm{0%,26%{background:var(--s5)}30%,92%{background:var(--lime)}96%,100%{background:var(--s5)}}
@keyframes aExMiPart{0%,4%{background:var(--s1);color:var(--ink-3)}
  8%,26%{background:var(--lime);color:var(--on-accent)}
  30%,100%{background:var(--s1);color:var(--ink-3)}}

/* ---------------- trigger ----------------
   Les deux panneaux chargent la meme ligne au meme instant, a 52 pour
   cent. En haut un pointeur vient appuyer sur le bouton, en bas la
   sentinelle en pointille sort du cadre par le haut et personne n'a
   rien touche. Cause explicite, cause implicite, effet identique. */
.ex-trigger{width:170px;height:150px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:9px}
.ex-trigger .pn{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden;
  padding:8px 9px;display:flex;flex-direction:column;gap:5px}
.ex-trigger .tg{display:block;font:600 7px/1 var(--mono);font-style:normal}
.ex-trigger .a .tg{color:var(--lime)}
.ex-trigger .b .tg{color:var(--a2)}
.ex-trigger .pn i{height:10px;border-radius:4px;background:var(--s2);flex:0 0 auto}
.ex-trigger .nw{background:var(--lime);animation:aExTgNew 3.6s ease infinite}
.ex-trigger .btn{position:absolute;right:9px;bottom:8px;width:44px;height:13px;border-radius:6px;
  background:var(--s4);animation:aExTgBtn 3.6s ease infinite}
.ex-trigger .cur{position:absolute;right:16px;bottom:2px;animation:aExTgCur 3.6s cubic-bezier(.3,0,.2,1) infinite}
.ex-trigger .sen{position:absolute;left:9px;right:9px;bottom:8px;height:2px;
  background:repeating-linear-gradient(90deg,var(--a2) 0 4px,transparent 4px 8px);
  animation:aExTgSen 3.6s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExTgNew{0%,52%{opacity:0;transform:translateY(-5px)}62%,92%{opacity:1;transform:translateY(0)}
  97%,100%{opacity:0;transform:translateY(-5px)}}
@keyframes aExTgBtn{0%,40%{background:var(--s4)}46%,52%{background:var(--lime)}58%,100%{background:var(--s4)}}
@keyframes aExTgCur{0%,22%{transform:translate(12px,10px);opacity:0}
  32%{transform:translate(0,0);opacity:1}
  44%{transform:translate(0,3px);opacity:1}
  56%{transform:translate(0,0);opacity:1}
  70%{transform:translate(12px,10px);opacity:0}
  70.001%,100%{transform:translate(12px,10px);opacity:0}}
/* la sentinelle s'allume APRES le depart et s'eteint la ou elle est
   sortie, sinon elle apparait d'un coup au raccord de boucle */
@keyframes aExTgSen{0%{transform:translateY(0);opacity:0}
  6%,14%{transform:translateY(0);opacity:1}
  46%,52%{transform:translateY(-42px);opacity:1}
  60%{transform:translateY(-42px);opacity:0}
  60.001%,100%{transform:translateY(0);opacity:0}}

/* ---------------- signifier ----------------
   L'action est glissable sur les quatre lignes, l'affordance est la
   meme partout. Une seule la signale, en laissant depasser 12px de
   sa zone d'action. Rien d'autre ne change. */
.ex-signifier{width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-signifier .rw{position:relative;display:flex;align-items:center;gap:9px;height:26px;
  padding:0 10px;border-radius:8px;background:var(--card-2);border:1px solid var(--line-2);overflow:hidden}
.ex-signifier .av{width:16px;height:16px;border-radius:50%;background:var(--s3);flex:0 0 auto}
.ex-signifier .l{flex:1;height:6px;border-radius:3px;background:var(--s4)}
.ex-signifier .act{position:absolute;right:0;top:0;bottom:0;width:40px;background:var(--danger);
  display:grid;place-items:center;animation:aExSgAct 3s cubic-bezier(.3,.9,.3,1) infinite}
.ex-signifier .act b{width:14px;height:2px;border-radius:1px;background:var(--void)}
.ex-signifier .hot .av,.ex-signifier .hot .l{animation:aExSgRow 3s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExSgAct{0%,30%{transform:translateX(28px)}52%{transform:translateX(0)}72%,100%{transform:translateX(28px)}}
@keyframes aExSgRow{0%,30%{transform:translateX(0)}52%{transform:translateX(-28px)}72%,100%{transform:translateX(0)}}

/* ---------------- change-blindness ----------------
   Le cercle en pointille marque le point de fixation, pose sur le
   formulaire. Le total change dans le coin oppose et rien ne l'annonce
   pendant les deux tiers du cycle. Le petit sursaut n'arrive qu'a la
   fin, et c'est la seule fois ou l'oeil peut l'attraper. */
.ex-change-blindness{position:relative;width:168px;height:132px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-change-blindness .hd{display:flex;align-items:center;gap:8px}
.ex-change-blindness .hd .t{height:8px;width:54px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-change-blindness .tot{position:relative;width:32px;height:16px;margin-left:auto;
  font:700 12px/16px var(--mono);font-variant-numeric:tabular-nums;color:var(--lime);text-align:right}
.ex-change-blindness .tot u{position:absolute;right:0;top:0;text-decoration:none}
.ex-change-blindness .v1{animation:aExCbA 4.4s steps(1,end) infinite}
.ex-change-blindness .v2{animation:aExCbB 4.4s steps(1,end) infinite}
.ex-change-blindness .frm{display:flex;flex-direction:column;gap:8px}
.ex-change-blindness .fl{height:20px;border-radius:6px;background:var(--s1);border:1px solid var(--line)}
.ex-change-blindness .fl.s{width:62%}
.ex-change-blindness .foc{position:absolute;left:22px;top:64px;width:22px;height:22px;border-radius:50%;
  border:1.5px dashed var(--ink-3);box-sizing:border-box}
@keyframes aExCbA{0%,46%{opacity:1}48%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExCbB{0%,46%{opacity:0}48%,94%{opacity:1}96%,100%{opacity:0}}

/* ---------------- object-constancy ----------------
   La vignette devient l'en tete de la fiche sans jamais changer de
   couleur ni de rayon. Translation de 46px et 26px, plus un
   agrandissement, en une seule piece, donc l'oeil suit un objet et
   pas deux. */
.ex-object-constancy{position:relative;width:168px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px}
.ex-object-constancy .grd{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ex-object-constancy .grd i{height:34px;border-radius:7px;background:var(--s2)}
.ex-object-constancy .det{position:absolute;left:11px;right:11px;top:96px;display:flex;
  flex-direction:column;gap:6px;animation:aExOcDet 4s ease infinite}
.ex-object-constancy .det .l{height:6px;border-radius:3px;background:var(--s4)}
.ex-object-constancy .det .l.s{width:62%}
.ex-object-constancy .mv{position:absolute;left:11px;top:11px;width:69px;height:34px;border-radius:7px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));transform-origin:top left;
  animation:aExOcMv 4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExOcMv{0%,14%{transform:translate(0,0) scale(1,1)}
  50%,86%{transform:translate(0,0) scale(2.13,2.15)}98%,100%{transform:translate(0,0) scale(1,1)}}
@keyframes aExOcDet{0%,20%{opacity:0}52%,86%{opacity:1}96%,100%{opacity:0}}

/* ---------------- common-fate ----------------
   Les trois lignes cochees partent ensemble, meme direction, meme
   duree, meme courbe, et se lisent comme un seul bloc qui tombe dans
   la corbeille. Les deux autres ne bougent pas d'un pixel. */
.ex-common-fate{position:relative;width:168px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:7px}
.ex-common-fate .rw{display:flex;align-items:center;gap:9px;height:20px;padding:0 9px;
  border-radius:7px;background:var(--s1)}
.ex-common-fate .on{background:rgba(var(--accent-rgb),.14);
  animation:aExCfRow 3.6s cubic-bezier(.4,0,.2,1) infinite}
.ex-common-fate .ck{width:11px;height:11px;border-radius:3px;background:var(--s4);flex:0 0 auto}
.ex-common-fate .on .ck{background:var(--lime)}
.ex-common-fate .l{flex:1;height:5px;border-radius:3px;background:var(--s4)}
.ex-common-fate .bin{position:absolute;right:11px;bottom:11px;width:24px;height:22px;border-radius:5px;
  background:var(--s3)}
@keyframes aExCfRow{0%{transform:translate(0,0);opacity:0}
  8%,20%{transform:translate(0,0);opacity:1}
  58%{transform:translate(58px,52px) scale(.72);opacity:1}
  66%{transform:translate(58px,52px) scale(.72);opacity:0}
  66.001%,100%{transform:translate(0,0);opacity:0}}

/* ---------------- apparent-motion ----------------
   Douze segments, un seul cycle de 1.08s, chacun retarde de 90ms.
   Un intervalle sous les 100ms, donc les douze allumages fusionnent
   en une rotation. Aucun element ne tourne, il n'y a que des segments
   qui s'allument dans l'ordre. */
.ex-apparent-motion{position:relative;width:166px;height:130px;border-radius:11px;overflow:hidden;
  background:var(--s0);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px}
.ex-apparent-motion .sp{position:relative;display:block;width:46px;height:46px}
.ex-apparent-motion .sp i{position:absolute;left:50%;top:50%;width:4px;height:13px;margin:-23px 0 0 -2px;
  border-radius:2px;background:var(--lime);transform-origin:center 23px;opacity:.16;
  animation:aExAmSeg 1.08s linear infinite}
.ex-apparent-motion .sp i:nth-child(1){transform:rotate(0)}
.ex-apparent-motion .sp i:nth-child(2){transform:rotate(30deg);animation-delay:.09s}
.ex-apparent-motion .sp i:nth-child(3){transform:rotate(60deg);animation-delay:.18s}
.ex-apparent-motion .sp i:nth-child(4){transform:rotate(90deg);animation-delay:.27s}
.ex-apparent-motion .sp i:nth-child(5){transform:rotate(120deg);animation-delay:.36s}
.ex-apparent-motion .sp i:nth-child(6){transform:rotate(150deg);animation-delay:.45s}
.ex-apparent-motion .sp i:nth-child(7){transform:rotate(180deg);animation-delay:.54s}
.ex-apparent-motion .sp i:nth-child(8){transform:rotate(210deg);animation-delay:.63s}
.ex-apparent-motion .sp i:nth-child(9){transform:rotate(240deg);animation-delay:.72s}
.ex-apparent-motion .sp i:nth-child(10){transform:rotate(270deg);animation-delay:.81s}
.ex-apparent-motion .sp i:nth-child(11){transform:rotate(300deg);animation-delay:.9s}
.ex-apparent-motion .sp i:nth-child(12){transform:rotate(330deg);animation-delay:.99s}
.ex-apparent-motion .cap{display:flex;flex-direction:column;align-items:center;gap:6px}
.ex-apparent-motion .cap b{height:6px;width:74px;border-radius:3px;background:var(--s3)}
.ex-apparent-motion .cap .s{width:46px}
/* le segment s'allume vers la fin du tour, pas a zero, sinon le
   raccord de boucle est lui meme un allumage supplementaire */
@keyframes aExAmSeg{0%,66%{opacity:.16}70%{opacity:1}100%{opacity:.16}}

/* ---------------- saccadic-masking ----------------
   Le regard saute vers l'ancre du bas a 40 pour cent. Le flash blanc
   qui couvre la page dure exactement le temps de ce saut, et c'est
   pendant ce flash que le titre du bas change. Vision coupee, donc
   changement jamais vu. */
.ex-saccadic-masking{position:relative;width:168px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px}
.ex-saccadic-masking .doc{display:flex;flex-direction:column;gap:7px}
.ex-saccadic-masking .doc i{height:8px;border-radius:4px;background:var(--s2)}
.ex-saccadic-masking .doc .h{height:12px;width:58%;background:var(--s4)}
.ex-saccadic-masking .doc .h2{height:12px;width:48%;background:var(--s4);
  animation:aExSmH 3.6s steps(1,end) infinite}
.ex-saccadic-masking .eye{position:absolute;right:12px;top:20px;width:18px;height:18px;border-radius:50%;
  border:1.5px dashed var(--ink-2);box-sizing:border-box;animation:aExSmEye 3.6s steps(1,end) infinite}
.ex-saccadic-masking .flash{position:absolute;inset:0;background:rgba(var(--ink-rgb),.9);
  animation:aExSmFl 3.6s steps(1,end) infinite}
/* le regard atterrit sur le second titre, centre a y 66, depuis son
   propre centre a y 29, soit 37px et pas 74 */
@keyframes aExSmEye{0%,38%{transform:translate(0,0)}40%,88%{transform:translate(0,37px)}
  90%,100%{transform:translate(0,0)}}
@keyframes aExSmFl{0%,38%{opacity:0}40%,45%{opacity:.85}47%,88%{opacity:0}
  90%,95%{opacity:.85}97%,100%{opacity:0}}
@keyframes aExSmH{0%,41%{background:var(--s4)}43%,89%{background:var(--lime)}91%,100%{background:var(--s4)}}

/* ---------------- peripheral-motion ----------------
   Le caret clignote au centre, c'est la que la tache se joue. La
   pastille du coin pulse trois fois puis se tait pour de bon, et un
   point statique garde l'information. Trois cycles, comme le dit la
   consigne, pas une boucle sans fin. */
.ex-peripheral-motion{position:relative;width:168px;height:132px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:12px}
.ex-peripheral-motion .ed{position:relative;display:flex;flex-direction:column;gap:9px;width:104px;
  margin-top:26px}
.ex-peripheral-motion .ln{height:8px;border-radius:4px;background:var(--s3)}
.ex-peripheral-motion .ln.s{width:70%}
.ex-peripheral-motion .ln.s2{width:44%}
.ex-peripheral-motion .cr{position:absolute;left:48px;bottom:0;width:2px;height:8px;background:var(--lime);
  animation:aExPmCr .9s steps(1,end) infinite}
.ex-peripheral-motion .bdg{position:absolute;right:10px;top:10px;width:18px;height:18px;border-radius:9px;
  background:var(--lime);animation:aExPmBdg 4.8s ease-in-out infinite}
.ex-peripheral-motion .dot{position:absolute;right:15px;top:15px;width:8px;height:8px;border-radius:50%;
  background:var(--lime);opacity:.45}
@keyframes aExPmCr{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExPmBdg{0%{transform:scale(.6);opacity:0}
  4%{transform:scale(1);opacity:.9}
  11%{transform:scale(.6);opacity:0}
  15%{transform:scale(1);opacity:.9}
  22%{transform:scale(.6);opacity:0}
  26%{transform:scale(1);opacity:.9}
  33%,100%{transform:scale(.6);opacity:0}}

/* ---------------- attention-capture ----------------
   Premiere moitie du cycle, trois cartes s'allument et le regard fait
   la navette entre elles sans rien lire. Seconde moitie, une seule
   s'allume et il s'y pose. Grille de 146px utiles en deux colonnes de
   68.5, centres a 45 et 122 en x, 48 et 97 en y, donc le regard de 18
   se cale a 36 et 113, 39 et 88. */
.ex-attention-capture{position:relative;width:168px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-attention-capture .hd .t{height:8px;width:52px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-attention-capture .gri{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.ex-attention-capture .w{height:40px;border-radius:8px;background:var(--s2)}
.ex-attention-capture .w1{animation:aExAcW1 5.2s ease-in-out infinite}
.ex-attention-capture .w2{animation:aExAcW2 5.2s ease-in-out infinite}
.ex-attention-capture .w3{animation:aExAcW3 5.2s ease-in-out infinite}
.ex-attention-capture .gaze{position:absolute;left:0;top:0;width:18px;height:18px;border-radius:50%;
  border:1.5px dashed var(--ink-2);box-sizing:border-box;animation:aExAcG 5.2s steps(1,end) infinite}
@keyframes aExAcW1{0%,6%{transform:scale(1);background:var(--s2)}
  14%,20%{transform:scale(1.04);background:var(--lime)}
  28%,52%{transform:scale(1);background:var(--s2)}
  60%,72%{transform:scale(1.04);background:var(--lime)}
  82%,100%{transform:scale(1);background:var(--s2)}}
@keyframes aExAcW2{0%,10%{transform:scale(1);background:var(--s2)}
  18%,24%{transform:scale(1.04);background:var(--a2)}
  32%,100%{transform:scale(1);background:var(--s2)}}
@keyframes aExAcW3{0%,14%{transform:scale(1);background:var(--s2)}
  22%,28%{transform:scale(1.04);background:var(--a4)}
  36%,100%{transform:scale(1);background:var(--s2)}}
@keyframes aExAcG{0%,12%{transform:translate(36px,39px)}
  16%,22%{transform:translate(113px,39px)}
  26%,32%{transform:translate(36px,88px)}
  36%,46%{transform:translate(113px,39px)}
  50%,100%{transform:translate(36px,39px)}}

/* ---------------- cognitive-load ----------------
   A gauche une carte quitte sa place et va combler le trou en
   pointille, les trois autres ne bougent pas. Une chose a suivre.
   A droite les quatre partent chacune ailleurs, quatre choses a
   suivre pour la meme reorganisation et la meme duree. */
.ex-cognitive-load{display:flex;gap:14px;width:170px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px}
.ex-cognitive-load .col{flex:1;display:flex;flex-direction:column;gap:7px}
.ex-cognitive-load .tg{display:block;font:700 9px/1 var(--mono);color:var(--ink-3);font-style:normal;
  margin-bottom:2px}
.ex-cognitive-load .col i{display:block;height:22px;border-radius:6px;background:var(--s2)}
.ex-cognitive-load .hole{background:none;border:1.5px dashed var(--line-2);box-sizing:border-box}
.ex-cognitive-load .mv{background:var(--lime);animation:aExClOne 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-cognitive-load .m1,.ex-cognitive-load .m2,.ex-cognitive-load .m3,.ex-cognitive-load .m4{background:var(--lime)}
.ex-cognitive-load .m1{animation:aExClA 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-cognitive-load .m2{animation:aExClB 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-cognitive-load .m3{animation:aExClC 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-cognitive-load .m4{animation:aExClD 3.6s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExClOne{0%,16%{transform:translateY(0)}52%,88%{transform:translateY(58px)}100%{transform:translateY(0)}}
@keyframes aExClA{0%,16%{transform:translateY(0)}52%,88%{transform:translateY(29px)}100%{transform:translateY(0)}}
@keyframes aExClB{0%,16%{transform:translateY(0)}52%,88%{transform:translateY(58px)}100%{transform:translateY(0)}}
@keyframes aExClC{0%,16%{transform:translateY(0)}52%,88%{transform:translateY(-58px)}100%{transform:translateY(0)}}
@keyframes aExClD{0%,16%{transform:translateY(0)}52%,88%{transform:translateY(-29px)}100%{transform:translateY(0)}}

/* ---------------- doherty ----------------
   La jauge du bas est un axe de temps, 146px pour 800ms, une
   milliseconde vaut donc 0.1825px et le repere des 400ms tombe a 73px,
   pile au milieu. Le remplissage s'arrete a 44 pour cent, soit 64px,
   soit 350ms, avant le repere. */
.ex-doherty{position:relative;width:168px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-doherty .fld{position:relative;display:flex;align-items:center;gap:3px;height:26px;padding:0 10px;
  border-radius:8px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-doherty .q{height:6px;width:64px;border-radius:3px;background:var(--s5)}
.ex-doherty .cr{width:2px;height:12px;background:var(--lime);animation:aExDoCr .9s steps(1,end) infinite}
.ex-doherty .res{display:flex;flex-direction:column;gap:7px}
.ex-doherty .res i{display:block;height:18px;border-radius:6px;background:var(--s2);
  animation:aExDoRes 3.2s cubic-bezier(.2,.9,.25,1) infinite}
.ex-doherty .res i:nth-child(2){animation-delay:.05s;width:82%}
.ex-doherty .res i:nth-child(3){animation-delay:.1s;width:64%}
.ex-doherty .ms{position:relative;height:5px;border-radius:3px;background:var(--s2);margin-top:auto}
.ex-doherty .fill{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:3px;background:var(--lime);
  animation:aExDoMs 3.2s linear infinite}
.ex-doherty .mk{position:absolute;left:73px;top:-5px;bottom:-5px;width:2px;margin-left:-1px;
  background:repeating-linear-gradient(180deg,var(--ink-2) 0 3px,transparent 3px 6px)}
@keyframes aExDoCr{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExDoRes{0%,20%{transform:translateY(7px);opacity:0}
  46%,88%{transform:translateY(0);opacity:1}96%,100%{transform:translateY(7px);opacity:0}}
@keyframes aExDoMs{0%,10%{width:0}46%,88%{width:44%}96%,100%{width:0}}

/* ---------------- vestibular-trigger ----------------
   Trois plans qui defilent a trois vitesses differentes sur toute la
   fenetre, c'est le cas d'ecole. Le trait pointille marque le tiers
   autorise, la zone en mouvement en couvre les deux tiers, et les
   directions ne sont pas les memes d'un plan a l'autre. */
.ex-vestibular-trigger{width:170px;height:138px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:10px;display:flex;flex-direction:column;gap:9px}
.ex-vestibular-trigger .vp{position:relative;flex:1;border-radius:9px;overflow:hidden;background:var(--s1)}
.ex-vestibular-trigger .sky,.ex-vestibular-trigger .mid,.ex-vestibular-trigger .fore{
  position:absolute;left:-30%;right:-30%}
.ex-vestibular-trigger .sky{top:0;height:40%;background:linear-gradient(90deg,var(--a3),var(--a2),var(--a3));
  animation:aExVesA 5s linear infinite}
.ex-vestibular-trigger .mid{top:34%;height:34%;background:linear-gradient(90deg,var(--lime),var(--accent-2),var(--lime));
  animation:aExVesB 3.4s linear infinite}
.ex-vestibular-trigger .fore{top:62%;bottom:0;background:linear-gradient(90deg,var(--a4),var(--a3),var(--a4));
  animation:aExVesA 2.2s linear infinite}
.ex-vestibular-trigger .third{position:absolute;left:0;right:0;top:33.33%;height:1px;z-index:2;
  background:repeating-linear-gradient(90deg,var(--void) 0 4px,transparent 4px 8px)}
.ex-vestibular-trigger .warn{position:absolute;right:7px;bottom:5px;z-index:2;
  font:700 10px/1 var(--mono);color:var(--void);font-style:normal}
.ex-vestibular-trigger .ftr{display:flex;flex-direction:column;gap:6px}
.ex-vestibular-trigger .ftr .l{height:7px;border-radius:4px;background:var(--s3)}
.ex-vestibular-trigger .ftr .l.s{width:58%}
@keyframes aExVesA{0%,100%{transform:translateX(0)}50%{transform:translateX(-24px)}}
@keyframes aExVesB{0%,100%{transform:translateX(-24px)}50%{transform:translateX(0)}}

/* ---------------- pause-stop-hide ----------------
   Le carrousel avance tout seul. La jauge mesure la duree du mouvement
   et le trait rouge est a cinq secondes, soit 62.5 pour cent d'un
   cycle de 8s. Le bouton pause s'allume apres ce trait, quand le
   critere devient obligatoire et non plus recommande. */
.ex-pause-stop-hide{width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-pause-stop-hide .vp{position:relative;height:64px;border-radius:9px;overflow:hidden;background:var(--s1)}
.ex-pause-stop-hide .rail{position:absolute;left:0;top:0;display:flex;gap:8px;padding:8px;
  animation:aExPsRail 8s cubic-bezier(.5,0,.5,1) infinite}
.ex-pause-stop-hide .rail i{flex:0 0 auto;width:64px;height:48px;border-radius:7px;background:var(--s3)}
.ex-pause-stop-hide .rail .on{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-pause-stop-hide .dots{display:flex;gap:6px;justify-content:center}
.ex-pause-stop-hide .dots b{width:5px;height:5px;border-radius:50%;background:var(--s3)}
.ex-pause-stop-hide .dots .on{background:var(--lime)}
.ex-pause-stop-hide .ft{display:flex;align-items:center;gap:10px;margin-top:auto}
.ex-pause-stop-hide .pz{position:relative;flex:0 0 auto;width:16px;height:16px;border-radius:4px;
  border:1.5px solid var(--ink-3);box-sizing:border-box;animation:aExPsPz 8s steps(1,end) infinite}
.ex-pause-stop-hide .pz::after{content:"";position:absolute;left:3.5px;top:3px;width:8px;height:7px;
  box-sizing:border-box;border-left:2.5px solid var(--ink-2);border-right:2.5px solid var(--ink-2)}
.ex-pause-stop-hide .dur{position:relative;flex:1;height:5px;border-radius:3px;background:var(--s2)}
.ex-pause-stop-hide .fill{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:3px;
  background:var(--ink-3);animation:aExPsD 8s linear infinite}
.ex-pause-stop-hide .five{position:absolute;left:62.5%;top:-5px;bottom:-5px;width:2px;margin-left:-1px;
  background:var(--danger)}
@keyframes aExPsRail{0%,10%{transform:translateX(0)}
  30%,40%{transform:translateX(-72px)}
  60%,78%{transform:translateX(-144px)}
  94%,100%{transform:translateX(0)}}
@keyframes aExPsPz{0%,64%{border-color:var(--ink-3)}66%,78%{border-color:var(--lime)}80%,100%{border-color:var(--ink-3)}}
@keyframes aExPsD{0%{width:0}96%{width:100%}96.001%,100%{width:0}}

/* ---------------- flash-threshold ----------------
   Cette scene ne clignote pas non plus. Le danger vient ici des
   donnees, pas du design, un flux temps reel qui redessine sa barre
   sept fois par seconde produit un clignotement que personne n'a
   dessine. Les barres sont statiques et l'alerte est ce qui bouge,
   doucement. C'est la seule maniere honnete de montrer ce terme. */
.ex-flash-threshold{width:170px;height:134px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-flash-threshold .hd{display:flex;align-items:center;gap:8px}
.ex-flash-threshold .hd .t{height:8px;width:56px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-flash-threshold .rate{margin-left:auto;font:700 10px/1 var(--mono);color:var(--danger);font-style:normal}
.ex-flash-threshold .chart{display:flex;align-items:flex-end;gap:4px;height:52px}
.ex-flash-threshold .chart i{flex:1;border-radius:3px 3px 0 0;background:var(--danger);height:40%}
.ex-flash-threshold .chart i:nth-child(2n){height:92%}
.ex-flash-threshold .chart i:nth-child(3n){height:26%}
.ex-flash-threshold .chart i:nth-child(5n){height:74%}
.ex-flash-threshold .warn{display:flex;align-items:center;gap:8px;margin-top:auto;padding:7px 9px;
  border-radius:8px;background:rgba(var(--danger-rgb),.14);border:1px solid rgba(var(--danger-rgb),.4);
  animation:aExFtWarn 3.2s ease-in-out infinite}
.ex-flash-threshold .warn .ic{width:12px;height:12px;border-radius:50%;background:var(--danger);flex:0 0 auto}
.ex-flash-threshold .warn .l{flex:1;height:6px;border-radius:3px;background:var(--danger);opacity:.6}
@keyframes aExFtWarn{0%,10%{opacity:.55}50%,60%{opacity:1}96%,100%{opacity:.55}}

/* ---------------- wcag-animation ----------------
   Deux interrupteurs, celui du systeme et celui de l'application. Le
   second existe parce que beaucoup d'utilisateurs ne trouvent jamais
   le premier, et que certains appareils ne l'exposent pas. Les cartes
   du bas suivent le second, qui bascule a la moitie du cycle. */
.ex-wcag-animation{width:168px;height:138px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-wcag-animation .row{display:flex;align-items:center;gap:10px;padding:7px 9px;border-radius:8px;
  background:var(--card-2);border:1px solid var(--line-2)}
.ex-wcag-animation .row .l{flex:1;height:6px;border-radius:3px;background:var(--s5)}
.ex-wcag-animation .row .l.s{width:62%;flex:0 0 auto}
.ex-wcag-animation .sw{position:relative;flex:0 0 auto;width:28px;height:15px;border-radius:8px;
  background:var(--s3);margin-left:auto}
.ex-wcag-animation .sw i{position:absolute;top:2px;left:2px;width:11px;height:11px;border-radius:50%;
  background:var(--ink)}
.ex-wcag-animation .sw.a{background:var(--s3);opacity:.5}
.ex-wcag-animation .sw.b{animation:aExWaSw 5s steps(1,end) infinite}
.ex-wcag-animation .sw.b i{animation:aExWaKn 5s steps(1,end) infinite}
.ex-wcag-animation .vp{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden;padding:9px}
.ex-wcag-animation .cd{display:block;height:14px;border-radius:6px;background:var(--lime);margin-bottom:7px;
  animation:aExWaCd 5s cubic-bezier(.2,.9,.25,1) infinite}
.ex-wcag-animation .d2{animation-delay:.1s;width:66%}
@keyframes aExWaSw{0%,48%{background:var(--s3)}50%,98%{background:var(--lime)}99%,100%{background:var(--s3)}}
@keyframes aExWaKn{0%,48%{transform:translateX(0)}50%,98%{transform:translateX(13px)}99%,100%{transform:translateX(0)}}
@keyframes aExWaCd{0%,8%{transform:translateY(14px);opacity:0}
  22%,44%{transform:translateY(0);opacity:1}
  47%{transform:translateY(14px);opacity:0}
  50%,58%{transform:translateY(0);opacity:0}
  64%,94%{transform:translateY(0);opacity:1}
  97%{transform:translateY(0);opacity:0}
  100%{transform:translateY(14px);opacity:0}}

/* ---------------- reduced-transparency ----------------
   Meme photo, meme barre laterale, meme texte. En haut un
   backdrop-filter, en bas un aplat opaque. Rien ne s'anime sur la
   propriete elle meme, une requete media est un etat binaire, pas une
   transition. Seule la photo derriere glisse, pour qu'on voie le flou
   la suivre en haut et rester indifferent en bas. */
.ex-reduced-transparency{width:168px;height:142px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-reduced-transparency .app{position:relative;flex:1;border-radius:9px;overflow:hidden}
.ex-reduced-transparency .ph{position:absolute;left:-14%;right:-14%;top:0;bottom:0;
  background:linear-gradient(115deg,var(--lime),var(--a3),var(--a4),var(--lime));
  animation:aExRtPh 6s ease-in-out infinite}
.ex-reduced-transparency .sb{position:absolute;left:0;top:0;bottom:0;width:58px;padding:9px 8px;
  display:flex;flex-direction:column;gap:7px}
.ex-reduced-transparency .sb b{display:block;height:5px;border-radius:3px;background:var(--void)}
.ex-reduced-transparency .sb .s{width:62%}
.ex-reduced-transparency .blur{background:rgba(var(--ink-rgb),.16);
  border-right:1px solid rgba(var(--ink-rgb),.3);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.ex-reduced-transparency .solid{background:var(--card);border-right:1px solid var(--line-2)}
.ex-reduced-transparency .solid b{background:var(--ink)}
@keyframes aExRtPh{0%,100%{transform:translateX(0)}50%{transform:translateX(-24px)}}

/* ---------------- blast-radius ----------------
   Trois groupes, trois comportements. Le theme bascule et rien ne se
   passe de plus. Le champ de facturation fait monter la barre. La
   zone de danger n'a ni l'un ni l'autre, elle est deja ailleurs.
   Meme cycle de 4.6s pour les trois, seule la reponse change. */
.ex-blast-radius{position:relative;width:170px;height:152px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-blast-radius .grp{display:flex;flex-direction:column;gap:5px}
.ex-blast-radius .ttl{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-blast-radius .red .ttl{color:var(--danger)}
.ex-blast-radius .rw{display:flex;align-items:center;gap:9px;height:22px;padding:0 9px;
  border-radius:7px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-blast-radius .red .rw{border-color:rgba(var(--danger-rgb),.5);background:rgba(var(--danger-rgb),.08)}
.ex-blast-radius .l{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-blast-radius .l.s{width:56%;flex:0 0 auto}
.ex-blast-radius .tg{position:relative;flex:0 0 auto;width:26px;height:14px;border-radius:8px;
  background:var(--s3);animation:aExBrTg 4.6s ease infinite}
.ex-blast-radius .tg i{position:absolute;top:2px;left:2px;width:10px;height:10px;border-radius:50%;
  background:var(--ink);animation:aExBrKn 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-blast-radius .fld{flex:0 0 auto;width:40px;height:15px;border-radius:5px;box-sizing:border-box;
  background:var(--s1);border:1px solid var(--line-2);animation:aExBrFld 4.6s ease infinite}
.ex-blast-radius .del{flex:0 0 auto;width:28px;height:14px;border-radius:5px;background:var(--danger)}
.ex-blast-radius .savebar{position:absolute;left:11px;right:11px;bottom:11px;height:24px;border-radius:8px;
  background:var(--s0);border:1px solid var(--lime);box-sizing:border-box;
  display:flex;align-items:center;gap:7px;padding:0 8px;
  animation:aExBrBar 4.6s cubic-bezier(.2,1.2,.3,1) infinite}
.ex-blast-radius .savebar .dt{width:6px;height:6px;border-radius:50%;background:var(--lime)}
.ex-blast-radius .savebar b{display:block;height:13px;border-radius:4px}
.ex-blast-radius .savebar .c{width:24px;background:var(--s4);margin-left:auto}
.ex-blast-radius .savebar .s{width:28px;background:var(--lime)}
@keyframes aExBrTg{0%,12%{background:var(--s3)}22%,66%{background:var(--lime)}78%,100%{background:var(--s3)}}
@keyframes aExBrKn{0%,12%{transform:translateX(0)}22%,66%{transform:translateX(12px)}78%,100%{transform:translateX(0)}}
@keyframes aExBrFld{0%,30%{border-color:var(--line-2)}38%,64%{border-color:var(--lime)}72%,100%{border-color:var(--line-2)}}
@keyframes aExBrBar{0%,34%{transform:translateY(34px);opacity:0}44%,66%{transform:translateY(0);opacity:1}
  76%,100%{transform:translateY(34px);opacity:0}}

/* ---------------- save-bar ----------------
   Le premier champ est modifie a 14 pour cent, la barre monte a 28,
   le bouton annuler s'allume a 58 et la valeur revient a 66. La barre
   ne part qu'apres, a 78. L'ordre est le sujet, annuler d'abord,
   restitution ensuite, disparition en dernier. */
.ex-save-bar{position:relative;width:168px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-save-bar .hd .t{height:8px;width:52px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-save-bar .fld{position:relative;display:block;height:30px;border-radius:8px;background:var(--s1);
  border:1px solid var(--line-2);box-sizing:border-box}
.ex-save-bar .fld:nth-of-type(1){animation:aExSbFld 4.6s ease infinite}
.ex-save-bar .lb{position:absolute;left:9px;top:6px;width:30px;height:4px;border-radius:2px;background:var(--ink-3)}
.ex-save-bar .lb.s{width:22px}
.ex-save-bar .val{position:absolute;left:9px;top:16px;width:54px;height:6px;border-radius:3px;
  background:var(--s5);transform-origin:left center;animation:aExSbVal 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-save-bar .v2{width:36px;animation:none}
.ex-save-bar .bar2{position:absolute;left:11px;right:11px;bottom:11px;height:28px;border-radius:9px;
  background:var(--card-2);border:1px solid var(--line-2);box-sizing:border-box;
  display:flex;align-items:center;gap:7px;padding:0 9px;
  animation:aExSbBar 4.6s cubic-bezier(.2,1.2,.3,1) infinite}
.ex-save-bar .bar2 .dot{width:6px;height:6px;border-radius:50%;background:var(--lime)}
.ex-save-bar .btn{display:block;height:15px;border-radius:5px}
.ex-save-bar .cancel{width:28px;background:var(--s4);margin-left:auto;animation:aExSbCx 4.6s steps(1,end) infinite}
.ex-save-bar .save{width:32px;background:var(--lime)}
@keyframes aExSbVal{0%,14%{transform:scaleX(1)}24%,60%{transform:scaleX(1.6)}66%,100%{transform:scaleX(1)}}
@keyframes aExSbFld{0%,14%{border-color:var(--line-2)}24%,60%{border-color:var(--lime)}70%,100%{border-color:var(--line-2)}}
@keyframes aExSbBar{0%,18%{transform:translateY(40px);opacity:0}28%,68%{transform:translateY(0);opacity:1}
  78%,100%{transform:translateY(40px);opacity:0}}
@keyframes aExSbCx{0%,56%{background:var(--s4)}58%,64%{background:var(--ink-2)}66%,100%{background:var(--s4)}}

/* ---------------- override-dot ----------------
   Les points sont alignes sur la meme verticale, les lignes modifiees
   forment donc une colonne lisible d'un coup d'oeil. Le curseur
   appuie sur le bouton de la deuxieme ligne a 42 pour cent et seule
   cette ligne revient a son defaut, les autres ne bougent pas. */
.ex-override-dot{position:relative;width:170px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-override-dot .rw{display:flex;align-items:center;gap:9px;height:30px;padding:0 9px;
  border-radius:7px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-override-dot .dot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;background:var(--s1)}
.ex-override-dot .on .dot{background:var(--lime)}
.ex-override-dot .tx{flex:1;display:flex;flex-direction:column;gap:5px}
.ex-override-dot .tx b{display:block;height:5px;border-radius:3px;background:var(--s5)}
.ex-override-dot .tx .s{width:58%;background:var(--s3)}
.ex-override-dot .rst{flex:0 0 auto;width:15px;height:15px;border-radius:5px;box-sizing:border-box;
  border:1.5px solid var(--lime);opacity:0}
.ex-override-dot .on .rst{opacity:1}
.ex-override-dot .r2 .dot,.ex-override-dot .r2 .rst{animation:aExOdOff 4.2s ease infinite}
.ex-override-dot .cur{position:absolute;right:11px;top:60px;
  animation:aExOdCur 4.2s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExOdOff{0%,44%{opacity:1;transform:scale(1)}56%,92%{opacity:0;transform:scale(.4)}
  98%,100%{opacity:1;transform:scale(1)}}
@keyframes aExOdCur{0%,20%{transform:translate(12px,10px);opacity:0}
  30%{transform:translate(0,0);opacity:1}
  42%{transform:translate(0,3px);opacity:1}
  54%{transform:translate(0,0);opacity:1}
  70%{transform:translate(12px,10px);opacity:0}
  70.001%,100%{transform:translate(12px,10px);opacity:0}}

/* ---------------- settings-search ----------------
   Le chemin d'acces est au dessus de chaque resultat et il est complet,
   trois niveaux. C'est lui qui apprend ou vit le reglage, pas le
   resultat lui meme. La requete se tape d'abord, les resultats
   n'arrivent qu'a 44 pour cent. */
.ex-settings-search{width:170px;height:148px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-settings-search .fld{display:flex;align-items:center;gap:5px;height:28px;padding:0 9px;
  box-sizing:border-box;border-radius:8px;background:var(--s1);border:1px solid var(--lime)}
.ex-settings-search .ic{flex:0 0 auto;width:9px;height:9px;border-radius:50%;
  border:1.5px solid var(--ink-3);box-sizing:border-box}
.ex-settings-search .q{display:block;height:6px;border-radius:3px;background:var(--s5);width:0;
  animation:aExSsQ 4s steps(9,end) infinite}
.ex-settings-search .caret{display:block;flex:0 0 auto;width:1.5px;height:14px;background:var(--lime);
  animation:aExSsCr .9s steps(1,end) infinite}
.ex-settings-search .res{display:flex;flex-direction:column;gap:5px;animation:aExSsRes 4s ease infinite}
.ex-settings-search .r2{animation-delay:.14s;opacity:.55}
.ex-settings-search .pth{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-settings-search .res .rw{display:flex;align-items:center;gap:9px;height:26px;padding:0 9px;
  border-radius:7px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-settings-search .hit{flex:1;height:6px;border-radius:3px;background:var(--lime)}
.ex-settings-search .res .l{flex:1;height:6px;border-radius:3px;background:var(--s5)}
.ex-settings-search .tg{position:relative;flex:0 0 auto;width:24px;height:13px;border-radius:7px;
  background:var(--s3)}
.ex-settings-search .tg i{position:absolute;top:2px;left:2px;width:9px;height:9px;border-radius:50%;
  background:var(--ink)}
@keyframes aExSsQ{0%{width:0}34%,86%{width:54px}96%,100%{width:0}}
@keyframes aExSsCr{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExSsRes{0%,30%{opacity:0;transform:translateY(6px)}44%,86%{opacity:1;transform:translateY(0)}
  96%,100%{opacity:0;transform:translateY(6px)}}

/* ---------------- danger-zone ----------------
   La page fait 248px pour une fenetre de 120, il faut donc defiler de
   128px avant que la zone apparaisse. Les deux boutons principaux
   restent tout en haut. La distance est litterale, pas symbolique. */
.ex-danger-zone{position:relative;width:170px;height:120px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-danger-zone .pg{position:absolute;left:0;right:0;top:0;padding:11px;display:flex;
  flex-direction:column;gap:8px;animation:aExDzScroll 5.6s cubic-bezier(.4,0,.2,1) infinite}
.ex-danger-zone .hd{height:13px;width:58%;border-radius:5px;background:var(--s4)}
.ex-danger-zone .pg .l{height:8px;border-radius:4px;background:var(--s2)}
.ex-danger-zone .pg .l.s{width:66%}
.ex-danger-zone .prim{display:flex;gap:7px}
.ex-danger-zone .prim b{display:block;height:18px;border-radius:6px}
.ex-danger-zone .prim .p{width:52px;background:var(--lime)}
.ex-danger-zone .prim .g{width:38px;background:var(--s3)}
.ex-danger-zone .zone{margin-top:24px;padding:9px;border-radius:9px;box-sizing:border-box;
  border:1.5px solid var(--danger);background:rgba(var(--danger-rgb),.08);
  display:flex;flex-direction:column;gap:8px}
.ex-danger-zone .lbl{font:700 8px/1 var(--mono);color:var(--danger);font-style:normal}
.ex-danger-zone .zr{display:flex;align-items:center;gap:9px}
.ex-danger-zone .zl{flex:1;height:6px;border-radius:3px;background:rgba(var(--danger-rgb),.45)}
.ex-danger-zone .zl.s{width:56%;flex:0 0 auto}
.ex-danger-zone .zb{width:32px;height:15px;border-radius:5px;background:var(--danger);margin-left:auto}
@keyframes aExDzScroll{0%,16%{transform:translateY(0)}56%,86%{transform:translateY(-128px)}
  96%,100%{transform:translateY(0)}}

/* ---------------- hold-to-delete ----------------
   Le curseur relache a 40 pour cent, l'anneau se rembobine, et c'est
   la tout le sujet. Un faux mouvement s'annule tout seul, ce qu'une
   boite de dialogue ne sait plus faire une fois le second clic parti.
   Le second essai va au bout et la carte disparait. */
.ex-hold-to-delete{position:relative;width:168px;height:138px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;align-items:center;gap:12px}
.ex-hold-to-delete .crd{width:100%;padding:10px;border-radius:9px;box-sizing:border-box;
  background:var(--card-2);border:1px solid var(--line-2);display:flex;flex-direction:column;gap:7px;
  animation:aExHdCard 5s ease infinite}
.ex-hold-to-delete .crd .t{height:8px;width:56%;border-radius:4px;background:var(--ink);opacity:.8}
.ex-hold-to-delete .crd .l{height:6px;border-radius:3px;background:var(--s4)}
.ex-hold-to-delete .btn{position:relative;display:grid;place-items:center;width:54px;height:54px}
.ex-hold-to-delete .ring{width:54px;height:54px;transform:rotate(-90deg)}
.ex-hold-to-delete .ring circle{fill:none}
.ex-hold-to-delete .ring .bg{stroke:var(--danger-bg);stroke-width:4}
.ex-hold-to-delete .ring .fg{stroke:var(--danger);stroke-width:4;stroke-linecap:round;
  stroke-dasharray:1;stroke-dashoffset:1;animation:aExHdRing 5s cubic-bezier(.4,0,.6,1) infinite}
.ex-hold-to-delete .tr{position:absolute;width:12px;height:14px;border-radius:2px;background:var(--danger)}
.ex-hold-to-delete .cur{position:absolute;left:50%;top:85px;margin-left:6px;animation:aExHdCur 5s ease infinite}
.ex-hold-to-delete .st{position:absolute;left:11px;bottom:11px;font:700 8px/1 var(--mono);
  color:var(--danger);font-style:normal}
@keyframes aExHdRing{0%,10%{stroke-dashoffset:1}40%{stroke-dashoffset:.42}
  52%,58%{stroke-dashoffset:1}82%,92%{stroke-dashoffset:0}98%,100%{stroke-dashoffset:1}}
@keyframes aExHdCur{0%,8%{transform:scale(1);opacity:.5}14%,40%{transform:scale(.74);opacity:1}
  52%,58%{transform:scale(1);opacity:.5}64%,92%{transform:scale(.74);opacity:1}
  98%,100%{transform:scale(1);opacity:.5}}
@keyframes aExHdCard{0%,84%{opacity:1;transform:scale(1)}92%,96%{opacity:0;transform:scale(.94)}
  99%,100%{opacity:1;transform:scale(1)}}

/* ---------------- named-confirmation ----------------
   Le nom attendu est au dessus du champ, lisible et copiable, sinon
   la friction ne sert a rien. Treize caracteres, treize pas, largeur
   en ch. Le bouton reste inerte jusqu'a 48 pour cent, c'est a dire
   jusqu'a la derniere lettre, et pas une frappe avant. */
.ex-named-confirmation{width:168px;height:150px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-named-confirmation .ttl{height:9px;width:62%;border-radius:4px;background:var(--ink);opacity:.85}
.ex-named-confirmation .warn{height:6px;border-radius:3px;background:rgba(var(--danger-rgb),.5)}
.ex-named-confirmation .ask{display:block;padding:5px 8px;border-radius:6px;background:var(--s1);
  font:600 10px/1 var(--mono);color:var(--ink-2)}
.ex-named-confirmation .ask em{font-style:normal}
.ex-named-confirmation .fld{display:flex;align-items:center;gap:2px;height:28px;border-radius:8px;
  background:var(--s0);border:1px solid var(--line-2);box-sizing:border-box;padding:0 8px;overflow:hidden;
  font:600 11px/28px var(--mono)}
.ex-named-confirmation .typed{display:block;font:inherit;color:var(--ink);white-space:nowrap;
  overflow:hidden;width:0;animation:aExNcType 5s steps(13,end) infinite}
.ex-named-confirmation .caret{display:block;flex:0 0 auto;width:1.5px;height:14px;background:var(--lime);
  animation:aExNcCr .9s steps(1,end) infinite}
.ex-named-confirmation .acts{display:flex;gap:8px;margin-top:auto}
.ex-named-confirmation .acts b{display:block;flex:1;height:24px;border-radius:8px}
.ex-named-confirmation .keep{background:var(--s3)}
.ex-named-confirmation .del{background:var(--s3);animation:aExNcBtn 5s steps(1,end) infinite}
@keyframes aExNcType{0%{width:0}42%,86%{width:13ch}96%,100%{width:0}}
@keyframes aExNcCr{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExNcBtn{0%,44%{background:var(--s3)}48%,88%{background:var(--danger)}92%,100%{background:var(--s3)}}

/* ---------------- grace-period ----------------
   L'objet supprime reste dans la liste, avec son etat, sa date exacte
   et son bouton d'annulation. Le cacher pour faire propre reviendrait
   a n'avoir de delai de grace que dans la base de donnees. Le curseur
   annule a 62 pour cent et la ligne redevient normale. */
.ex-grace-period{position:relative;width:170px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-grace-period .lst{display:flex;flex-direction:column;gap:9px}
.ex-grace-period .row{height:26px;border-radius:8px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-grace-period .pend{padding:9px;border-radius:8px;box-sizing:border-box;
  background:var(--card-2);border:1px solid var(--warn);
  display:flex;flex-direction:column;gap:8px;animation:aExGpPend 5.2s ease infinite}
.ex-grace-period .pend .hd{display:flex;align-items:center;gap:8px}
.ex-grace-period .nm{flex:1;height:6px;border-radius:3px;background:var(--s5)}
.ex-grace-period .tag{font:700 7px/1 var(--mono);color:var(--warn);font-style:normal}
.ex-grace-period .ft{display:flex;align-items:center;gap:8px}
.ex-grace-period .dt{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-grace-period .undo{width:34px;height:15px;border-radius:5px;background:var(--s3);margin-left:auto;
  animation:aExGpUndo 5.2s steps(1,end) infinite}
.ex-grace-period .cur{position:absolute;right:14px;bottom:19px;animation:aExGpCur 5.2s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExGpPend{0%,66%{border-color:var(--warn)}
  74%,94%{border-color:var(--line-2)}
  98%,100%{border-color:var(--warn)}}
@keyframes aExGpUndo{0%,58%{background:var(--s3)}60%,68%{background:var(--lime)}72%,100%{background:var(--s3)}}
@keyframes aExGpCur{0%,26%{transform:translate(12px,10px);opacity:0}
  38%{transform:translate(0,0);opacity:1}
  58%{transform:translate(0,3px);opacity:1}
  70%{transform:translate(0,0);opacity:1}
  84%{transform:translate(12px,10px);opacity:0}
  84.001%,100%{transform:translate(12px,10px);opacity:0}}

/* ---------------- liquid-tabs ----------------
   Trois segments de 44px avec 4px d'ecart, donc un pas de 48 et des
   positions de blob a 0, 48 et 96 exactement. Le blob fait lui aussi
   44 de large, il couvre donc sa cible au pixel pres, et la couleur
   du libelle actif suit le blob au lieu de rester grise dessous. */
.ex-liquid-tabs{width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-liquid-tabs .hd .t{height:8px;width:56px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-liquid-tabs .sgm{position:relative;display:flex;gap:4px;padding:4px;border-radius:11px;
  box-sizing:border-box;background:var(--s1);border:1px solid var(--line)}
.ex-liquid-tabs .s{position:relative;z-index:2;width:44px;box-sizing:border-box;text-align:center;
  padding:7px 0;border-radius:8px;font:600 9px/9px var(--mono);color:var(--ink-2);font-style:normal;
  animation:aExLtA 4.4s cubic-bezier(.5,0,.2,1) infinite}
.ex-liquid-tabs .s2{animation-name:aExLtB}
.ex-liquid-tabs .s3{animation-name:aExLtC}
.ex-liquid-tabs .blob{position:absolute;left:4px;top:4px;width:44px;height:23px;border-radius:8px;
  background:var(--lime);z-index:1;animation:aExLtBlob 4.4s cubic-bezier(.5,0,.2,1) infinite}
.ex-liquid-tabs .bdy{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden}
.ex-liquid-tabs .bdy i{position:absolute;left:9px;right:9px;top:9px;bottom:9px;border-radius:6px}
.ex-liquid-tabs .v1{background:repeating-linear-gradient(90deg,var(--s3) 0 14px,transparent 14px 20px);
  animation:aExLtV1 4.4s ease infinite}
.ex-liquid-tabs .v2{background:repeating-linear-gradient(180deg,var(--s3) 0 7px,transparent 7px 13px);
  animation:aExLtV2 4.4s ease infinite}
.ex-liquid-tabs .v3{background:radial-gradient(circle at 40% 45%,var(--s4) 0 10px,transparent 11px),
  radial-gradient(circle at 70% 70%,var(--s3) 0 8px,transparent 9px);
  animation:aExLtV3 4.4s ease infinite}
@keyframes aExLtBlob{
  0%,16%{transform:translateX(0) scaleX(1)}
  26%{transform:translateX(24px) scaleX(1.5)}
  32%,44%{transform:translateX(48px) scaleX(1)}
  56%{transform:translateX(72px) scaleX(1.5)}
  66%,82%{transform:translateX(96px) scaleX(1)}
  92%,100%{transform:translateX(0) scaleX(1)}}
@keyframes aExLtA{0%,16%{color:var(--on-accent)}26%,88%{color:var(--ink-2)}96%,100%{color:var(--on-accent)}}
@keyframes aExLtB{0%,22%{color:var(--ink-2)}32%,44%{color:var(--on-accent)}54%,100%{color:var(--ink-2)}}
@keyframes aExLtC{0%,56%{color:var(--ink-2)}66%,82%{color:var(--on-accent)}90%,100%{color:var(--ink-2)}}
@keyframes aExLtV1{0%,18%{opacity:1}28%,88%{opacity:0}96%,100%{opacity:1}}
@keyframes aExLtV2{0%,24%{opacity:0}34%,46%{opacity:1}56%,100%{opacity:0}}
@keyframes aExLtV3{0%,58%{opacity:0}68%,82%{opacity:1}90%,100%{opacity:0}}

/* ---------------- cursor-trail ----------------
   Le trajet va d'une tuile a une autre, il ne fait pas un tour
   decoratif. Chaque tuile atteinte SE SOULEVE et sort son titre, et
   le curseur lui meme s'ouvre en pastille au dessus d'elle puis se
   referme en repartant.
   La traînee porte alors une information, elle est longue pendant les
   trajets rapides entre deux tuiles et se referme quand le curseur
   ralentit pour se poser. Un tour a vitesse constante au dessus d'une
   grille qui ne reagit pas ne montrerait rien de tout cela.
   Grille de 148 utiles, deux colonnes de 70 avec 8 d'ecart, donc des
   centres de tuiles a 46 et 124 en x, 48 et 108 en y. Le curseur de
   14 se cale donc a 39 et 117, 41 et 101. */
.ex-cursor-trail{position:relative;width:170px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:11px}
.ex-cursor-trail .grd{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ex-cursor-trail .tl{position:relative;height:52px;border-radius:8px;overflow:hidden;
  background:linear-gradient(135deg,var(--s2),var(--s1));border:1px solid var(--line)}
.ex-cursor-trail .tl b{position:absolute;left:8px;bottom:7px;display:block;height:5px;width:34px;
  border-radius:3px;background:var(--ink);opacity:0}
.ex-cursor-trail .t1{animation:aExCtTile 5.2s cubic-bezier(.2,.9,.25,1) infinite}
.ex-cursor-trail .t4{animation:aExCtTile 5.2s cubic-bezier(.2,.9,.25,1) infinite;animation-delay:2.6s}
.ex-cursor-trail .t1 b{animation:aExCtLbl 5.2s ease infinite}
.ex-cursor-trail .t4 b{animation:aExCtLbl 5.2s ease infinite;animation-delay:2.6s}
/* Le .cur ne recoit que ses coordonnees et son animation, sa forme
   vient de base.css. Le .tr n'est pas un curseur, il a droit a la
   sienne. nth-child et non nth-of-type, les six suiveurs sont des
   span comme la grille et le curseur. */
.ex-cursor-trail .tr,.ex-cursor-trail .cur{position:absolute;left:39px;top:41px;
  animation:aExCtPath 5.2s cubic-bezier(.5,0,.3,1) infinite}
.ex-cursor-trail .tr{display:block;width:12px;height:12px;border-radius:50%;background:var(--lime)}
.ex-cursor-trail .tr:nth-child(2){animation-delay:-.30s;opacity:.16;filter:blur(3px)}
.ex-cursor-trail .tr:nth-child(3){animation-delay:-.25s;opacity:.26;filter:blur(2.4px)}
.ex-cursor-trail .tr:nth-child(4){animation-delay:-.20s;opacity:.38;filter:blur(1.8px)}
.ex-cursor-trail .tr:nth-child(5){animation-delay:-.15s;opacity:.52;filter:blur(1.2px)}
.ex-cursor-trail .tr:nth-child(6){animation-delay:-.10s;opacity:.68;filter:blur(.6px)}
.ex-cursor-trail .tr:nth-child(7){animation-delay:-.05s;opacity:.85}
.ex-cursor-trail .cur{z-index:8;animation-name:aExCtPath,aExCtCur;
  animation-duration:5.2s,5.2s;animation-iteration-count:infinite,infinite}
/* Le trajet s'arrete VRAIMENT sur chaque tuile, les paliers 18 a 34 et
   62 a 82 sont des temps morts. C'est pendant ces arrets que la
   traînee se referme, et pendant les transitions qu'elle s'etire. */
@keyframes aExCtPath{
  0%,6%{transform:translate(0,0)}
  18%,34%{transform:translate(0,0)}
  50%{transform:translate(78px,60px)}
  62%,82%{transform:translate(78px,60px)}
  100%{transform:translate(0,0)}}
@keyframes aExCtCur{0%,14%{scale:1}22%,32%{scale:1.9}42%,58%{scale:1}
  66%,80%{scale:1.9}88%,100%{scale:1}}
@keyframes aExCtTile{0%,14%{transform:translateY(0)}22%,32%{transform:translateY(-4px)}
  42%,100%{transform:translateY(0)}}
@keyframes aExCtLbl{0%,16%{opacity:0}24%,32%{opacity:.9}42%,100%{opacity:0}}

/* ---------------- optimistic-ui ----------------
   Le coeur se remplit a 26 pour cent, bien avant la reponse. Le
   serveur refuse a 62, le coeur revient a 70 ET le bandeau arrive a
   68 avec sa relance. Le compteur suit le meme aller retour, 128 puis
   129 puis 128, sinon le retour en arriere serait a moitie fait. */
.ex-optimistic-ui{position:relative;width:168px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-optimistic-ui .post{display:flex;flex-direction:column;gap:6px}
.ex-optimistic-ui .img{height:46px;border-radius:8px;background:linear-gradient(135deg,var(--a3),var(--a4))}
.ex-optimistic-ui .cap{height:6px;width:72%;border-radius:3px;background:var(--s4)}
.ex-optimistic-ui .bar{display:flex;align-items:center;gap:9px}
.ex-optimistic-ui .hrt{width:17px;height:17px;flex:0 0 auto;border-radius:5px 5px 9px 9px;
  border:2px solid var(--s6);box-sizing:border-box;transform:rotate(-45deg);
  animation:aExOuHrt 4.4s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-optimistic-ui .cnt{position:relative;width:26px;height:14px;flex:0 0 auto;
  font:700 10px/14px var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-2)}
.ex-optimistic-ui .cnt u{position:absolute;left:0;top:0;text-decoration:none}
.ex-optimistic-ui .c1{animation:aExOuA 4.4s steps(1,end) infinite}
.ex-optimistic-ui .c2{animation:aExOuB 4.4s steps(1,end) infinite}
.ex-optimistic-ui .err{display:flex;align-items:center;gap:8px;margin-top:auto;box-sizing:border-box;
  padding:7px 9px;border-radius:8px;background:rgba(var(--danger-rgb),.14);border:1px solid var(--danger);
  animation:aExOuErr 4.4s ease infinite}
.ex-optimistic-ui .err .l{flex:1;height:5px;border-radius:3px;background:var(--danger);opacity:.6}
.ex-optimistic-ui .err .rt{width:30px;height:14px;border-radius:5px;background:var(--danger)}
@keyframes aExOuHrt{0%,18%{background:transparent;border-color:var(--s6);transform:rotate(-45deg) scale(1)}
  26%{background:var(--danger);border-color:var(--danger);transform:rotate(-45deg) scale(1.24)}
  34%,62%{background:var(--danger);border-color:var(--danger);transform:rotate(-45deg) scale(1)}
  70%{background:transparent;border-color:var(--danger);transform:rotate(-45deg) scale(1.1)}
  80%,100%{background:transparent;border-color:var(--s6);transform:rotate(-45deg) scale(1)}}
@keyframes aExOuA{0%,24%{opacity:1}26%,68%{opacity:0}70%,100%{opacity:1}}
@keyframes aExOuB{0%,24%{opacity:0}26%,68%{opacity:1}70%,100%{opacity:0}}
@keyframes aExOuErr{0%,60%{opacity:0;transform:translateY(6px)}
  68%,92%{opacity:1;transform:translateY(0)}97%,100%{opacity:0;transform:translateY(6px)}}

/* ---------------- undo-toast ----------------
   Le message est archive d'abord, a 10 pour cent, sans rien demander.
   Le toast monte a 28, on appuie sur annuler a 66, la ligne REVIENT a
   80 et le toast ne part qu'a 94. La jauge se recharge apres, une
   fois le toast invisible. */
.ex-undo-toast{position:relative;width:170px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-undo-toast .rw{display:flex;align-items:center;gap:9px;height:26px;padding:0 9px;
  box-sizing:border-box;border-radius:7px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-undo-toast .rw .av{width:15px;height:15px;border-radius:50%;background:var(--s4);flex:0 0 auto}
.ex-undo-toast .rw .l{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-undo-toast .go{animation:aExUtRow 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-undo-toast .go .av{background:var(--lime)}
.ex-undo-toast .toa{position:absolute;left:11px;right:11px;bottom:11px;height:28px;border-radius:9px;
  box-sizing:border-box;background:var(--card-2);border:1px solid var(--line-2);overflow:hidden;
  display:flex;align-items:center;gap:9px;padding:0 10px;
  animation:aExUtT 4.6s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-undo-toast .toa .msg{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-undo-toast .toa .undo{width:34px;height:9px;border-radius:4px;background:var(--lime);
  animation:aExUtU 4.6s steps(1,end) infinite}
.ex-undo-toast .toa .drain{position:absolute;left:0;bottom:0;right:0;height:2px;background:var(--lime);
  transform-origin:left;animation:aExUtD 4.6s linear infinite}
@keyframes aExUtRow{0%,10%{transform:translateX(0);opacity:1}
  32%,66%{transform:translateX(172px);opacity:0}
  80%,100%{transform:translateX(0);opacity:1}}
@keyframes aExUtT{0%,14%{transform:translateY(36px);opacity:0}28%,84%{transform:translateY(0);opacity:1}
  94%,100%{transform:translateY(36px);opacity:0}}
@keyframes aExUtD{0%,28%{transform:scaleX(1)}66%,94%{transform:scaleX(.3)}98%,100%{transform:scaleX(1)}}
@keyframes aExUtU{0%,64%{opacity:.55}66%,76%{opacity:1}80%,100%{opacity:.55}}

/* ---------------- inline-alert ----------------
   L'emplacement du bandeau est RESERVE au dessus du champ concerne,
   28px qui existent que le message soit la ou non. Rien ne bouge quand
   il arrive, il ne fait qu'apparaitre. Animer sa hauteur pousserait
   vers le bas le champ dont il parle, ce qui est le piege du terme. */
.ex-inline-alert{width:168px;height:148px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-inline-alert .lb{height:5px;width:34px;border-radius:3px;background:var(--ink-3)}
.ex-inline-alert .fld{height:26px;border-radius:7px;background:var(--s1);box-sizing:border-box;
  border:1px solid var(--line-2)}
.ex-inline-alert .slot{height:30px;flex:0 0 auto}
.ex-inline-alert .alert{display:flex;align-items:center;gap:8px;height:30px;box-sizing:border-box;
  border-radius:8px;padding:0 9px;background:rgba(var(--danger-rgb),.14);
  border-left:3px solid var(--danger);animation:aExIaAl 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-inline-alert .alert .ic{width:10px;height:10px;border-radius:50%;background:var(--danger);flex:0 0 auto}
.ex-inline-alert .alert .l{flex:1;height:5px;border-radius:3px;background:var(--danger);opacity:.55}
.ex-inline-alert .bad{animation:aExIaFld 4s ease infinite}
.ex-inline-alert .sub{margin-top:auto;height:24px;border-radius:8px;background:var(--lime)}
@keyframes aExIaAl{0%,14%{opacity:0;transform:translateY(-6px)}
  32%,90%{opacity:1;transform:translateY(0)}97%,100%{opacity:0;transform:translateY(-6px)}}
@keyframes aExIaFld{0%,14%{border-color:var(--line-2)}32%,90%{border-color:var(--danger)}
  97%,100%{border-color:var(--line-2)}}

/* ---------------- stale-revalidate ----------------
   Les deux valeurs sont empilees a la meme place, meme largeur, meme
   police tabulaire, et elles se croisent en opacite. Rien ne change de
   taille, donc rien ne pousse rien, et le trait de repere a droite le
   prouve. Le cache s'affiche des la premiere image, il n'y a jamais
   d'ecran vide ni de squelette sur une revisite. */
.ex-stale-revalidate{width:170px;height:138px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-stale-revalidate .hd{display:flex;align-items:center;gap:8px}
.ex-stale-revalidate .hd .t{height:8px;width:52px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-stale-revalidate .spin{width:10px;height:10px;border-radius:50%;margin-left:auto;
  border:1.5px solid var(--s2);border-top-color:var(--lime);animation:aExSrSpin .9s linear infinite}
.ex-stale-revalidate .kpi{position:relative;display:flex;align-items:flex-end;gap:8px;
  padding:10px;border-radius:9px;background:var(--card-2);border:1px solid var(--line-2)}
.ex-stale-revalidate .stk{position:relative;width:46px;height:28px}
.ex-stale-revalidate .stk u{position:absolute;left:0;top:0;width:46px;text-decoration:none;
  font:800 22px/28px var(--mono);font-variant-numeric:tabular-nums;color:var(--lime)}
.ex-stale-revalidate .v1{animation:aExSrA 3.8s ease infinite}
.ex-stale-revalidate .v2{animation:aExSrB 3.8s ease infinite}
.ex-stale-revalidate .gd{width:1px;height:24px;
  background:repeating-linear-gradient(180deg,var(--line-2) 0 3px,transparent 3px 6px)}
.ex-stale-revalidate .un{width:26px;height:6px;border-radius:3px;background:var(--s4);margin-bottom:5px}
.ex-stale-revalidate .lst{display:flex;flex-direction:column;gap:7px;margin-top:auto}
.ex-stale-revalidate .lst i{height:9px;border-radius:4px;background:var(--s2)}
.ex-stale-revalidate .lst i:last-child{width:64%}
@keyframes aExSrSpin{to{transform:rotate(360deg)}}
@keyframes aExSrA{0%,48%{opacity:1}56%,94%{opacity:0}98%,100%{opacity:1}}
@keyframes aExSrB{0%,48%{opacity:0}56%,94%{opacity:1}98%,100%{opacity:0}}

/* ---------------- empty-state ----------------
   Les deux colonnes vont jusqu'au bout de leur action au lieu de se
   croiser en opacite. A gauche on appuie sur Creer et la premiere
   ligne tombe dans la liste, le vide se resout. A droite on appuie
   sur Relancer, l'indicateur tourne, et les lignes qui existaient
   deja arrivent. Meme duree, meme instant de pression a 40 pour cent,
   et deux resolutions opposees.
   C'est cela que le terme demande de distinguer, pas deux illustrations
   differentes. Un ecran vide se remplit par une creation, un
   chargement rate se remplit par un renvoi. */
.ex-empty-state{width:172px;height:146px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;gap:11px}
.ex-empty-state .col{position:relative;flex:1;display:flex;flex-direction:column;gap:7px}
.ex-empty-state .tag{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-empty-state .ko .tag{color:var(--danger)}
.ex-empty-state .box{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:7px;border-radius:10px;box-sizing:border-box;
  border:1px dashed var(--s5);overflow:hidden}
.ex-empty-state .ko .box{border-color:rgba(var(--danger-rgb),.55);border-style:solid}
.ex-empty-state .ic{width:24px;height:18px;border-radius:5px;box-sizing:border-box;
  border:1.5px dashed var(--s6);animation:aExEsHint 5.4s ease infinite}
.ex-empty-state .ko .ic{border-style:solid;border-color:var(--danger)}
.ex-empty-state .ln{width:44px;height:5px;border-radius:3px;background:var(--s3);
  animation:aExEsHint 5.4s ease infinite}
.ex-empty-state .ctb{width:46px;height:17px;border-radius:7px;background:var(--lime);
  animation:aExEsCta 5.4s steps(1,end) infinite}
.ex-empty-state .ko .ctb{background:transparent;border:1.5px solid var(--danger);box-sizing:border-box}
/* les lignes qui arrivent occupent la boite, elles ne la survolent pas */
.ex-empty-state .fill{position:absolute;left:7px;right:7px;top:7px;display:flex;
  flex-direction:column;gap:6px}
.ex-empty-state .fill i{display:block;height:13px;border-radius:5px;background:var(--s3);
  animation:aExEsRow 5.4s cubic-bezier(.2,.9,.25,1) infinite}
.ex-empty-state .fill i:nth-child(2){animation-delay:.09s}
.ex-empty-state .fill i:nth-child(3){animation-delay:.18s}
.ex-empty-state .col .fill i{background:var(--lime)}
.ex-empty-state .ko .fill i{background:var(--s3)}
.ex-empty-state .spin{position:absolute;left:50%;top:50%;width:14px;height:14px;margin:-7px 0 0 -7px;
  border-radius:50%;border:2px solid var(--s2);border-top-color:var(--danger);
  animation:aExEsSpin .8s linear infinite,aExEsSpinOn 5.4s steps(1,end) infinite}
/* la premiere colonne cree UNE ligne, la seconde en ramene trois,
   parce que les donnees existaient deja et n'avaient pas charge */
.ex-empty-state .col .fill i:nth-child(2),
.ex-empty-state .col .fill i:nth-child(3){display:none}
.ex-empty-state .ko .fill i:nth-child(2),
.ex-empty-state .ko .fill i:nth-child(3){display:block}
@keyframes aExEsHint{0%,40%{opacity:1}52%,92%{opacity:0}97%,100%{opacity:1}}
@keyframes aExEsCta{0%,36%{filter:brightness(1)}38%,44%{filter:brightness(1.35)}
  46%,100%{filter:brightness(1)}}
@keyframes aExEsRow{0%,52%{opacity:0;transform:translateY(-7px)}
  64%,92%{opacity:1;transform:translateY(0)}97%,100%{opacity:0;transform:translateY(-7px)}}
@keyframes aExEsSpin{to{transform:rotate(360deg)}}
@keyframes aExEsSpinOn{0%,40%{opacity:0}44%,58%{opacity:1}62%,100%{opacity:0}}

/* ---------------- partial-failure ----------------
   Les deux comptes sont annonces avant tout, 47 passees et 3 refusees,
   jamais le lot entier en echec. Chaque ligne fautive porte SA raison
   et non un message unique, et la relance ne reprend que ces trois la
   en conservant les 47 qui ont abouti. */
.ex-partial-failure{width:170px;height:146px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-partial-failure .sum{display:flex;align-items:center;gap:7px}
.ex-partial-failure .sum b{display:block;padding:4px 8px;border-radius:6px;font:700 9px/1 var(--mono)}
.ex-partial-failure .sum .ok{background:rgba(var(--accent-rgb),.2);color:var(--lime)}
.ex-partial-failure .sum .ko{background:rgba(var(--danger-rgb),.2);color:var(--danger)}
.ex-partial-failure .lst{display:flex;flex-direction:column;gap:6px}
.ex-partial-failure .r{display:flex;align-items:center;gap:8px;height:18px;padding:0 8px;
  box-sizing:border-box;border-radius:6px;background:var(--s1)}
.ex-partial-failure .r .ln{flex:1;height:5px;border-radius:3px;background:var(--s4)}
.ex-partial-failure .r.bad{background:rgba(var(--danger-rgb),.12)}
.ex-partial-failure .r.bad .ln{background:var(--danger);opacity:.55}
.ex-partial-failure .why{width:44px;height:5px;border-radius:3px;background:var(--danger);
  animation:aExPfWhy 4.2s ease infinite}
.ex-partial-failure .r:nth-of-type(2) .why{animation-delay:.12s}
.ex-partial-failure .rt{display:flex;align-items:center;gap:8px;margin-top:auto;height:22px;padding:0 9px;
  box-sizing:border-box;border-radius:7px;background:var(--danger);
  animation:aExPfRt 4.2s ease infinite}
.ex-partial-failure .rt i{height:6px;border-radius:3px;background:var(--void);opacity:.7}
.ex-partial-failure .rt .a{width:52px}
.ex-partial-failure .rt .b{width:22px;margin-left:auto}
@keyframes aExPfWhy{0%,42%{opacity:0}56%,90%{opacity:1}97%,100%{opacity:0}}
@keyframes aExPfRt{0%,50%{opacity:0;transform:translateY(6px)}64%,92%{opacity:1;transform:translateY(0)}
  97%,100%{opacity:0;transform:translateY(6px)}}

/* ---------------- progressive-disclosure ----------------
   Une boite d'impression. Trois reglages que tout le monde touche
   restent visibles, sept que presque personne ne touche sont derriere
   un declencheur QUI PORTE LEUR NOM ET LEUR NOMBRE. Le mouvement
   revele une seconde classe d'options dont la vue par defaut
   protegeait l'utilisateur. Gele, on perd l'existence meme de cette
   classe et le fait qu'elle a ete comptee. */
.ex-progressive-disclosure{width:170px;height:168px;border-radius:11px;overflow:hidden;
  background:var(--card-2);border:1px solid var(--line-2);padding:11px;
  display:flex;flex-direction:column;gap:6px}
.ex-progressive-disclosure .ttl{height:8px;width:48px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-progressive-disclosure .rw{display:flex;align-items:center;gap:9px;height:20px;padding:0 9px;
  box-sizing:border-box;border-radius:6px;background:var(--s1)}
.ex-progressive-disclosure .rw .l{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-progressive-disclosure .stp{position:relative;flex:0 0 auto;width:30px;height:13px;border-radius:4px;
  background:var(--s3)}
.ex-progressive-disclosure .stp b{position:absolute;left:0;top:0;bottom:0;width:11px;border-radius:4px;
  background:var(--s5)}
.ex-progressive-disclosure .tg{position:relative;flex:0 0 auto;width:22px;height:12px;border-radius:7px;
  background:var(--lime)}
.ex-progressive-disclosure .tg i{position:absolute;top:2px;left:12px;width:8px;height:8px;
  border-radius:50%;background:var(--on-accent)}
.ex-progressive-disclosure .trg{display:flex;align-items:center;gap:7px;height:22px;padding:0 9px;
  box-sizing:border-box;border-radius:7px;background:var(--s0);border:1px dashed var(--line-2)}
.ex-progressive-disclosure .cv{flex:0 0 auto;width:5px;height:5px;
  border-right:1.5px solid var(--ink-2);border-bottom:1.5px solid var(--ink-2);
  transform:rotate(-45deg);animation:aExPdCv 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-progressive-disclosure .nm{flex:1;font:600 8px/1 var(--mono);color:var(--ink-2);font-style:normal}
.ex-progressive-disclosure .cnt{flex:0 0 auto;font:700 8px/1 var(--mono);color:var(--lime);font-style:normal}
.ex-progressive-disclosure .slot{position:relative;flex:1}
.ex-progressive-disclosure .adv{position:absolute;left:0;right:0;top:0;display:flex;
  flex-direction:column;gap:5px;animation:aExPdAdv 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-progressive-disclosure .adv i{display:block;height:9px;border-radius:4px;background:var(--s2)}
.ex-progressive-disclosure .adv i:nth-child(2){width:82%}
.ex-progressive-disclosure .adv i:nth-child(3){width:64%}
@keyframes aExPdAdv{0%,20%{opacity:0;transform:translateY(-8px)}36%,86%{opacity:1;transform:translateY(0)}
  95%,100%{opacity:0;transform:translateY(-8px)}}
@keyframes aExPdCv{0%,20%{transform:rotate(-45deg)}36%,86%{transform:rotate(45deg)}
  95%,100%{transform:rotate(-45deg)}}

/* ---------------- disclosure-triangle ----------------
   Un arbre de fichiers. L'anneau de focus se deplace de ligne en ligne
   au Tab et couvre la LIGNE ENTIÈRE, ce qui montre que la cible de
   clic n'est pas le triangle de 12px. Puis le chevron tourne d'un
   quart de tour et les enfants apparaissent. Gele, on perd la taille
   reelle de la cible et la continuite de la rotation. */
.ex-disclosure-triangle{width:168px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:11px;display:flex;flex-direction:column;gap:5px}
.ex-disclosure-triangle .rw{position:relative;display:flex;align-items:center;gap:8px;height:20px;
  padding:0 8px;box-sizing:border-box;border-radius:5px}
.ex-disclosure-triangle .rw .nm{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-disclosure-triangle .rw .nm.s{width:56%;flex:0 0 auto}
.ex-disclosure-triangle .tri{flex:0 0 auto;width:0;height:0;border-left:8px solid var(--lime);
  border-top:5px solid transparent;border-bottom:5px solid transparent}
.ex-disclosure-triangle .open{animation:aExDtTri 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-disclosure-triangle .r1{animation:aExDtF1 4.4s steps(1,end) infinite}
.ex-disclosure-triangle .r2{animation:aExDtF2 4.4s steps(1,end) infinite}
.ex-disclosure-triangle .kids{position:relative;flex:1}
.ex-disclosure-triangle .kids .k{position:absolute;left:18px;right:0;display:flex;align-items:center;
  gap:8px;height:18px;animation:aExDtK 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-disclosure-triangle .kids .k i{flex:1;height:5px;border-radius:3px;background:var(--s3)}
.ex-disclosure-triangle .k1{top:0}
.ex-disclosure-triangle .k2{top:22px;animation-delay:.05s}
.ex-disclosure-triangle .k3{top:44px;animation-delay:.1s}
@keyframes aExDtTri{0%,18%{transform:rotate(0)}38%,84%{transform:rotate(90deg)}96%,100%{transform:rotate(0)}}
@keyframes aExDtK{0%,22%{opacity:0;transform:translateY(-7px)}40%,84%{opacity:1;transform:translateY(0)}
  95%,100%{opacity:0;transform:translateY(-7px)}}
@keyframes aExDtF1{0%,6%{background:none}10%,30%{background:rgba(var(--accent-rgb),.16)}
  34%,100%{background:none}}
@keyframes aExDtF2{0%,34%{background:none}38%,88%{background:rgba(var(--accent-rgb),.16)}
  94%,100%{background:none}}

/* ---------------- disabled-vs-hidden ----------------
   Le meme menu, vu par deux comptes. En haut Export CSV est grise mais
   le focus S'Y POSE et sort sa raison, Pro plan. En bas l'action
   d'administration n'existe pas du tout pour ce compte, et l'espace
   qu'elle occuperait reste vide.
   Gele, on perd le fait que le controle grise reste atteignable au
   clavier, ce qui est toute la consigne du terme. */
.ex-disabled-vs-hidden{width:172px;height:120px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;gap:12px}
.ex-disabled-vs-hidden .col{flex:1;display:flex;flex-direction:column;gap:7px}
.ex-disabled-vs-hidden .tag{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-disabled-vs-hidden .mn{position:relative;padding:5px;border-radius:8px;background:var(--card-2);
  border:1px solid var(--line-2);display:flex;flex-direction:column;gap:3px}
.ex-disabled-vs-hidden .it{display:flex;align-items:center;gap:8px;height:18px;padding:0 7px;
  box-sizing:border-box;border-radius:5px}
.ex-disabled-vs-hidden .it .l{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-disabled-vs-hidden .it .l.s{width:58%;flex:0 0 auto}
.ex-disabled-vs-hidden .off{opacity:.45}
.ex-disabled-vs-hidden .off .l{background:var(--s4)}
.ex-disabled-vs-hidden .foc{animation:aExDvFoc 4.4s steps(1,end) infinite}
.ex-disabled-vs-hidden .why{height:15px;
  border-radius:5px;box-sizing:border-box;background:var(--s1);border:1px solid var(--line-2);
  display:flex;align-items:center;padding:0 6px;
  font:600 7px/1 var(--mono);color:var(--ink-2);
  animation:aExDvWhy 4.4s ease infinite}
.ex-disabled-vs-hidden .tag{font-size:7px}
.ex-disabled-vs-hidden .gone{height:18px;border-radius:5px;border:1px dashed var(--line);
  box-sizing:border-box;opacity:.4}
@keyframes aExDvFoc{0%,16%{box-shadow:none}22%,80%{box-shadow:0 0 0 2px var(--lime)}
  88%,100%{box-shadow:none}}
@keyframes aExDvWhy{0%,20%{opacity:0;transform:translateY(-4px)}
  30%,80%{opacity:1;transform:translateY(0)}88%,100%{opacity:0;transform:translateY(-4px)}}

/* ---------------- command-palette ----------------
   La palette FAIT quelque chose. On tape, la liste tombe de quatre a
   deux, Entree lance la commande surlignee et le panneau apparait
   DANS l'application derriere. Une palette qui filtre sans jamais rien
   executer ne montrerait que la moitie du terme.
   La colonne des raccourcis a droite est le second metier de l'outil,
   elle apprend les raccourcis et finit par le rendre inutile. */
.ex-command-palette{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px}
.ex-command-palette .app2{display:flex;flex-direction:column;gap:7px}
.ex-command-palette .app2 i{display:block;height:11px;border-radius:5px;background:var(--s2)}
.ex-command-palette .app2 i:nth-child(3){width:66%}
.ex-command-palette .pnl{position:absolute;left:11px;right:11px;bottom:11px;height:34px;
  border-radius:8px;box-sizing:border-box;background:var(--card-2);border:1px solid var(--lime);
  animation:aExCpPnl 5s cubic-bezier(.2,.9,.25,1) infinite}
.ex-command-palette .ov{position:absolute;left:8px;right:8px;top:16px;padding:6px;border-radius:10px;
  box-sizing:border-box;background:var(--void);border:1px solid var(--line-2);
  box-shadow:0 14px 28px rgba(var(--shadow-rgb),.55);
  display:flex;flex-direction:column;gap:4px;z-index:3;
  animation:aExCpOv 5s cubic-bezier(.2,.9,.25,1) infinite}
.ex-command-palette .inp{display:flex;align-items:center;gap:6px;height:22px;padding:0 8px;
  box-sizing:border-box;border-radius:6px;background:var(--s0)}
.ex-command-palette .kbd{flex:0 0 auto;font:700 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-command-palette .q{display:block;height:5px;border-radius:3px;background:var(--s5);width:0;
  animation:aExCpQ 5s steps(7,end) infinite}
.ex-command-palette .caret{display:block;flex:0 0 auto;width:1.5px;height:11px;background:var(--lime);
  animation:aExCpCr .9s steps(1,end) infinite}
.ex-command-palette .row{display:flex;align-items:center;gap:8px;height:16px;padding:0 8px;
  box-sizing:border-box;border-radius:5px}
.ex-command-palette .row .l{flex:1;height:4px;border-radius:2px;background:var(--s5)}
.ex-command-palette .row .sc{flex:0 0 auto;font:700 6px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-command-palette .k1{animation:aExCpHit 5s ease infinite}
.ex-command-palette .k3,.ex-command-palette .k4{animation:aExCpCut 5s ease infinite}
.ex-command-palette .k4{animation-delay:.06s}
@keyframes aExCpOv{0%,10%{opacity:0;transform:translateY(-8px) scale(.97)}
  20%,74%{opacity:1;transform:translateY(0) scale(1)}
  82%,100%{opacity:0;transform:translateY(-8px) scale(.97)}}
@keyframes aExCpQ{0%,18%{width:0}44%,74%{width:42px}82%,100%{width:0}}
@keyframes aExCpCr{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExCpHit{0%,44%{background:none}52%,74%{background:rgba(var(--accent-rgb),.24)}
  82%,100%{background:none}}
@keyframes aExCpCut{0%,44%{opacity:1;transform:scaleY(1)}56%,74%{opacity:0;transform:scaleY(.3)}
  82%,100%{opacity:1;transform:scaleY(1)}}
@keyframes aExCpPnl{0%,74%{opacity:0;transform:translateY(10px)}
  84%,96%{opacity:1;transform:translateY(0)}99%,100%{opacity:0;transform:translateY(10px)}}

/* ---------------- focus-handoff ----------------
   Les trois choses qu'une navigation cote client doit deplacer a la
   main sont toutes visibles. Le titre de l'onglet change, l'anneau de
   focus se pose sur le nouveau titre principal, et la region live
   annonce une fois. Gele, il ne reste qu'une page qui a glisse, ce qui
   est precisement le probleme que le terme decrit. */
.ex-focus-handoff{width:170px;height:148px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-focus-handoff .tbb{display:flex;align-items:center;gap:7px;height:20px;padding:0 9px;
  box-sizing:border-box;border-radius:6px 6px 0 0;background:var(--panel);border:1px solid var(--line)}
.ex-focus-handoff .tbb .fav{width:7px;height:7px;border-radius:2px;background:var(--lime);flex:0 0 auto}
.ex-focus-handoff .stk{position:relative;flex:1;height:5px}
.ex-focus-handoff .stk i{position:absolute;left:0;top:0;height:5px;border-radius:3px;background:var(--ink-2)}
.ex-focus-handoff .t1{width:44px;animation:aExFhT1 4s steps(1,end) infinite}
.ex-focus-handoff .t2{width:68px;animation:aExFhT2 4s steps(1,end) infinite}
.ex-focus-handoff .vp{position:relative;flex:1;overflow:hidden;border-radius:9px}
.ex-focus-handoff .pg{position:absolute;inset:0;padding:11px;box-sizing:border-box;
  display:flex;flex-direction:column;gap:8px}
.ex-focus-handoff .pg .h1{height:12px;width:62%;border-radius:5px;background:rgba(var(--ink-rgb),.55)}
.ex-focus-handoff .pg .l{height:7px;border-radius:4px;background:rgba(var(--ink-rgb),.22)}
.ex-focus-handoff .a{background:var(--s2);animation:aExFhA 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-focus-handoff .b{background:var(--s4);animation:aExFhB 4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-focus-handoff .b .h1{background:var(--lime);animation:aExFhRing 4s ease infinite}
.ex-focus-handoff .live{position:absolute;right:9px;bottom:9px;width:38px;height:10px;border-radius:5px;
  background:var(--lime);z-index:4;animation:aExFhLive 4s ease infinite}
@keyframes aExFhA{0%,14%{transform:translateX(0)}46%,74%{transform:translateX(-100%)}94%,100%{transform:translateX(0)}}
@keyframes aExFhB{0%,14%{transform:translateX(100%)}46%,74%{transform:translateX(0)}94%,100%{transform:translateX(100%)}}
@keyframes aExFhRing{0%,46%{box-shadow:none}56%,86%{box-shadow:0 0 0 3px rgba(var(--accent-rgb),.4)}95%,100%{box-shadow:none}}
@keyframes aExFhT1{0%,44%{opacity:1}48%,90%{opacity:0}96%,100%{opacity:1}}
@keyframes aExFhT2{0%,44%{opacity:0}48%,90%{opacity:1}96%,100%{opacity:0}}
@keyframes aExFhLive{0%,50%{opacity:0;transform:scale(.85)}58%,68%{opacity:1;transform:scale(1)}
  78%,100%{opacity:0;transform:scale(.85)}}

/* ---------------- auto-hide-header ----------------
   Ce que l'en-tete achete en partant est visible, deux lignes de plus
   entrent dans le cadre. Le trait pointille marque le seuil, il n'part
   qu'apres l'avoir franchi, a 40 pour cent. Au retour aucun seuil, le
   document remonte a 64 et il est revenu a 66, soit 80ms plus tard.
   Gele, on perd l'asymetrie, qui est tout le design. */
.ex-auto-hide-header{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-auto-hide-header .hd{position:absolute;left:0;right:0;top:0;height:24px;z-index:3;
  background:var(--lime);display:flex;align-items:center;gap:8px;padding:0 10px;
  animation:aExAhH 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-auto-hide-header .hd b{display:block;height:6px;border-radius:3px;background:var(--on-accent);opacity:.6}
.ex-auto-hide-header .hd .t{width:44px}
.ex-auto-hide-header .hd .m{width:14px;margin-left:auto}
.ex-auto-hide-header .art{position:absolute;left:10px;right:22px;top:30px;display:flex;
  flex-direction:column;gap:7px;animation:aExAhD 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-auto-hide-header .art i{display:block;height:8px;border-radius:4px;background:var(--s3)}
.ex-auto-hide-header .art i:nth-child(4n){width:64%}
.ex-auto-hide-header .gauge{position:absolute;right:7px;top:30px;bottom:8px;width:4px;border-radius:2px;
  background:var(--s1)}
.ex-auto-hide-header .thumb{position:absolute;left:0;right:0;top:0;height:34px;border-radius:2px;
  background:var(--s5);animation:aExAhT 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-auto-hide-header .thr{position:absolute;left:-5px;right:-5px;top:26px;height:1px;
  background:repeating-linear-gradient(90deg,var(--danger) 0 3px,transparent 3px 6px)}
.ex-auto-hide-header .lbl{position:absolute;right:15px;top:19px;z-index:4;
  font:700 7px/1 var(--mono);color:var(--danger);font-style:normal}
@keyframes aExAhH{0%,40%{transform:translateY(0)}54%,64%{transform:translateY(-100%)}
  66%,100%{transform:translateY(0)}}
@keyframes aExAhD{0%,10%{transform:translateY(0)}52%,64%{transform:translateY(-78px)}
  92%,100%{transform:translateY(0)}}
@keyframes aExAhT{0%,10%{transform:translateY(0)}52%,64%{transform:translateY(38px)}
  92%,100%{transform:translateY(0)}}

/* ---------------- scroll-restoration ----------------
   Le defilement est un EVENEMENT. La grille descend vraiment de 40px,
   on ouvre une fiche, on revient, et elle est toujours a 40 avec la
   vignette ouverte cerclee. La pastille page 2 reste allumee pendant
   tout l'aller retour, c'est le compte de pages charge que la consigne
   demande de restaurer en meme temps que l'offset.
   Gele, on ne voit qu'une grille decalee, sans savoir qui l'a decalee
   ni ce qui a ete preserve. */
.ex-scroll-restoration{position:relative;width:170px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-scroll-restoration .grd{position:absolute;left:10px;right:22px;top:10px;display:grid;
  grid-template-columns:1fr 1fr;gap:8px;animation:aExSrG 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-restoration .grd i{display:block;height:36px;border-radius:7px;background:var(--s2)}
.ex-scroll-restoration .hit{background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExSrHit 5.4s ease infinite}
.ex-scroll-restoration .gauge{position:absolute;right:7px;top:10px;bottom:10px;width:4px;
  border-radius:2px;background:var(--s1)}
.ex-scroll-restoration .thumb{position:absolute;left:0;right:0;top:0;height:40px;border-radius:2px;
  background:var(--s5);animation:aExSrT 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-restoration .pg2{position:absolute;left:10px;bottom:9px;z-index:4;padding:3px 7px;
  border-radius:5px;background:rgba(var(--accent-rgb),.2);
  font:700 7px/1 var(--mono);color:var(--lime);font-style:normal}
.ex-scroll-restoration .detail{position:absolute;inset:0;background:var(--s4);z-index:3;
  animation:aExSrD 5.4s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExSrG{0%,10%{transform:translateY(0)}26%,94%{transform:translateY(-40px)}
  99%,100%{transform:translateY(0)}}
@keyframes aExSrT{0%,10%{transform:translateY(0)}26%,94%{transform:translateY(30px)}
  99%,100%{transform:translateY(0)}}
@keyframes aExSrD{0%,36%{transform:translateX(100%)}48%,70%{transform:translateX(0)}
  82%,100%{transform:translateX(100%)}}
@keyframes aExSrHit{0%,30%{box-shadow:none}34%,88%{box-shadow:0 0 0 2px var(--ink)}
  94%,100%{box-shadow:none}}

/* ---------------- autosave ----------------
   La frappe s'arrete a 38 pour cent, la jauge du debounce ne demarre
   qu'a ce moment la et met exactement une seconde sur un cycle de 5s,
   puis l'ecriture part. C'est la PAUSE qui est le mecanisme, et sans
   elle le terme se reduirait a un texte qui s'ecrit et une coche.
   Quinze caracteres, quinze pas, largeur en ch. */
.ex-autosave{width:170px;height:148px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-autosave .tb{display:flex;align-items:center;gap:6px}
.ex-autosave .tb b{display:block;width:14px;height:14px;border-radius:4px;background:var(--s3)}
.ex-autosave .tb .sp{width:1px;height:12px;background:var(--line-2)}
.ex-autosave .doc{flex:1;padding:9px 10px;border-radius:8px;box-sizing:border-box;background:var(--s0);
  border:1px solid var(--line);font:600 11px/17px var(--mono);
  display:flex;flex-direction:column;gap:5px}
.ex-autosave .doc .p{display:block;height:6px;border-radius:3px;background:var(--s3)}
.ex-autosave .doc .p.s{width:72%}
.ex-autosave .typed{display:block;overflow:hidden;white-space:nowrap;color:var(--ink);width:0;
  animation:aExAsT 5s steps(15,end) infinite}
.ex-autosave .dbc{position:relative;height:3px;border-radius:2px;background:var(--s2);overflow:hidden}
.ex-autosave .dbc b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:var(--ink-3);animation:aExAsDb 5s linear infinite}
.ex-autosave .st{position:relative;height:14px}
.ex-autosave .st em{position:absolute;left:0;top:0;display:flex;align-items:center;gap:6px;
  font:600 8px/14px var(--mono);font-style:normal}
.ex-autosave .s1{color:var(--ink-3);animation:aExAsS1 5s steps(1,end) infinite}
.ex-autosave .s2{color:var(--lime);animation:aExAsS2 5s steps(1,end) infinite}
.ex-autosave .s1 i{display:block;width:9px;height:9px;border-radius:50%;box-sizing:border-box;
  border:1.5px solid var(--ink-3);border-top-color:transparent;animation:aExAsSp .8s linear infinite}
.ex-autosave .s2 i{display:block;width:9px;height:9px;border-radius:50%;background:var(--lime)}
@keyframes aExAsT{0%{width:0}38%,94%{width:15ch}98%,100%{width:0}}
@keyframes aExAsDb{0%,38%{width:0}58%{width:100%}58.001%,100%{width:0}}
@keyframes aExAsS1{0%,58%{opacity:0}60%,72%{opacity:1}74%,100%{opacity:0}}
@keyframes aExAsS2{0%,72%{opacity:0}74%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aExAsSp{to{transform:rotate(360deg)}}

/* ---------------- unsaved-guard ----------------
   La garde se tient sur la SORTIE, pas dans la page. Le lien Projets
   est active a 40 pour cent, la page amorce son depart, et la boite
   la repousse a 48. Trois boutons, enregistrer, abandonner, rester.
   Gele, on verrait une boite au dessus d'un formulaire sans savoir ce
   qui l'a declenchee ni ce qu'elle empeche. */
.ex-unsaved-guard{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-unsaved-guard .nav{position:absolute;left:0;right:0;top:0;height:24px;z-index:2;
  display:flex;align-items:center;gap:9px;padding:0 10px;box-sizing:border-box;
  background:var(--panel);border-bottom:1px solid var(--line)}
.ex-unsaved-guard .nav i{display:block;height:5px;border-radius:3px;background:var(--s4)}
.ex-unsaved-guard .nav .bk{width:30px;background:var(--lime);
  animation:aExUgNav 4.8s steps(1,end) infinite}
.ex-unsaved-guard .nav .ti{width:44px}
.ex-unsaved-guard .page{position:absolute;left:0;right:0;top:24px;bottom:0;padding:11px;
  box-sizing:border-box;display:flex;flex-direction:column;gap:8px;
  animation:aExUgPage 4.8s cubic-bezier(.4,0,.2,1) infinite}
.ex-unsaved-guard .fld{position:relative;display:block;height:26px;border-radius:7px;
  box-sizing:border-box;background:var(--s1);border:1px solid var(--line-2)}
.ex-unsaved-guard .dot{position:absolute;right:9px;top:9px;width:7px;height:7px;border-radius:50%;
  background:var(--lime);animation:aExUgDot 4.8s ease infinite}
.ex-unsaved-guard .ln{display:block;height:8px;border-radius:4px;background:var(--s2)}
.ex-unsaved-guard .dlg{position:absolute;left:13px;right:13px;top:44px;padding:10px;
  border-radius:9px;box-sizing:border-box;background:var(--card);border:1px solid var(--line-2);
  z-index:4;box-shadow:0 14px 30px rgba(var(--shadow-rgb),.6);
  display:flex;flex-direction:column;gap:9px;
  animation:aExUgD 4.8s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-unsaved-guard .dlg .t{height:6px;width:66%;border-radius:3px;background:var(--ink);opacity:.8}
.ex-unsaved-guard .dlg .m{height:5px;border-radius:3px;background:var(--s5)}
.ex-unsaved-guard .btns{display:flex;gap:6px}
.ex-unsaved-guard .btns b{display:block;flex:1;height:16px;border-radius:5px;background:var(--s3)}
.ex-unsaved-guard .btns .pr{background:var(--lime)}
@keyframes aExUgDot{0%,10%{opacity:0;transform:scale(.4)}22%,92%{opacity:1;transform:scale(1)}
  98%,100%{opacity:0;transform:scale(.4)}}
@keyframes aExUgNav{0%,38%{background:var(--lime)}40%,52%{background:var(--ink)}
  56%,100%{background:var(--lime)}}
@keyframes aExUgPage{0%,40%{transform:translateX(0)}48%{transform:translateX(-10px)}
  58%,100%{transform:translateX(0)}}
@keyframes aExUgD{0%,46%{opacity:0;transform:translateY(14px) scale(.94)}
  60%,90%{opacity:1;transform:translateY(0) scale(1)}
  97%,100%{opacity:0;transform:translateY(14px) scale(.94)}}

/* ---------------- inline-validation ----------------
   Toute la regle porte sur le MOMENT. On tape jusqu'a 34 pour cent et
   il ne se passe rien, c'est volontaire. Le champ est quitte a 38 et
   le verdict tombe. La correction reprend a 58 et cette fois la
   verification est directe, l'erreur part a 74 pendant la frappe.
   Gele, il ne reste qu'un champ rouge devenu vert, c'est a dire rien
   du terme. Les trois pastilles du bas nomment les trois moments. */
.ex-inline-validation{width:170px;height:152px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-inline-validation .lb{height:5px;width:36px;border-radius:3px;background:var(--ink-3)}
.ex-inline-validation .ok{display:block;height:26px;border-radius:7px;box-sizing:border-box;
  background:var(--s1);border:1px solid var(--line-2)}
.ex-inline-validation .fld{display:flex;align-items:center;gap:2px;height:28px;padding:0 9px;
  box-sizing:border-box;border-radius:7px;background:var(--s1);border:1.5px solid var(--line-2);
  font:600 11px/28px var(--mono);animation:aExIvF 5.4s steps(1,end) infinite}
.ex-inline-validation .typed{display:block;overflow:hidden;white-space:nowrap;color:var(--ink);width:0;
  animation:aExIvT 5.4s steps(9,end) infinite}
.ex-inline-validation .caret{display:block;flex:0 0 auto;width:1.5px;height:14px;background:var(--lime);
  animation:aExIvC .9s steps(1,end) infinite}
.ex-inline-validation .msg{display:flex;align-items:center;gap:6px;height:11px;
  animation:aExIvM 5.4s ease infinite}
.ex-inline-validation .msg i{display:block;width:8px;height:8px;border-radius:50%;
  background:var(--danger);flex:0 0 auto}
.ex-inline-validation .msg b{display:block;height:5px;width:88px;border-radius:3px;
  background:var(--danger);opacity:.7}
.ex-inline-validation .ph{display:flex;gap:5px;margin-top:auto}
.ex-inline-validation .ph em{flex:1;height:12px;border-radius:4px;background:var(--s1);
  font:700 6px/12px var(--mono);text-align:center;color:var(--ink-3);font-style:normal}
.ex-inline-validation .p1{animation:aExIvP1 5.4s steps(1,end) infinite}
.ex-inline-validation .p2{animation:aExIvP2 5.4s steps(1,end) infinite}
.ex-inline-validation .p3{animation:aExIvP3 5.4s steps(1,end) infinite}
@keyframes aExIvT{0%,4%{width:0}34%,52%{width:9ch}58%{width:6ch}74%,94%{width:12ch}98%,100%{width:0}}
@keyframes aExIvF{0%,36%{border-color:var(--line-2)}38%,72%{border-color:var(--danger)}
  74%,94%{border-color:var(--lime)}97%,100%{border-color:var(--line-2)}}
@keyframes aExIvM{0%,36%{opacity:0}40%,72%{opacity:1}76%,100%{opacity:0}}
@keyframes aExIvC{0%,50%{opacity:1}51%,100%{opacity:0}}
@keyframes aExIvP1{0%,4%{background:var(--s1);color:var(--ink-3)}
  8%,34%{background:var(--s4);color:var(--void)}38%,100%{background:var(--s1);color:var(--ink-3)}}
@keyframes aExIvP2{0%,36%{background:var(--s1);color:var(--ink-3)}
  40%,54%{background:var(--danger);color:var(--void)}58%,100%{background:var(--s1);color:var(--ink-3)}}
@keyframes aExIvP3{0%,56%{background:var(--s1);color:var(--ink-3)}
  60%,92%{background:var(--lime);color:var(--on-accent)}96%,100%{background:var(--s1);color:var(--ink-3)}}

/* ---------------- error-summary ----------------
   Le SAUT est le sujet, pas la liste. La soumission echoue a 20 pour
   cent, le recapitulatif prend le focus a 28, puis a 54 on active sa
   deuxieme ligne et le focus part se poser sur le champ qu'elle nomme,
   tout en bas du formulaire.
   Gele, on aurait un bloc rouge qui liste des erreurs, ce que fait
   n'importe quel message, et le lien qui saute disparaitrait. */
.ex-error-summary{position:relative;width:170px;height:158px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-error-summary .slot{position:relative;height:38px}
.ex-error-summary .sum{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;gap:6px;
  padding:8px 10px;box-sizing:border-box;border-radius:8px;background:rgba(var(--danger-rgb),.12);
  border-left:3px solid var(--danger);animation:aExEsSum 5.2s cubic-bezier(.3,.9,.3,1) infinite}
.ex-error-summary .sum i{display:block;height:6px;border-radius:3px;background:var(--danger);opacity:.72}
.ex-error-summary .sum i:last-child{width:64%}
.ex-error-summary .lnk{animation:aExEsLnk 5.2s steps(1,end) infinite}
.ex-error-summary .fld{display:block;height:24px;border-radius:7px;box-sizing:border-box;
  background:var(--s1);border:1px solid var(--line-2)}
.ex-error-summary .bad{border-color:var(--danger);animation:aExEsBad 5.2s ease infinite}
.ex-error-summary .sub{margin-top:auto;height:22px;border-radius:7px;background:var(--lime);
  animation:aExEsSub 5.2s steps(1,end) infinite}
@keyframes aExEsSum{0%,20%{opacity:0;transform:translateY(-9px)}
  28%,92%{opacity:1;transform:translateY(0)}98%,100%{opacity:0;transform:translateY(-9px)}}
@keyframes aExEsLnk{0%,50%{opacity:.72;box-shadow:none}
  54%,68%{opacity:1;box-shadow:0 0 0 2px rgba(var(--danger-rgb),.55)}
  72%,100%{opacity:.72;box-shadow:none}}
@keyframes aExEsBad{0%,56%{box-shadow:none}62%,90%{box-shadow:0 0 0 3px rgba(var(--danger-rgb),.35)}
  96%,100%{box-shadow:none}}
@keyframes aExEsSub{0%,14%{filter:brightness(1)}16%,22%{filter:brightness(1.3)}
  26%,100%{filter:brightness(1)}}

/* ---------------- input-mask ----------------
   Les espaces ne sont PAS tapes, ils s'ouvrent tout seuls apres le
   quatrieme, le huitieme et le douzieme chiffre pendant que la frappe
   continue sans interruption. La ligne du bas montre ce qui part
   reellement, seize chiffres colles, ce qui est la seconde moitie
   du terme et la raison de ne jamais stocker la valeur masquee.
   Gele, on verrait seize chiffres et trois espaces sans savoir
   lesquels ont ete tapes. */
.ex-input-mask{width:172px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-input-mask .lb{height:5px;width:44px;border-radius:3px;background:var(--ink-3)}
.ex-input-mask .fld{display:flex;align-items:center;height:32px;padding:0 10px;box-sizing:border-box;
  border-radius:8px;background:var(--s1);border:1px solid var(--lime);
  font:700 11px/32px var(--mono);color:var(--lime)}
.ex-input-mask .fld b{display:block;opacity:0;animation:aExImD 5.6s steps(1,end) infinite}
.ex-input-mask .fld .sep{display:block;width:0;height:2px;border-radius:2px;background:var(--ink-3);
  animation:aExImS 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-input-mask .sent{display:flex;align-items:center;gap:8px;margin-top:auto;padding:6px 9px;
  border-radius:7px;box-sizing:border-box;background:var(--void);border:1px solid var(--line)}
.ex-input-mask .sent em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-input-mask .sent i{display:block;flex:1;height:5px;border-radius:3px;background:var(--s4);
  animation:aExImSent 5.6s ease infinite}
@keyframes aExImD{0%{opacity:0}3%,90%{opacity:1}94%,100%{opacity:0}}
@keyframes aExImS{0%,20%{width:0}24%,90%{width:6px}94%,100%{width:0}}
@keyframes aExImSent{0%,62%{opacity:0}70%,90%{opacity:1}96%,100%{opacity:0}}

/* ---------------- wizard-resume ----------------
   Le sujet est le DEPART et le RETOUR. On avance jusqu'a l'etape 3, on
   part a 44 pour cent et le panneau disparait completement, puis on
   revient a 62 et le tunnel se retrouve exactement a l'etape 3, avec
   les deux premieres toujours pleines.
   Gele, on verrait une barre de progression, ce que fait n'importe
   quel formulaire multi etapes sans reprise. */
.ex-wizard-resume{position:relative;width:170px;height:144px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:11px}
.ex-wizard-resume .rail{position:relative;display:flex;align-items:center;justify-content:space-between}
.ex-wizard-resume .rail::before{content:"";position:absolute;left:6px;right:6px;top:5px;height:1.5px;
  background:var(--s2)}
.ex-wizard-resume .rail i{position:relative;display:block;width:12px;height:12px;border-radius:50%;
  background:var(--s3)}
.ex-wizard-resume .s1{animation:aExWzS 5.6s steps(1,end) infinite}
.ex-wizard-resume .s2{animation:aExWzS 5.6s steps(1,end) infinite;animation-delay:.3s}
.ex-wizard-resume .s3{animation:aExWzOn 5.6s steps(1,end) infinite}
.ex-wizard-resume .lb{font:700 8px/1 var(--mono);color:var(--ink-2);font-style:normal}
.ex-wizard-resume .bdy{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden}
.ex-wizard-resume .pane{position:absolute;inset:9px;display:flex;flex-direction:column;gap:7px;
  animation:aExWzP 5.6s ease infinite}
.ex-wizard-resume .pane i{display:block;height:18px;border-radius:6px;background:var(--s3)}
.ex-wizard-resume .pane i:last-child{width:62%}
.ex-wizard-resume .away{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal;
  animation:aExWzA 5.6s ease infinite}
@keyframes aExWzS{0%,4%{background:var(--s3)}10%,42%{background:var(--lime)}
  62%,90%{background:var(--lime)}96%,100%{background:var(--s3)}}
@keyframes aExWzOn{0%,20%{background:var(--s3);box-shadow:none}
  28%,42%{background:var(--lime);box-shadow:0 0 0 4px rgba(var(--accent-rgb),.22)}
  62%,90%{background:var(--lime);box-shadow:0 0 0 4px rgba(var(--accent-rgb),.22)}
  96%,100%{background:var(--s3);box-shadow:none}}
@keyframes aExWzP{0%,42%{opacity:1}48%,58%{opacity:0}64%,100%{opacity:1}}
@keyframes aExWzA{0%,44%{opacity:0}50%,58%{opacity:1}64%,100%{opacity:0}}

/* ---------------- load-more ----------------
   Le compteur passe de 40 a 60 sur 312 exactement quand les deux
   lignes arrivent, et l'anneau de focus RESTE sur le bouton apres le
   chargement. Un spinner qui remplacerait le bouton emporterait le
   focus avec lui, ce que la consigne interdit.
   Gele, on aurait une liste et un bouton, sans le denominateur qui
   est precisement ce que le defilement infini ne peut pas donner. */
.ex-load-more{width:170px;height:156px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:7px}
.ex-load-more .rw{display:flex;align-items:center;gap:9px;height:20px;padding:0 9px;
  box-sizing:border-box;border-radius:6px;background:var(--s1)}
.ex-load-more .rw i{flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-load-more .slot{position:relative;height:47px}
.ex-load-more .nu{position:absolute;left:0;right:0;display:flex;align-items:center;gap:9px;height:20px;
  padding:0 9px;box-sizing:border-box;border-radius:6px;background:var(--s1);
  animation:aExLmN 4.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-load-more .nu i{flex:1;height:5px;border-radius:3px;background:var(--lime);opacity:.6}
.ex-load-more .n2{top:27px;animation-delay:.08s}
.ex-load-more .ft{display:flex;align-items:center;gap:9px;margin-top:auto}
.ex-load-more .btn{flex:0 0 auto;width:76px;height:22px;border-radius:8px;box-sizing:border-box;
  border:1.5px solid var(--lime);animation:aExLmB 4.8s ease infinite}
.ex-load-more .cnt{position:relative;flex:1;height:12px;text-align:right;
  font:700 9px/12px var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-3)}
.ex-load-more .cnt u{position:absolute;right:0;top:0;text-decoration:none}
.ex-load-more .c1{animation:aExLmC1 4.8s steps(1,end) infinite}
.ex-load-more .c2{animation:aExLmC2 4.8s steps(1,end) infinite}
@keyframes aExLmN{0%,34%{opacity:0;transform:translateY(-10px)}
  52%,92%{opacity:1;transform:translateY(0)}98%,100%{opacity:0;transform:translateY(-10px)}}
@keyframes aExLmB{0%,20%{background:transparent;box-shadow:none}
  26%,34%{background:rgba(var(--accent-rgb),.22);box-shadow:0 0 0 2px var(--lime)}
  40%,92%{background:transparent;box-shadow:0 0 0 2px var(--lime)}
  98%,100%{background:transparent;box-shadow:none}}
@keyframes aExLmC1{0%,46%{opacity:1}50%,94%{opacity:0}98%,100%{opacity:1}}
@keyframes aExLmC2{0%,46%{opacity:0}50%,94%{opacity:1}98%,100%{opacity:0}}

/* ---------------- virtualisation ----------------
   Les lignes en pointille sont celles qui n'EXISTENT PAS dans le DOM,
   au dessus et au dessous de la fenetre. Le curseur est minuscule
   parce que les espaceurs reproduisent la hauteur des 5000, et c'est
   exactement ce qu'ils achetent, une barre de defilement honnete pour
   des lignes absentes. Le compteur dit 12 rendues sur 5000.
   Aller retour et non enroulement, sinon le curseur sauterait en haut
   a chaque tour, ce qui se voit. */
.ex-virtualisation{width:170px;height:148px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:8px}
.ex-virtualisation .hd{display:flex;align-items:center;gap:8px}
.ex-virtualisation .hd .t{height:7px;width:46px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-virtualisation .cnt{margin-left:auto;font:700 8px/1 var(--mono);color:var(--lime);font-style:normal}
.ex-virtualisation .bdy{display:flex;gap:9px;flex:1}
.ex-virtualisation .port{position:relative;flex:1;overflow:hidden;border-radius:7px;background:var(--s1)}
.ex-virtualisation .strip{position:absolute;left:6px;right:6px;top:6px;display:flex;
  flex-direction:column;gap:6px;animation:aExVrt 6.4s ease-in-out infinite}
.ex-virtualisation .strip i{display:block;height:13px;border-radius:5px;background:var(--s3);flex:0 0 auto}
.ex-virtualisation .gh{background:transparent;border:1px dashed var(--s4);box-sizing:border-box}
.ex-virtualisation .sbar{position:relative;flex:0 0 auto;width:7px;border-radius:4px;background:var(--s1)}
.ex-virtualisation .sbar i{position:absolute;left:0;right:0;top:0;height:12px;border-radius:4px;
  background:var(--lime);display:block;animation:aExVrtS 6.4s ease-in-out infinite}
@keyframes aExVrt{0%,100%{transform:translateY(0)}50%{transform:translateY(-78px)}}
@keyframes aExVrtS{0%,100%{transform:translateY(0)}50%{transform:translateY(85px)}}

/* ---------------- select-all-matching ----------------
   DEUX etapes distinctes, jamais une case ambigue. A 18 pour cent la
   case d'en-tete prend les quatre messages visibles et le compte dit
   4. Le bandeau propose alors l'extension EN NOMMANT son nombre reel,
   et le compte ne passe a 3200 qu'a 62, apres une seconde action
   deliberee.
   Gele, on verrait des cases cochees et un bandeau, sans savoir qu'il
   s'agit de deux decisions ni ce que la seconde engage. */
.ex-select-all-matching{width:172px;height:150px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:7px}
.ex-select-all-matching .hd{display:flex;align-items:center;gap:9px;height:16px}
.ex-select-all-matching .box{flex:0 0 auto;width:13px;height:13px;border-radius:3px;box-sizing:border-box;
  border:1.5px solid var(--s5);animation:aExSamBox 5.6s steps(1,end) infinite}
.ex-select-all-matching .cnt{position:relative;flex:1;height:12px;text-align:right;
  font:700 9px/12px var(--mono);font-variant-numeric:tabular-nums}
.ex-select-all-matching .cnt u{position:absolute;right:0;top:0;text-decoration:none}
.ex-select-all-matching .k1{color:var(--lime);animation:aExSamK1 5.6s steps(1,end) infinite}
.ex-select-all-matching .k2{color:var(--a2);animation:aExSamK2 5.6s steps(1,end) infinite}
.ex-select-all-matching .banner{display:flex;align-items:center;gap:8px;height:22px;padding:0 9px;
  box-sizing:border-box;border-radius:6px;background:rgba(var(--a2-rgb),.16);
  border:1px solid rgba(var(--a2-rgb),.5);animation:aExSamB 5.6s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-select-all-matching .banner em{font:600 7px/1 var(--mono);color:var(--a2);font-style:normal}
.ex-select-all-matching .banner b{display:block;width:32px;height:12px;border-radius:4px;
  background:var(--a2);margin-left:auto;animation:aExSamBtn 5.6s steps(1,end) infinite}
.ex-select-all-matching .rw{display:flex;align-items:center;gap:9px;height:19px;padding:0 9px;
  box-sizing:border-box;border-radius:6px;background:var(--s1);
  animation:aExSamRow 5.6s steps(1,end) infinite}
.ex-select-all-matching .rw:nth-of-type(4){animation-delay:.08s}
.ex-select-all-matching .rw:nth-of-type(5){animation-delay:.16s}
.ex-select-all-matching .rw i{display:block;width:11px;height:11px;border-radius:3px;
  background:var(--s4);flex:0 0 auto}
.ex-select-all-matching .rw b{display:block;flex:1;height:5px;border-radius:3px;background:var(--s5)}
@keyframes aExSamRow{0%,14%{background:var(--s1)}20%,92%{background:rgba(var(--accent-rgb),.14)}
  97%,100%{background:var(--s1)}}
@keyframes aExSamBox{0%,14%{border-color:var(--s5);background:transparent}
  20%,92%{border-color:var(--lime);background:var(--lime)}
  97%,100%{border-color:var(--s5);background:transparent}}
@keyframes aExSamK1{0%,16%{opacity:0}22%,58%{opacity:1}62%,100%{opacity:0}}
@keyframes aExSamK2{0%,58%{opacity:0}62%,92%{opacity:1}97%,100%{opacity:0}}
@keyframes aExSamB{0%,26%{opacity:0;transform:translateY(-9px)}
  38%,92%{opacity:1;transform:translateY(0)}97%,100%{opacity:0;transform:translateY(-9px)}}
@keyframes aExSamBtn{0%,54%{opacity:.6}56%,64%{opacity:1}68%,100%{opacity:.6}}

/* ---------------- filter-chips ----------------
   Une boutique. Retirer la puce prix elargit l ensemble, le compte
   passe de 18 a 42 dans la MEME image, et une vignette apparait dans
   la grille parce qu elle correspond maintenant. Les trois choses
   sont un seul evenement vu a trois endroits.
   Gele, on verrait deux puces et une grille sans savoir que l une
   commande l autre. */
.ex-filter-chips{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-filter-chips .tpp2{display:flex;align-items:center;gap:7px}
.ex-filter-chips .chps{display:flex;gap:6px}
.ex-filter-chips .chps i{display:block;height:18px;border-radius:9px;background:var(--s3)}
.ex-filter-chips .q1{width:42px}
.ex-filter-chips .q2{width:38px;background:var(--lime);transform-origin:left center;
  animation:aExFcChip 4.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-filter-chips .cnt{position:relative;flex:1;height:12px;text-align:right;
  font:700 9px/12px var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-3)}
.ex-filter-chips .cnt u{position:absolute;right:0;top:0;text-decoration:none}
.ex-filter-chips .n1{animation:aExFcN1 4.8s steps(1,end) infinite}
.ex-filter-chips .n2{animation:aExFcN2 4.8s steps(1,end) infinite}
.ex-filter-chips .grd{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ex-filter-chips .grd i{display:block;height:34px;border-radius:7px;background:var(--s2)}
.ex-filter-chips .nu{background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExFcNew 4.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-filter-chips .cur{left:96px;top:2px;animation:aExFcCur 4.8s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExFcChip{0%,44%{transform:scaleX(1);opacity:1}
  56%,86%{transform:scaleX(.15);opacity:0}92%,100%{transform:scaleX(1);opacity:1}}
@keyframes aExFcN1{0%,44%{opacity:1}46%,88%{opacity:0}92%,100%{opacity:1}}
@keyframes aExFcN2{0%,44%{opacity:0}46%,88%{opacity:1}92%,100%{opacity:0}}
@keyframes aExFcNew{0%,44%{opacity:0;transform:scale(.9)}58%,86%{opacity:1;transform:scale(1)}
  92%,100%{opacity:0;transform:scale(.9)}}
@keyframes aExFcCur{0%,20%{transform:translate(12px,10px);opacity:0}
  30%{transform:translate(0,0);opacity:1}42%{transform:translate(0,3px);opacity:1}
  54%{transform:translate(0,0);opacity:1}68%{transform:translate(12px,10px);opacity:0}
  68.001%,100%{transform:translate(12px,10px);opacity:0}}

/* ---------------- table-density ----------------
   Un tableau de factures. SEUL le remplissage vertical change, les
   barres de texte gardent exactement la meme hauteur et la meme
   largeur, et le repere bleu montre que la ligne du haut ne bouge pas
   d un pixel pendant la bascule. Une cinquieme ligne entre dans le
   cadre, ce qui est le gain reel.
   Gele, on aurait un tableau, sans savoir ce qui a change ni ce qui a
   ete preserve. */
.ex-table-density{width:172px;height:150px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;display:flex;flex-direction:column;gap:9px}
.ex-table-density .bar{display:flex;align-items:center;gap:8px}
.ex-table-density .bar .t{height:7px;width:44px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-table-density .tg{position:relative;width:52px;height:16px;border-radius:5px;margin-left:auto;
  background:var(--s1);border:1px solid var(--line-2);box-sizing:border-box}
.ex-table-density .tg b{position:absolute;top:2px;left:2px;width:23px;height:10px;border-radius:3px;
  background:var(--lime);animation:aExTdTg 4.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-table-density .tbl{position:relative;flex:1;overflow:hidden}
.ex-table-density .anch{position:absolute;left:0;top:0;width:2px;height:10px;border-radius:1px;
  background:var(--a2);z-index:2}
.ex-table-density .r{display:flex;align-items:center;gap:8px;margin-left:7px;padding:9px 0;
  border-bottom:1px solid var(--line);animation:aExTdR 4.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-table-density .r i{display:block;height:6px;border-radius:3px;flex:0 0 auto}
.ex-table-density .r .c1{width:32px;background:var(--s5)}
.ex-table-density .r .c2{width:48px;background:var(--s3)}
.ex-table-density .r .c3{width:24px;margin-left:auto;background:var(--s4)}
@keyframes aExTdR{0%,16%{padding-top:9px;padding-bottom:9px}
  50%,86%{padding-top:3px;padding-bottom:3px}98%,100%{padding-top:9px;padding-bottom:9px}}
@keyframes aExTdTg{0%,16%{transform:translateX(0)}50%,86%{transform:translateX(23px)}
  98%,100%{transform:translateX(0)}}

/* ---------------- permission-priming ----------------
   Une application photo. L ecran interne explique a 14 pour cent, on
   appuie sur Autoriser a 42, et c est SEULEMENT ensuite, a 58, que la
   boite du systeme apparait. On ne depense la question couteuse que
   sur quelqu un qui a deja dit oui, parce qu un refus natif ne se
   rattrape pas.
   Gele, on aurait deux boites empilees sans savoir laquelle appelle
   l autre, ce qui est exactement le contraire du terme. */
.ex-permission-priming{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-permission-priming .app{position:absolute;inset:0;padding:11px;display:grid;
  grid-template-columns:1fr 1fr;gap:8px;align-content:start}
.ex-permission-priming .app i{display:block;height:40px;border-radius:7px;background:var(--s2)}
.ex-permission-priming .soft{position:absolute;left:10px;right:10px;top:14px;padding:11px;
  border-radius:11px;box-sizing:border-box;background:var(--card);
  border:1px solid rgba(var(--accent-rgb),.45);z-index:2;
  box-shadow:0 12px 26px rgba(var(--shadow-rgb),.5);display:flex;flex-direction:column;gap:8px;
  animation:aExPpSoft 5.6s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-permission-priming .soft .ic{width:22px;height:18px;border-radius:5px;background:var(--lime)}
.ex-permission-priming .soft .t{height:6px;width:64%;border-radius:3px;background:var(--ink);opacity:.8}
.ex-permission-priming .soft .m{height:5px;border-radius:3px;background:var(--s5)}
.ex-permission-priming .soft .bt{display:flex;gap:7px;margin-top:2px}
.ex-permission-priming .soft .bt b{display:block;flex:1;height:16px;border-radius:5px;background:var(--s3)}
.ex-permission-priming .soft .yes{background:var(--lime);animation:aExPpYes 5.6s steps(1,end) infinite}
.ex-permission-priming .sysd{position:absolute;left:24px;right:24px;top:44px;padding:10px;
  border-radius:10px;box-sizing:border-box;background:var(--void);border:1px solid var(--s4);z-index:4;
  box-shadow:0 16px 30px rgba(var(--shadow-rgb),.65);display:flex;flex-direction:column;gap:8px;
  animation:aExPpSys 5.6s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-permission-priming .sysd .t{height:5px;width:74%;border-radius:3px;background:var(--ink-2)}
.ex-permission-priming .sysd .bt{display:flex;gap:6px}
.ex-permission-priming .sysd .bt b{display:block;flex:1;height:14px;border-radius:4px;background:var(--s3)}
@keyframes aExPpSoft{0%,8%{opacity:0;transform:translateY(14px)}
  20%,52%{opacity:1;transform:translateY(0)}
  64%,92%{opacity:.22;transform:scale(.96)}98%,100%{opacity:0;transform:translateY(14px)}}
@keyframes aExPpYes{0%,40%{filter:brightness(1)}42%,50%{filter:brightness(1.4)}
  54%,100%{filter:brightness(1)}}
@keyframes aExPpSys{0%,56%{opacity:0;transform:scale(.9)}
  68%,90%{opacity:1;transform:scale(1)}97%,100%{opacity:0;transform:scale(.9)}}

/* ---------------- quota-meter ----------------
   Une page de facturation. Les trois nombres sont ensemble, consomme,
   total et date de remise a zero, jamais un pourcentage seul. Le trait
   rouge marque les 80 pour cent et la barre change de couleur EN LE
   FRANCHISSANT, ce qui donne une cause visible au changement.
   Gele, on aurait une barre remplie sans savoir contre quoi, ni ce
   qui reste, ni quand elle se libere. */
.ex-quota-meter{width:172px;height:146px;border-radius:11px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line-2);padding:11px;display:flex;flex-direction:column;gap:10px}
.ex-quota-meter .hd{height:7px;width:52px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-quota-meter .num{display:flex;align-items:baseline;gap:6px}
.ex-quota-meter .used{position:relative;width:38px;height:24px;
  font:800 18px/24px var(--mono);font-variant-numeric:tabular-nums;color:var(--lime)}
.ex-quota-meter .used u{position:absolute;left:0;top:0;text-decoration:none}
.ex-quota-meter .u1{animation:aExQmU1 5.2s steps(1,end) infinite}
.ex-quota-meter .u2{animation:aExQmU2 5.2s steps(1,end) infinite}
.ex-quota-meter .tot{font:700 10px/24px var(--mono);color:var(--ink-3);font-style:normal}
.ex-quota-meter .rst{margin-left:auto;font:600 8px/24px var(--mono);color:var(--ink-3);font-style:normal}
.ex-quota-meter .mbar{position:relative;height:12px;border-radius:6px;background:var(--s1);overflow:hidden}
.ex-quota-meter .mbar b{position:absolute;inset:0;background:var(--lime);transform-origin:left;
  animation:aExQmBar 5.2s cubic-bezier(.4,0,.6,1) infinite}
.ex-quota-meter .thr{position:absolute;left:80%;top:-4px;bottom:-4px;width:2px;margin-left:-1px;z-index:2;
  background:repeating-linear-gradient(180deg,var(--danger) 0 3px,transparent 3px 6px)}
.ex-quota-meter .lgd{display:flex;gap:8px;margin-top:auto}
.ex-quota-meter .lgd i{display:block;flex:1;height:8px;border-radius:4px;background:var(--s1)}
@keyframes aExQmBar{0%,6%{transform:scaleX(.42);background:var(--lime)}
  50%{transform:scaleX(.79);background:var(--lime)}
  54%,92%{transform:scaleX(.94);background:var(--danger)}
  100%{transform:scaleX(.42);background:var(--lime)}}
@keyframes aExQmU1{0%,50%{opacity:1}54%,94%{opacity:0}98%,100%{opacity:1}}
@keyframes aExQmU2{0%,50%{opacity:0}54%,94%{opacity:1}98%,100%{opacity:0}}

/* ---------------- split-text ----------------
   Un titre de hero decoupe en trois lignes, chacune dans une boite a
   sa taille exacte en overflow hidden. Rien ne s estompe, la ligne est
   dehors puis dedans, et c est pour cela que le bord reste net.
   45ms entre chaque ligne, dans la fourchette de la consigne.
   Gele, on verrait trois lignes de titre, sans la boite qui les
   decoupe ni la cascade qui les ordonne. */
.ex-split-text{width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:13px;display:flex;flex-direction:column;justify-content:center;gap:5px}
.ex-split-text .m{display:block;overflow:hidden;height:24px}
.ex-split-text .m b{display:block;height:24px;border-radius:5px;background:var(--lime);
  animation:aExStLine 4.2s cubic-bezier(.2,.9,.25,1) infinite}
.ex-split-text .m:nth-child(2) b{width:76%;animation-delay:.045s}
.ex-split-text .m:nth-child(3) b{width:52%;animation-delay:.09s}
.ex-split-text .sub{margin-top:8px;height:7px;width:64%;border-radius:4px;background:var(--s4);
  animation:aExStSub 4.2s ease infinite}
@keyframes aExStLine{0%,10%{transform:translateY(100%)}34%,84%{transform:translateY(0)}
  96%,100%{transform:translateY(-100%)}}
@keyframes aExStSub{0%,30%{opacity:0}44%,84%{opacity:1}94%,100%{opacity:0}}

/* ---------------- kinetic-type ----------------
   Trois mots sur trois temps de 450ms, dans la fourchette de la
   consigne. Chacun a sa taille et sa graisse, et c est le CONTRASTE
   entre eux qui est le message. L espace est reserve d avance, donc
   aucune arrivee ne deplace celles qui sont deja posees.
   Gele, on aurait trois mots empiles, ce qui est une composition et
   non un mouvement. */
.ex-kinetic-type{width:170px;height:140px;border-radius:11px;overflow:hidden;background:var(--void);
  padding:13px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:4px}
.ex-kinetic-type .w{display:block;border-radius:5px;animation:aExKt 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-kinetic-type .w1{width:76px;height:16px;background:var(--s5)}
.ex-kinetic-type .w2{width:34px;height:11px;background:var(--s4);animation-delay:.45s}
.ex-kinetic-type .w3{width:118px;height:30px;background:var(--lime);animation-delay:.9s}
.ex-kinetic-type .beat{display:flex;gap:6px;margin-top:10px}
.ex-kinetic-type .beat i{display:block;width:7px;height:7px;border-radius:50%;background:var(--s2);
  animation:aExKtBeat 4.6s steps(1,end) infinite}
.ex-kinetic-type .beat i:nth-child(2){animation-delay:.45s}
.ex-kinetic-type .beat i:nth-child(3){animation-delay:.9s}
@keyframes aExKt{0%,6%{transform:translateY(14px) scale(.88);opacity:0}
  22%,82%{transform:translateY(0) scale(1);opacity:1}
  94%,100%{transform:translateY(-10px) scale(.94);opacity:0}}
@keyframes aExKtBeat{0%,6%{background:var(--s2)}10%,82%{background:var(--lime)}
  94%,100%{background:var(--s2)}}

/* ---------------- spotlight ----------------
   Le halo porte 180ms de RETARD sur le curseur, et c est ce retard qui
   le fait lire comme une lumiere ayant du poids plutot qu une forme
   collee au pointeur. Les trois cartes s allument a son passage, une
   lumiere qui traverse une surface inerte n a rien eclaire.
   Gele, on aurait un degrade pose sur une grille. */
.ex-spotlight{position:relative;width:172px;height:158px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:11px}
.ex-spotlight .glow{position:absolute;width:130px;height:130px;left:-46px;top:-38px;border-radius:50%;
  background:radial-gradient(circle,rgba(var(--accent-rgb),.36),transparent 66%);z-index:1;
  animation:aExSpG 5s ease-in-out infinite;animation-delay:.18s}
.ex-spotlight .grd{position:relative;z-index:2;display:flex;flex-direction:column;gap:8px}
.ex-spotlight .cd{display:flex;flex-direction:column;gap:6px;padding:9px;border-radius:9px;
  box-sizing:border-box;background:rgba(var(--ink-rgb),.04);border:1px solid rgba(var(--ink-rgb),.1);
  animation:aExSpCd 5s ease-in-out infinite}
.ex-spotlight .cd:nth-child(2){animation-delay:.5s}
.ex-spotlight .cd:nth-child(3){animation-delay:1s}
.ex-spotlight .cd .t{height:7px;width:54%;border-radius:4px;background:rgba(var(--ink-rgb),.7)}
.ex-spotlight .cd .l{height:5px;border-radius:3px;background:rgba(var(--ink-rgb),.22)}
.ex-spotlight .cur{left:0;top:0;z-index:3;animation:aExSpC 5s ease-in-out infinite}
@keyframes aExSpG{0%,100%{transform:translate(6px,4px)}50%{transform:translate(120px,116px)}}
@keyframes aExSpC{0%,100%{transform:translate(52px,42px)}50%{transform:translate(166px,154px)}}
@keyframes aExSpCd{0%,14%{border-color:rgba(var(--ink-rgb),.1);background:rgba(var(--ink-rgb),.04)}
  30%,44%{border-color:rgba(var(--accent-rgb),.5);background:rgba(var(--accent-rgb),.09)}
  60%,100%{border-color:rgba(var(--ink-rgb),.1);background:rgba(var(--ink-rgb),.04)}}

/* ---------------- gradient-drift ----------------
   Trois taches sur 7s, 9s et 11s. Aucun petit diviseur commun, donc le
   motif met 693 secondes a se repeter, soit onze minutes, et personne
   ne reconnait jamais une position deja vue. Les trois jauges du bas
   montrent les trois periodes qui defilent a des vitesses differentes,
   c est cette desynchronisation qui est le metier du terme.
   Gele, on aurait un joli fond, sans la raison pour laquelle il ne
   lasse pas. */
.ex-gradient-drift{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--stage)}
.ex-gradient-drift .b{position:absolute;width:86px;height:86px;border-radius:50%;filter:blur(20px)}
.ex-gradient-drift .b1{background:rgba(var(--accent-rgb),.5);animation:aExGdB1 7s ease-in-out infinite}
.ex-gradient-drift .b2{background:rgba(var(--a2-rgb),.34);animation:aExGdB2 9s ease-in-out infinite}
/* --a4-rgb n existe pas dans base.css, on passe par l opacite pour
   que la tache suive la palette au lieu de figer une couleur */
.ex-gradient-drift .b3{background:var(--a4);opacity:.26;animation:aExGdB3 11s ease-in-out infinite}
.ex-gradient-drift .ct{position:absolute;left:13px;right:13px;top:16px;display:flex;
  flex-direction:column;gap:7px;z-index:2}
.ex-gradient-drift .ct .h{height:13px;width:74%;border-radius:5px;background:rgba(var(--ink-rgb),.82)}
.ex-gradient-drift .ct .l{height:6px;border-radius:3px;background:rgba(var(--ink-rgb),.3)}
.ex-gradient-drift .per{position:absolute;left:13px;right:13px;bottom:12px;display:flex;
  flex-direction:column;gap:5px;z-index:2}
.ex-gradient-drift .per span{position:relative;display:block;height:3px;border-radius:2px;
  background:rgba(var(--ink-rgb),.12);overflow:hidden}
.ex-gradient-drift .per i{position:absolute;left:0;top:0;bottom:0;width:22px;border-radius:2px}
.ex-gradient-drift .p1 i{background:var(--lime);animation:aExGdP 7s linear infinite}
.ex-gradient-drift .p2 i{background:var(--a2);animation:aExGdP 9s linear infinite}
.ex-gradient-drift .p3 i{background:var(--a4);animation:aExGdP 11s linear infinite}
@keyframes aExGdB1{0%,100%{transform:translate(-30px,-24px)}50%{transform:translate(96px,40px)}}
@keyframes aExGdB2{0%,100%{transform:translate(110px,10px)}50%{transform:translate(-14px,86px)}}
@keyframes aExGdB3{0%,100%{transform:translate(30px,96px) scale(1)}50%{transform:translate(80px,-18px) scale(1.25)}}
@keyframes aExGdP{0%,100%{transform:translateX(0)}50%{transform:translateX(124px)}}

/* ---------------- mask-image-reveal ----------------
   Une couverture d article. Le rideau passe a 52 pour cent, l image se
   pose a 62, dix pour cent plus tard. C est ce DECALAGE qui fait la
   profondeur, la zone decouverte est encore en train de bouger quand
   on la decouvre. Echelle de 1.15 a 1, la valeur de la consigne.
   Gele, on aurait une image et un titre, sans le rideau ni le fait que
   l image bouge encore derriere lui. */
.ex-mask-image-reveal{width:170px;height:146px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;box-sizing:border-box;display:flex;flex-direction:column;gap:9px}
.ex-mask-image-reveal .fr{position:relative;height:84px;border-radius:9px;overflow:hidden}
.ex-mask-image-reveal .img{position:absolute;inset:0;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 55%,var(--accent-deep));
  animation:aExMirImg 4.6s cubic-bezier(.5,0,.2,1) infinite}
.ex-mask-image-reveal .wipe{position:absolute;inset:0;background:var(--s0);
  animation:aExMirWipe 4.6s cubic-bezier(.5,0,.2,1) infinite}
.ex-mask-image-reveal .cap{display:flex;flex-direction:column;gap:6px}
.ex-mask-image-reveal .cap .t{height:9px;width:66%;border-radius:4px;background:var(--ink);opacity:.85;
  animation:aExMirCap 4.6s ease infinite}
.ex-mask-image-reveal .cap .l{height:6px;width:44%;border-radius:3px;background:var(--s4);
  animation:aExMirCap 4.6s ease infinite;animation-delay:.06s}
@keyframes aExMirWipe{0%,8%{transform:translateY(0)}52%,86%{transform:translateY(-100%)}
  96%,100%{transform:translateY(0)}}
@keyframes aExMirImg{0%,8%{transform:scale(1.15)}62%,86%{transform:scale(1)}
  96%,100%{transform:scale(1.15)}}
@keyframes aExMirCap{0%,40%{opacity:0;transform:translateY(6px)}
  60%,86%{opacity:1;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(6px)}}

/* ---------------- hero-marquee ----------------
   Trois bandeaux inclines, celui du milieu part en sens INVERSE des
   deux autres, et les trois durees sont 8s, 10s et 13s, sans diviseur
   commun, donc ils ne retombent jamais ensemble. Le contenu est
   duplique et la course vaut exactement -50 pour cent, donc le raccord
   tombe sur un point identique et ne se voit jamais.
   Gele, on aurait trois bandes de texte inclinees, sans la tension que
   produisent les sens contraires ni le decalage des trois cadences. */
.ex-hero-marquee{position:relative;width:172px;height:140px;border-radius:11px;overflow:hidden;
  background:var(--void);display:flex;flex-direction:column;justify-content:center;gap:14px}
.ex-hero-marquee .band{display:block;white-space:nowrap;overflow:hidden;
  transform:rotate(-3deg) scale(1.12)}
.ex-hero-marquee .band span{display:inline-block;font:800 16px/26px var(--font);
  letter-spacing:.04em;padding-right:14px}
.ex-hero-marquee .b1{background:var(--lime)}
.ex-hero-marquee .b1 span{color:var(--on-accent);animation:aExHmA 8s linear infinite}
.ex-hero-marquee .b2{transform:rotate(2.5deg) scale(1.12)}
.ex-hero-marquee .b2 span{color:var(--ink-3);animation:aExHmB 10s linear infinite}
.ex-hero-marquee .b3{background:var(--s1)}
.ex-hero-marquee .b3 span{color:var(--ink-2);animation:aExHmA 13s linear infinite}
@keyframes aExHmA{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@keyframes aExHmB{from{transform:translateX(-50%)}to{transform:translateX(0)}}

/* ---------------- scroll-pinning ----------------
   Une etude de cas. La page descend, la section se BLOQUE de 22 a 56
   pour cent pendant que ses trois etapes internes avancent, puis la
   page repart. La jauge de droite ne s arrete jamais, elle montre que
   le defilement continue d arriver et qu il est depense sur les etapes
   au lieu de faire avancer la page.
   Le fil mesure 276px dans une fenetre de 150, donc la course utile
   vaut 126px, exactement la valeur du dernier palier.
   Gele, on aurait un fil et une carte, sans le gel qui est tout le
   terme. */
.ex-scroll-pinning{position:relative;width:170px;height:150px;overflow:hidden;border-radius:11px;
  background:var(--s0)}
.ex-scroll-pinning .feed{position:absolute;left:10px;right:24px;top:0;display:flex;
  flex-direction:column;gap:9px;animation:aExSpnFeed 6s cubic-bezier(.4,0,.4,1) infinite}
.ex-scroll-pinning .feed .r{display:block;height:26px;border-radius:7px;background:var(--s2);flex:0 0 auto}
.ex-scroll-pinning .sct{display:flex;flex-direction:column;gap:7px;padding:10px;border-radius:9px;
  box-sizing:border-box;background:var(--s1);border:1px solid var(--lime);flex:0 0 auto}
.ex-scroll-pinning .sct b{display:block;height:10px;border-radius:4px;background:var(--s3);
  animation:aExSpnStep 6s steps(1,end) infinite}
.ex-scroll-pinning .sct b:nth-child(2){animation-delay:.9s}
.ex-scroll-pinning .sct b:nth-child(3){animation-delay:1.8s}
.ex-scroll-pinning .gauge{position:absolute;right:8px;top:9px;bottom:9px;width:4px;border-radius:2px;
  background:var(--s1)}
.ex-scroll-pinning .thumb{position:absolute;left:0;right:0;top:0;height:28px;border-radius:2px;
  background:var(--s5);animation:aExSpnT 6s linear infinite}
.ex-scroll-pinning .lock{position:absolute;left:10px;bottom:9px;z-index:3;padding:3px 7px;
  border-radius:5px;background:rgba(var(--accent-rgb),.2);
  font:700 7px/1 var(--mono);color:var(--lime);font-style:normal;
  animation:aExSpnLock 6s steps(1,end) infinite}
/* deux rails, la section, quatre rails, gap 9, soit 276px en tout */
@keyframes aExSpnFeed{0%,4%{transform:translateY(0)}
  22%,56%{transform:translateY(-60px)}
  74%{transform:translateY(-126px)}
  88%{transform:translateY(-60px)}
  100%{transform:translateY(0)}}
/* la jauge fait 132 de haut, le curseur 28, la course vaut donc 104 */
@keyframes aExSpnT{0%,100%{transform:translateY(0)}50%{transform:translateY(104px)}}
@keyframes aExSpnStep{0%,24%{background:var(--s3)}30%,54%{background:var(--lime)}
  60%,100%{background:var(--s3)}}
@keyframes aExSpnLock{0%,20%{opacity:0}24%,54%{opacity:1}58%,100%{opacity:0}}

/* ---------------- sticky-stack ----------------
   Trois cartes de projet a des decalages CROISSANTS, 0, 10 et 20. Ce
   sont ces dix pixels qui laissent voir le bord de chaque carte et
   disent la profondeur du jeu. Chaque carte recouverte retrecit a 0.96
   et tombe a 60 pour cent d opacite, sinon rien ne dirait laquelle est
   devant.
   Gele, on verrait trois cartes decalees, sans savoir qu elles se sont
   collees l une apres l autre. */
.ex-sticky-stack{position:relative;width:170px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-sticky-stack .k{position:absolute;left:11px;right:11px;display:flex;flex-direction:column;gap:7px;
  padding:10px;border-radius:10px;box-sizing:border-box;border:1px solid var(--line-2);height:52px}
.ex-sticky-stack .k i{display:block;height:6px;border-radius:3px;background:rgba(var(--void-rgb),.3)}
.ex-sticky-stack .k i:last-child{width:58%}
.ex-sticky-stack .k1{top:11px;background:var(--s3);animation:aExSsCov 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-sticky-stack .k2{top:21px;background:var(--s5);
  animation:aExSsIn 5.4s cubic-bezier(.3,.9,.3,1) infinite,aExSsCov2 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-sticky-stack .k3{top:31px;background:var(--lime);
  animation:aExSsIn 5.4s cubic-bezier(.3,.9,.3,1) infinite;animation-delay:.6s}
/* 31 + 112 = 143, la carte part sous le bas de la fenetre, a 148 */
@keyframes aExSsIn{0%,10%{transform:translateY(112px)}46%,88%{transform:translateY(0)}
  100%{transform:translateY(112px)}}
@keyframes aExSsCov{0%,16%{transform:scale(1);opacity:1}
  50%,86%{transform:scale(.96);opacity:.6}100%{transform:scale(1);opacity:1}}
@keyframes aExSsCov2{0%,40%{opacity:1}68%,86%{opacity:.72}100%{opacity:1}}

/* ---------------- horizontal-section ----------------
   Une galerie de projets. La jauge de droite descend, VERTICALEMENT,
   pendant que le rail va lateralement. C est cet echange d axe qui est
   le motif, et sans la jauge on ne verrait qu un carrousel. La barre du
   bas dit jusqu ou va la section, sans quoi un rail qui glisse encore
   se lit comme un piege.
   Le rail mesure 267px et sa fenetre 161, la course vaut donc 106, ni
   plus ni moins, sinon il resterait du vide a droite.
   Gele, on perdrait a la fois l entree verticale et la fin annoncee. */
.ex-horizontal-section{position:relative;width:172px;height:140px;overflow:hidden;border-radius:11px;
  background:var(--s0)}
.ex-horizontal-section .rail{position:absolute;left:11px;top:26px;display:flex;gap:9px;
  animation:aExHsRail 5.4s cubic-bezier(.4,0,.4,1) infinite}
.ex-horizontal-section .rail i{display:block;flex:0 0 auto;width:60px;height:64px;border-radius:9px;
  background:var(--s3)}
.ex-horizontal-section .rail i:first-child{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-horizontal-section .hd{position:absolute;left:11px;top:11px;height:7px;width:52px;border-radius:4px;
  background:var(--ink);opacity:.85}
.ex-horizontal-section .vg{position:absolute;right:8px;top:11px;bottom:30px;width:4px;border-radius:2px;
  background:var(--s1)}
.ex-horizontal-section .vt{position:absolute;left:0;right:0;top:0;height:26px;border-radius:2px;
  background:var(--s5);animation:aExHsV 5.4s cubic-bezier(.4,0,.4,1) infinite}
.ex-horizontal-section .prg{position:absolute;left:11px;right:22px;bottom:12px;height:4px;
  border-radius:2px;background:var(--s1)}
.ex-horizontal-section .prg b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:var(--lime);animation:aExHsP 5.4s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExHsRail{0%,10%{transform:translateX(0)}62%,90%{transform:translateX(-106px)}
  100%{transform:translateX(0)}}
/* la jauge fait 99 de haut, le curseur 26, la course vaut donc 73 */
@keyframes aExHsV{0%,10%{transform:translateY(0)}62%,90%{transform:translateY(73px)}
  100%{transform:translateY(0)}}
@keyframes aExHsP{0%,10%{width:0}62%,90%{width:100%}100%{width:0}}

/* ---------------- smooth-scroll ----------------
   La molette pousse de 8 a 44 pour cent puis S ARRETE. La page brute
   s arrete avec elle, la page lissee continue jusqu a 62. Cet ecart est
   a la fois l effet recherche et son cout, de la latence entre le geste
   et l ecran, ce qui est exactement ce qui divise sur ce sujet.
   Gele, on aurait deux pages identiques, sans savoir laquelle a
   continue apres l arret. */
.ex-smooth-scroll{width:172px;height:146px;border-radius:11px;overflow:hidden;background:var(--s0);
  padding:11px;box-sizing:border-box;display:flex;flex-direction:column;gap:9px}
.ex-smooth-scroll .whl{display:flex;align-items:center;gap:8px}
.ex-smooth-scroll .whl em{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-smooth-scroll .whl span{position:relative;display:block;width:12px;height:20px;border-radius:6px;
  box-sizing:border-box;border:1.5px solid var(--s5);flex:0 0 auto}
.ex-smooth-scroll .whl i{position:absolute;left:50%;top:3px;width:2px;height:6px;margin-left:-1px;
  border-radius:1px;background:var(--lime);animation:aExSmoothW 4.6s steps(1,end) infinite}
.ex-smooth-scroll .whl b{flex:1;height:4px;border-radius:2px;background:var(--s2)}
.ex-smooth-scroll .cols{display:flex;gap:11px;flex:1;min-height:0}
.ex-smooth-scroll .col{flex:1;display:flex;flex-direction:column;gap:6px;min-width:0}
.ex-smooth-scroll .lb{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-smooth-scroll .vp{position:relative;flex:1;overflow:hidden;border-radius:7px;background:var(--s1)}
.ex-smooth-scroll .pg{position:absolute;left:6px;right:6px;top:6px;display:flex;flex-direction:column;gap:6px}
.ex-smooth-scroll .pg i{display:block;height:12px;border-radius:4px;background:var(--s3);flex:0 0 auto}
.ex-smooth-scroll .raw{animation:aExSmoothH 4.6s steps(5,end) infinite}
.ex-smooth-scroll .len{animation:aExSmoothS 4.6s cubic-bezier(.2,0,.15,1) infinite}
/* la page fait 138 de haut dans une fenetre de 81, la course de 44
   laisse donc toujours du contenu sous le bord bas */
@keyframes aExSmoothH{0%,8%{transform:translateY(0)}44%,64%{transform:translateY(-44px)}
  90%,100%{transform:translateY(0)}}
@keyframes aExSmoothS{0%,8%{transform:translateY(0)}62%,72%{transform:translateY(-44px)}
  94%,100%{transform:translateY(0)}}
@keyframes aExSmoothW{0%,8%{opacity:0}12%,42%{opacity:1}46%,100%{opacity:0}}

/* ---------------- velocity-skew ----------------
   La jauge du bas lit la vitesse instantanee, en valeur absolue, et les
   vignettes se deforment EXACTEMENT avec elle, huit degres quand la
   jauge est pleine, zero quand elle est vide. Plafond a huit degres,
   sous les dix de la consigne.
   Le defilement repart ensuite VERS LE HAUT et le skew change de signe,
   ce qui est le vrai sujet, la deformation suit une vitesse signee et
   pas une simple quantite de mouvement.
   Gele, on verrait des vignettes inclinees sans savoir de combien, dans
   quel sens, ni pourquoi. */
.ex-velocity-skew{position:relative;width:170px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;box-sizing:border-box;display:flex;flex-direction:column;gap:8px}
.ex-velocity-skew .vp{position:relative;flex:1;overflow:hidden;min-height:0}
.ex-velocity-skew .list{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;gap:8px;
  animation:aExVskMove 3.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-velocity-skew .crd{display:flex;align-items:center;gap:9px;height:26px;padding:0 9px;flex:0 0 auto;
  box-sizing:border-box;border-radius:8px;background:var(--s2);
  animation:aExVsk 3.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-velocity-skew .crd:nth-child(2){animation-delay:.04s}
.ex-velocity-skew .crd:nth-child(3){animation-delay:.08s}
.ex-velocity-skew .crd:nth-child(4){animation-delay:.12s}
.ex-velocity-skew .crd:nth-child(5){animation-delay:.16s}
.ex-velocity-skew .crd i{display:block;width:16px;height:16px;border-radius:5px;background:var(--lime);
  flex:0 0 auto}
.ex-velocity-skew .crd b{display:block;flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-velocity-skew .lb{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-velocity-skew .vg{position:relative;height:5px;border-radius:3px;background:var(--s2);flex:0 0 auto}
.ex-velocity-skew .vg b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:3px;
  background:var(--a2);animation:aExVskV 3.6s cubic-bezier(.4,0,.3,1) infinite}
/* la liste fait 162 de haut dans une fenetre de 98, la course de 34
   reste donc tres en dessous des 64 disponibles */
@keyframes aExVskMove{0%,8%{transform:translateY(0)}
  30%{transform:translateY(-20px)}
  54%,70%{transform:translateY(-34px)}
  92%,100%{transform:translateY(0)}}
/* meme cycle, meme courbe, le skew suit la jauge palier par palier et
   change de signe quand le defilement repart vers le haut */
@keyframes aExVsk{0%,8%{transform:skewY(0)}
  22%{transform:skewY(-8deg)}
  46%{transform:skewY(-3deg)}
  60%,74%{transform:skewY(0)}
  84%{transform:skewY(6deg)}
  94%,100%{transform:skewY(0)}}
@keyframes aExVskV{0%,8%{width:0}22%{width:100%}46%{width:38%}60%,74%{width:0}
  84%{width:76%}94%,100%{width:0}}

/* ---------------- scrollytelling ----------------
   Le visuel est IMMOBILE, c est lui le point fixe contre lequel le
   texte en mouvement se lit. Il ne change d etat qu au passage d une
   frontiere d etape, marquee en pointille, et jamais en continu, sinon
   il ne serait jamais stable assez longtemps pour etre lu pendant que
   le texte le decrit.
   Les etapes commencent a 0, 68 et 136 dans une colonne qui va de +30 a
   -104. La deuxieme croise la frontiere posee a 62 au treizieme pour
   cent du cycle, la troisieme au trente-neuvieme, et le retour donne
   les deux memes croisements a 61 et 87. Les bascules du visuel sont
   posees exactement la.
   Gele, on aurait une forme et du texte cote a cote, sans la relation
   qui fait tout le format. */
.ex-scrollytelling{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;box-sizing:border-box;display:flex;gap:12px}
.ex-scrollytelling .viz{position:relative;flex:0 0 auto;width:62px;height:62px;align-self:center;
  background:var(--lime);animation:aExStyV 6.4s linear infinite}
.ex-scrollytelling .col{position:relative;flex:1;overflow:hidden;min-width:0}
.ex-scrollytelling .txts{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;
  gap:22px;animation:aExStyScroll 6.4s linear infinite}
.ex-scrollytelling .stp{display:flex;flex-direction:column;gap:6px;height:46px;flex:0 0 auto}
.ex-scrollytelling .stp i{display:block;height:8px;border-radius:4px;background:var(--s4)}
.ex-scrollytelling .stp i:last-child{width:64%}
/* la frontiere, au milieu de la colonne de 124 */
.ex-scrollytelling .bnd{position:absolute;left:-14px;right:0;top:62px;height:1px;z-index:2;
  background:repeating-linear-gradient(90deg,var(--a2) 0 3px,transparent 3px 6px)}
@keyframes aExStyScroll{0%,100%{transform:translateY(30px)}50%{transform:translateY(-104px)}}
@keyframes aExStyV{0%,13%{border-radius:12px;transform:rotate(0)}
  13.5%,39%{border-radius:50%;transform:rotate(0)}
  39.5%,61%{border-radius:12px;transform:rotate(45deg)}
  61.5%,87%{border-radius:50%;transform:rotate(0)}
  87.5%,100%{border-radius:12px;transform:rotate(0)}}

/* ---------------- infinite-loop ----------------
   La BARRE DE SCROLL est ce que l exemple ajoute a l abstrait. Le
   contenu coule a vitesse constante, sans rupture, pendant que le
   curseur de la barre descend puis se TELEPORTE en haut. Ce saut est
   le modulo, et c est la seule trace visible d une mecanique dont tout
   le travail consiste justement a ne rien laisser voir.
   Douze cartes de pas 42, soit une ceinture de 504, et une course de
   -50 pour cent egale a 252, c est a dire deux copies de trois cartes.
   Le curseur se remet a zero deux fois par cycle, une fois par copie
   franchie, et la pastille mod s allume a chacun de ces instants.
   La fenetre fait 110 de haut pour 504 de contenu, il y a donc toujours
   de la matiere au dessus et en dessous.
   Gele, on aurait une liste de produits, sans la remise a zero qui est
   tout le terme. */
.ex-infinite-loop{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:11px;box-sizing:border-box}
.ex-infinite-loop .hd{display:block;height:8px;width:56px;border-radius:4px;background:var(--ink);opacity:.85}
.ex-infinite-loop .vp{position:absolute;left:11px;right:26px;top:29px;bottom:11px;overflow:hidden;
  border-radius:8px}
.ex-infinite-loop .belt{position:absolute;left:0;right:0;top:0;animation:aExIlpBelt 7s linear infinite}
.ex-infinite-loop .crd{display:flex;align-items:center;gap:8px;height:34px;margin-bottom:8px;padding:0 9px;
  box-sizing:border-box;border-radius:8px;background:var(--s2)}
.ex-infinite-loop .crd i{display:block;width:22px;height:22px;border-radius:6px;background:var(--s4);flex:0 0 auto}
.ex-infinite-loop .crd b{display:block;flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-infinite-loop .crd.hot i{background:var(--lime)}
.ex-infinite-loop .sb{position:absolute;right:11px;top:29px;bottom:11px;width:4px;border-radius:2px;
  background:var(--s1)}
.ex-infinite-loop .sb i{position:absolute;left:0;right:0;top:0;height:26px;border-radius:2px;
  background:var(--s5);animation:aExIlpThumb 7s linear infinite}
.ex-infinite-loop .mod{position:absolute;right:20px;top:33px;z-index:3;padding:2px 5px;border-radius:4px;
  background:rgba(var(--accent-rgb),.22);font:700 6px/1 var(--mono);color:var(--lime);font-style:normal;
  animation:aExIlpMod 7s steps(1,end) infinite}
@keyframes aExIlpBelt{from{transform:translateY(0)}to{transform:translateY(-50%)}}
/* la piste fait 110 et le curseur 26, la course vaut donc 84 */
@keyframes aExIlpThumb{0%{transform:translateY(0)}49.5%{transform:translateY(84px)}
  50%{transform:translateY(0)}99.5%{transform:translateY(84px)}100%{transform:translateY(0)}}
@keyframes aExIlpMod{0%,3%{opacity:1}7%,49%{opacity:0}50%,53%{opacity:1}57%,99%{opacity:0}
  100%{opacity:1}}

/* ---------------- magnetic-button ----------------
   La PREUVE que le rayon appartient a l element et pas a la page. Le
   pointeur traverse les deux commandes de la meme ligne, la pastille
   principale se penche vers lui et le lien secondaire, juste a cote, ne
   bouge pas d un pixel.
   Le centre de la pastille est a 57. Le pointeur passe par 9, 33, 57,
   81 puis 105, donc les decalages valent -48, -24, 0, +24, +48 et le
   corps suit a 0.20 pres, soit -10, -5, 0, +5, +10, le libelle a 0.28,
   soit -13, -7, 0, +7, +13. Au centre l attraction vaut zero.
   Passe 105 le pointeur sort du rayon et le ressort ramene tout a zero
   avant meme qu il atteigne le lien.
   Gele, on verrait deux commandes, sans savoir laquelle est aimantee. */
.ex-magnetic-button{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column}
.ex-magnetic-button .h1{display:block;height:10px;width:78%;border-radius:5px;background:var(--ink);opacity:.9}
.ex-magnetic-button .h2{display:block;height:10px;width:54%;border-radius:5px;background:var(--ink);
  opacity:.9;margin-top:8px}
.ex-magnetic-button .sub{display:block;height:6px;width:88%;border-radius:3px;background:var(--s4);
  margin-top:10px}
.ex-magnetic-button .row{display:flex;align-items:center;gap:12px;margin:auto auto 0;padding-bottom:6px}
.ex-magnetic-button .btn{display:grid;place-items:center;width:74px;height:30px;border-radius:15px;
  background:var(--lime);animation:aExMgbBody 4.4s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-magnetic-button .btn i{display:block;width:30px;height:5px;border-radius:3px;background:var(--on-accent);
  opacity:.8;animation:aExMgbLabel 4.4s cubic-bezier(.2,1.4,.3,1) infinite}
/* le lien secondaire n a aucune animation, c est la moitie de la demonstration */
.ex-magnetic-button .lnk{display:block;width:46px;height:6px;border-radius:3px;background:var(--s5)}
.ex-magnetic-button .cur{left:-1px;top:108px;animation:aExMgbCur 4.4s linear infinite}
@keyframes aExMgbCur{0%{transform:translateX(0);opacity:0}
  5%{transform:translateX(0);opacity:1}
  16%{transform:translateX(3px)}28%{transform:translateX(27px)}40%{transform:translateX(51px)}
  52%{transform:translateX(75px)}64%{transform:translateX(99px)}70%{transform:translateX(107px)}
  80%{transform:translateX(123px)}
  95%{transform:translateX(160px);opacity:1}
  100%{transform:translateX(160px);opacity:0}}
@keyframes aExMgbBody{0%,14%{transform:translateX(0)}
  19%{transform:translateX(-10px)}28%{transform:translateX(-5px)}40%{transform:translateX(0)}
  52%{transform:translateX(5px)}64%{transform:translateX(10px)}
  71%,100%{transform:translateX(0)}}
@keyframes aExMgbLabel{0%,14%{transform:translateX(0)}
  19%{transform:translateX(-13px)}28%{transform:translateX(-7px)}40%{transform:translateX(0)}
  52%{transform:translateX(7px)}64%{transform:translateX(13px)}
  71%,100%{transform:translateX(0)}}

/* ---------------- blend-cursor ----------------
   La coupure sombre et claire est le decor NECESSAIRE. Sans elle
   l inversion n aurait rien contre quoi se lire. La bille traverse la
   nav puis le titre sur le fond sombre, ou elle ressort claire, puis
   descend sur la moitie claire ou elle ressort sombre, sans jamais
   savoir ce qu elle survole.
   Sur le lien elle passe de 1 a 1.9, la seule affordance qui reste une
   fois la fleche native cachee. Centre de la bille 28 et 20, centre du
   lien 43 et 113, donc 15 et 93.
   Gele, on verrait une bille sur une page, sans l inversion ni la
   reaction au lien. */
.ex-blend-cursor{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden}
.ex-blend-cursor .tpp2{position:absolute;left:0;right:0;top:0;height:82px;background:var(--void)}
.ex-blend-cursor .bot{position:absolute;left:0;right:0;top:82px;bottom:0;background:var(--ink)}
.ex-blend-cursor .nav{position:absolute;left:12px;right:12px;top:12px;display:flex;gap:10px}
.ex-blend-cursor .nav i{display:block;flex:1;height:5px;border-radius:3px;background:var(--s5)}
.ex-blend-cursor .ttl{position:absolute;left:12px;top:34px;width:104px;height:14px;border-radius:5px;
  background:var(--s5)}
.ex-blend-cursor .sub{position:absolute;left:12px;top:56px;width:70px;height:6px;border-radius:3px;
  background:var(--s4)}
.ex-blend-cursor .lk{position:absolute;left:12px;top:104px;width:62px;height:18px;border-radius:9px;
  box-sizing:border-box;border:1.5px solid var(--void)}
.ex-blend-cursor .lk b{position:absolute;left:11px;top:6px;width:38px;height:5px;border-radius:3px;
  background:var(--void)}
.ex-blend-cursor .dot{position:absolute;left:16px;top:8px;width:24px;height:24px;border-radius:50%;
  background:var(--ink);mix-blend-mode:difference;z-index:6;
  animation:aExBcu 5s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExBcu{0%,6%{transform:translate(0,0) scale(1)}
  26%{transform:translate(72px,-6px) scale(1)}
  46%{transform:translate(36px,21px) scale(1)}
  66%,80%{transform:translate(15px,93px) scale(1.9)}
  95%,100%{transform:translate(0,0) scale(1)}}

/* ---------------- image-trail ----------------
   Les vignettes ne SUIVENT pas le pointeur, elles restent ou elles sont
   nees. Quand le pointeur est au quatrieme point, la premiere vignette
   est encore visible au premier point, en train de disparaitre. C est
   cela qui separe le terme d une trainee de points, qui elle poursuit
   le pointeur et n a aucune memoire.
   Six points sur un trajet ferme, cycle de 4.2s, donc une estampe
   toutes les 0.7s pour une vie de 2.1s, trois vivantes a la fois.
   Gele, on verrait six vignettes eparpillees, sans l ordre ni la
   disparition. */
.ex-image-trail{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-image-trail .ttl{position:absolute;left:12px;top:12px;width:80px;height:9px;border-radius:4px;
  background:var(--ink);opacity:.85}
.ex-image-trail .sub{position:absolute;left:12px;top:27px;width:52px;height:6px;border-radius:3px;
  background:var(--s4)}
.ex-image-trail .idx{position:absolute;right:12px;top:12px;display:flex;flex-direction:column;gap:6px}
.ex-image-trail .idx i{display:block;width:26px;height:4px;border-radius:2px;background:var(--s3)}
.ex-image-trail>i{position:absolute;display:block;width:30px;height:22px;border-radius:7px;opacity:0;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExItrDrop 4.2s ease-out infinite}
.ex-image-trail .d1{left:11px;top:47px}
.ex-image-trail .d2{left:51px;top:37px;animation-delay:.7s}
.ex-image-trail .d3{left:93px;top:51px;animation-delay:1.4s}
.ex-image-trail .d4{left:119px;top:85px;animation-delay:2.1s}
.ex-image-trail .d5{left:77px;top:109px;animation-delay:2.8s}
.ex-image-trail .d6{left:29px;top:95px;animation-delay:3.5s}
.ex-image-trail .cur{left:19px;top:51px;animation:aExItrCur 4.2s linear infinite}
@keyframes aExItrCur{0%{transform:translate(0,0)}
  16.67%{transform:translate(40px,-10px)}33.33%{transform:translate(82px,4px)}
  50%{transform:translate(108px,38px)}66.67%{transform:translate(66px,62px)}
  83.33%{transform:translate(18px,48px)}100%{transform:translate(0,0)}}
@keyframes aExItrDrop{0%{opacity:0;transform:scale(.55)}
  5%{opacity:1;transform:scale(1)}
  50%{opacity:0;transform:scale(.82)}
  100%{opacity:0;transform:scale(.55)}}

/* ---------------- hover-displacement ----------------
   Deux cartes, une seule survolee. La carte de droite ne bouge jamais,
   c est elle qui prouve que la deformation appartient au survol et pas
   a la page.
   La sequence complete est la, et c est ce qui manquait. Le pointeur
   entre a 12 pour cent, l intensite monte, l image se dechire et bascule
   vers la seconde, puis l intensite REDESCEND A ZERO et la seconde image
   est propre de 30 a 62. Le pointeur sort, l intensite remonte, et on
   revient a la premiere image, propre elle aussi.
   Huit rangees de 12 font 96, la hauteur exacte du cadre, et les
   poussees valent 3, 18, 8, 18, 3, 13, 8 puis 13 sur une carte de 72,
   soit un quart de la largeur au maximum.
   Gele, on aurait deux cartes, sans savoir laquelle est survolee ni que
   la dechirure revient a zero. */
.ex-hover-displacement{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:10px;box-sizing:border-box;display:flex;gap:8px}
.ex-hover-displacement .crd{flex:1;display:flex;flex-direction:column;gap:8px;min-width:0}
.ex-hover-displacement .shot{position:relative;height:96px;overflow:hidden;border-radius:8px;
  background:var(--s1)}
.ex-hover-displacement .shot i{position:relative;display:block;height:12px;overflow:hidden;
  background:linear-gradient(90deg,var(--lime),var(--accent-2))}
.ex-hover-displacement .shot i b{position:absolute;inset:0;opacity:0;
  background:linear-gradient(90deg,var(--a2),var(--accent-deep))}
.ex-hover-displacement .cap{height:6px;border-radius:3px;background:var(--s4)}
.ex-hover-displacement .cap.s{width:62%}
.ex-hover-displacement .crd:first-child .shot i b{animation:aExHdpFade 5.6s ease-in-out infinite}
.ex-hover-displacement .crd:first-child .r1,
.ex-hover-displacement .crd:first-child .r5{animation:aExHdpA 5.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-hover-displacement .crd:first-child .r3,
.ex-hover-displacement .crd:first-child .r7{animation:aExHdpB 5.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-hover-displacement .crd:first-child .r6,
.ex-hover-displacement .crd:first-child .r8{animation:aExHdpC 5.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-hover-displacement .crd:first-child .r2,
.ex-hover-displacement .crd:first-child .r4{animation:aExHdpD 5.6s cubic-bezier(.4,0,.3,1) infinite}
.ex-hover-displacement .cur{left:27px;top:122px;animation:aExHdpCur 5.6s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExHdpA{0%,12%{transform:translateX(0)}21%{transform:translateX(3px)}
  30%,62%{transform:translateX(0)}71%{transform:translateX(3px)}80%,100%{transform:translateX(0)}}
@keyframes aExHdpB{0%,12%{transform:translateX(0)}21%{transform:translateX(8px)}
  30%,62%{transform:translateX(0)}71%{transform:translateX(8px)}80%,100%{transform:translateX(0)}}
@keyframes aExHdpC{0%,12%{transform:translateX(0)}21%{transform:translateX(13px)}
  30%,62%{transform:translateX(0)}71%{transform:translateX(13px)}80%,100%{transform:translateX(0)}}
@keyframes aExHdpD{0%,12%{transform:translateX(0)}21%{transform:translateX(18px)}
  30%,62%{transform:translateX(0)}71%{transform:translateX(18px)}80%,100%{transform:translateX(0)}}
@keyframes aExHdpFade{0%,14%{opacity:0}30%,62%{opacity:1}78%,100%{opacity:0}}
@keyframes aExHdpCur{0%{transform:translate(0,0);opacity:0}
  8%{transform:translate(0,0);opacity:1}
  30%{transform:translate(4px,-40px)}62%{transform:translate(8px,-46px)}
  80%{transform:translate(2px,-10px)}
  92%{transform:translate(0,0);opacity:1}
  100%{transform:translate(0,0);opacity:0}}

/* ---------------- text-scramble ----------------
   Un seul item de nav se decode, celui que le pointeur survole. Les
   trois autres ne bougent jamais, et c est ce qui rattache le brouillage
   a une intention et non a un decor.
   Chaque emplacement fait defiler une bobine de douze signes, trois
   tires au hasard puis trois fois la vraie lettre, ecrits deux fois. La
   bobine fait 12 fois 16, soit 192, et la course d une periode vaut donc
   exactement -50 pour cent. steps(6) donne six images, trois brouillees
   et trois verrouillees.
   Le decalage de 0.1s par emplacement fait resoudre de gauche a droite.
   Gele, on lirait WORK, sans savoir qu il vient d etre faux. */
.ex-text-scramble{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:12px;box-sizing:border-box}
.ex-text-scramble .nav{display:flex;align-items:flex-end;gap:11px}
.ex-text-scramble .nav .it{display:block;width:20px;height:5px;border-radius:3px;background:var(--s5);
  opacity:.55;margin-bottom:5px}
.ex-text-scramble .word{display:flex;gap:3px}
.ex-text-scramble .sl{position:relative;display:block;width:9px;height:16px;overflow:hidden;
  box-shadow:inset 0 -2px 0 rgba(var(--accent-rgb),.25);animation:aExTscLock 2.6s linear infinite}
.ex-text-scramble .reel{display:block;animation:aExTscReel 2.6s steps(6,end) infinite}
.ex-text-scramble .reel b{display:block;height:16px;text-align:center;
  font:700 11px/16px var(--mono);color:var(--lime)}
.ex-text-scramble .sl:nth-child(2),.ex-text-scramble .sl:nth-child(2) .reel{animation-delay:.1s}
.ex-text-scramble .sl:nth-child(3),.ex-text-scramble .sl:nth-child(3) .reel{animation-delay:.2s}
.ex-text-scramble .sl:nth-child(4),.ex-text-scramble .sl:nth-child(4) .reel{animation-delay:.3s}
.ex-text-scramble .ttl{position:absolute;left:12px;top:50px;width:120px;height:16px;border-radius:6px;
  background:var(--s5)}
.ex-text-scramble .b1{position:absolute;left:12px;top:78px;width:140px;height:6px;border-radius:3px;
  background:var(--s4);opacity:.7}
.ex-text-scramble .b2{position:absolute;left:12px;top:90px;width:112px;height:6px;border-radius:3px;
  background:var(--s4);opacity:.7}
.ex-text-scramble .b3{position:absolute;left:12px;top:102px;width:128px;height:6px;border-radius:3px;
  background:var(--s4);opacity:.7}
.ex-text-scramble .cur{left:27px;top:13px}
@keyframes aExTscReel{from{transform:translateY(0)}to{transform:translateY(-50%)}}
@keyframes aExTscLock{0%,49%{box-shadow:inset 0 -2px 0 rgba(var(--accent-rgb),.25)}
  50%,99%{box-shadow:inset 0 -2px 0 var(--lime)}
  100%{box-shadow:inset 0 -2px 0 rgba(var(--accent-rgb),.25)}}

/* ---------------- variable-font ----------------
   L axe est BRANCHE sur quelque chose. La graisse du titre suit la
   profondeur de scroll, exactement la meme courbe et le meme cycle que
   la barre du bas, qui porte l etiquette wght. Un axe anime en boucle
   sans source ne serait qu une typo qui respire.
   La piste fait 96 et le curseur 8, la course vaut donc 88.
   Gele, on verrait une couverture avec un titre gras, sans savoir que
   la graisse est une valeur pilotee. */
.ex-variable-font{position:relative;width:170px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:9px}
.ex-variable-font .mast{display:block;font:200 26px/28px var(--font);color:var(--ink);
  letter-spacing:-.03em;animation:aExVfaWght 4.4s ease-in-out infinite}
.ex-variable-font .sub{display:block;height:6px;width:58%;border-radius:3px;background:var(--s4)}
.ex-variable-font .photo{display:block;height:52px;border-radius:8px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 60%,var(--accent-deep))}
.ex-variable-font .row{display:flex;align-items:center;gap:7px;margin-top:auto}
.ex-variable-font .row em{font:600 8px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-variable-font .trk{position:relative;display:block;width:96px;flex:0 0 auto;height:4px;border-radius:2px;
  background:var(--s2)}
.ex-variable-font .trk i{position:absolute;left:0;top:-2px;width:8px;height:8px;border-radius:50%;
  background:var(--a2);animation:aExVfaThumb 4.4s ease-in-out infinite}
@keyframes aExVfaWght{0%,100%{font-weight:200}50%{font-weight:900}}
@keyframes aExVfaThumb{0%,100%{transform:translateX(0)}50%{transform:translateX(88px)}}

/* ---------------- velocity-marquee ----------------
   Le bandeau et la page sont sur la MEME molette, et le calcul le dit.
   La cadence du bandeau vaut une derive de base de -0.5 par unite de
   temps plus la vitesse de scroll signee. Entre 55 et 70 pour cent la
   page REMONTE, la somme devient positive et le bandeau repart en
   arriere, de -58.7 a -38.0 pour cent.
   L integrale de la vitesse sur le cycle vaut exactement zero, la page
   revient donc a sa position de depart, pendant que la derive de base
   fait avancer le bandeau de -50 pour cent tout rond, soit deux motifs
   sur les quatre ecrits. Le raccord tombe sur un point identique.
   Le contenu est ecrit quatre fois parce que le bandeau depasse -50
   pour cent pendant sa course, jusqu a -58.7, et qu il faut de la
   matiere au dela.
   Gele, on verrait un pied de page avec du texte, sans la vitesse, sans
   le sens, et sans le lien avec la page au dessus. */
.ex-velocity-marquee{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-velocity-marquee .vp{position:relative;flex:1;overflow:hidden;min-height:0}
.ex-velocity-marquee .pg{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;gap:8px;
  animation:aExVmqPage 7s linear infinite}
.ex-velocity-marquee .pg i{display:block;height:6px;border-radius:3px;background:var(--s3);flex:0 0 auto}
.ex-velocity-marquee .band{overflow:hidden;height:26px;border-radius:6px;background:var(--void);
  display:flex;align-items:center;flex:0 0 auto}
.ex-velocity-marquee .strip{display:inline-block;white-space:nowrap;font:800 14px/26px var(--font);
  letter-spacing:.04em;color:var(--lime);animation:aExVmq 7s linear infinite}
.ex-velocity-marquee .vg{position:relative;height:5px;border-radius:3px;background:var(--s2);flex:0 0 auto}
.ex-velocity-marquee .vg b{position:absolute;top:0;bottom:0;width:50%;border-radius:3px}
.ex-velocity-marquee .pos{left:50%;transform-origin:left;transform:scaleX(0);background:var(--a2);
  animation:aExVmqPos 7s linear infinite}
.ex-velocity-marquee .neg{left:0;transform-origin:right;transform:scaleX(0);background:var(--a4);
  animation:aExVmqNeg 7s linear infinite}
.ex-velocity-marquee .zero{position:absolute;left:50%;top:-3px;width:1px;height:11px;background:var(--ink-3)}
@keyframes aExVmq{0%{transform:translateX(0)}10%{transform:translateX(-5%)}
  28%{transform:translateX(-39.2%)}40%{transform:translateX(-51.2%)}
  55%{transform:translateX(-58.7%)}70%{transform:translateX(-38%)}
  80%{transform:translateX(-40%)}100%{transform:translateX(-50%)}}
/* neuf barres de 6 avec 8 de gouttiere font 118, la fenetre 73, la
   course utile vaut donc 45, et l integrale revient a zero */
@keyframes aExVmqPage{0%,10%{transform:translateY(0)}28%{transform:translateY(-36px)}
  40%,55%{transform:translateY(-45px)}70%{transform:translateY(-4px)}
  80%,100%{transform:translateY(0)}}
@keyframes aExVmqPos{0%,10%{transform:scaleX(0)}13%,26%{transform:scaleX(.75)}
  30%,38%{transform:scaleX(.27)}42%,100%{transform:scaleX(0)}}
@keyframes aExVmqNeg{0%,55%{transform:scaleX(0)}58%,68%{transform:scaleX(1)}
  72%,78%{transform:scaleX(.16)}82%,100%{transform:scaleX(0)}}

/* ---------------- text-on-path ----------------
   Un badge de hero. Le cercle pointille et la fleche du centre ne
   bougent JAMAIS, seul le texte avance sur le trace. C est la seule
   facon de montrer la difference avec un sceau qui pivote, ou le guide
   tournerait avec le reste et ou personne ne pourrait dire ce qui se
   deplace.
   Le texte est ecrit plus long que la circonference, textPath coupe ce
   qui depasse, le cercle reste donc plein sans qu il faille accorder la
   longueur de la chaine au perimetre.
   Gele, on verrait un badge rond, sans savoir que le libelle voyage. */
.ex-text-on-path{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-text-on-path .im{display:block;height:74px;border-radius:8px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 60%,var(--accent-deep))}
.ex-text-on-path .h{display:block;height:9px;width:56%;border-radius:4px;background:var(--ink);
  opacity:.85;margin-top:10px}
.ex-text-on-path .s{display:block;height:6px;width:40%;border-radius:3px;background:var(--s4);
  margin-top:8px}
.ex-text-on-path .badge{position:absolute;right:10px;bottom:10px;width:58px;height:58px;
  display:grid;place-items:center}
.ex-text-on-path .badge svg{position:absolute;inset:0;width:58px;height:58px}
.ex-text-on-path .guide{fill:none;stroke:var(--s3);stroke-width:1;stroke-dasharray:3 4}
.ex-text-on-path text{font:700 10px var(--mono);fill:var(--lime);letter-spacing:1.2px;
  transform-box:view-box;transform-origin:50px 50px;animation:aExTop 9s linear infinite}
.ex-text-on-path .arw{position:relative;display:block;width:9px;height:9px;box-sizing:border-box;
  border-right:2px solid var(--ink);border-bottom:2px solid var(--ink);transform:rotate(45deg)}
@keyframes aExTop{to{transform:rotate(360deg)}}

/* ---------------- grain ----------------
   Un voile de hero avec des paliers durs, donc du banding bien visible.
   La pastille du coin dit si le grain est actif, et il s eteint la
   moitie du temps. C est cet aller retour qui montre a quoi sert le
   grain, sur la meme image, au meme endroit.
   Le bruit se decale de deux pixels quatre fois par demi seconde. Il
   n enleve aucune couleur et ne retire aucun detail, il casse seulement
   les plateaux, et c est ce qui le separe d un traitement.
   Gele, on ne saurait meme pas dans quel etat on se trouve. */
.ex-grain{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden}
.ex-grain .photo{position:absolute;inset:0;
  background:linear-gradient(150deg,var(--accent-2),var(--accent-deep))}
.ex-grain .scrim{position:absolute;left:0;right:0;bottom:0;height:92px;
  background:linear-gradient(180deg,rgba(var(--void-rgb),0) 0 12%,rgba(var(--void-rgb),.2) 12% 30%,
    rgba(var(--void-rgb),.4) 30% 50%,rgba(var(--void-rgb),.6) 50% 70%,rgba(var(--void-rgb),.8) 70% 100%)}
.ex-grain .noise{position:absolute;inset:-40px;
  background-image:radial-gradient(var(--ink) .6px,transparent .7px),
    radial-gradient(var(--void) .6px,transparent .7px);
  background-size:3px 3px,4px 4px;background-position:0 0,1px 2px;
  animation:aExGrnJit .5s steps(4,end) infinite,aExGrnFade 6.4s steps(1,end) infinite}
.ex-grain .h{position:absolute;left:12px;bottom:38px;width:70%;height:11px;border-radius:5px;
  background:var(--ink);opacity:.92}
.ex-grain .s{position:absolute;left:12px;bottom:20px;width:46%;height:6px;border-radius:3px;
  background:var(--ink);opacity:.6}
.ex-grain .chp{position:absolute;right:10px;top:10px;display:flex;align-items:center;gap:5px;
  padding:3px 7px;border-radius:9px;background:rgba(var(--void-rgb),.45)}
.ex-grain .chp i{display:block;width:6px;height:6px;border-radius:50%;
  animation:aExGrnChip 6.4s steps(1,end) infinite}
.ex-grain .chp em{font:700 7px/1 var(--mono);color:var(--ink);font-style:normal;opacity:.85}
@keyframes aExGrnJit{0%{transform:translate(0,0)}25%{transform:translate(-2px,1px)}
  50%{transform:translate(1px,-2px)}75%{transform:translate(2px,2px)}100%{transform:translate(0,0)}}
@keyframes aExGrnFade{0%,46%{opacity:.5}48%,96%{opacity:0}98%,100%{opacity:.5}}
@keyframes aExGrnChip{0%,46%{background:var(--lime)}48%,96%{background:var(--s3)}
  98%,100%{background:var(--lime)}}

/* ---------------- chromatic-aberration ----------------
   Trois reperes sur la meme photo. Celui du CENTRE ne recoit aucun
   decalage et reste net pendant que les deux autres prennent leur
   frange, ce qui met la loi radiale a l ecran au lieu de la laisser
   dans la definition.
   Les trois copies de chaque repere sont en screen, elles reviennent au
   neutre la ou elles se superposent et ne laissent de couleur que sur
   les bords. L intensite monte de zero au maximum puis redescend a
   zero, comme un survol.
   Les deux lignes de legende, sous la photo, ne recoivent rien. Elles
   portent du texte, et la consigne dit de rester sous trois pixels
   dessus, donc zero est le bon choix.
   Gele, on verrait des formes colorees, sans le repere net du centre
   qui est toute la demonstration. */
.ex-chromatic-aberration{position:relative;width:172px;height:148px;border-radius:11px;
  overflow:hidden;background:var(--s0);padding:12px;box-sizing:border-box}
.ex-chromatic-aberration .shot{position:relative;display:block;height:88px;border-radius:8px;
  overflow:hidden;background:var(--void)}
.ex-chromatic-aberration .ax{position:absolute;left:50%;top:8px;bottom:8px;width:1px;
  background:var(--s3);opacity:.5}
.ex-chromatic-aberration .m{position:absolute;top:24px;display:block;width:22px;height:40px}
.ex-chromatic-aberration .far{left:12px}
.ex-chromatic-aberration .r2{left:auto;right:12px}
.ex-chromatic-aberration .nil{left:50%;margin-left:-11px}
.ex-chromatic-aberration .m span{position:absolute;inset:0;border-radius:6px;mix-blend-mode:screen}
.ex-chromatic-aberration .m .r{background:var(--a4)}
.ex-chromatic-aberration .m .g{background:var(--lime)}
.ex-chromatic-aberration .m .c{background:var(--a2)}
.ex-chromatic-aberration .far .r{animation:aExChabR 3.4s ease-in-out infinite}
.ex-chromatic-aberration .far .c{animation:aExChabC 3.4s ease-in-out infinite}
.ex-chromatic-aberration .cap{display:block;height:7px;width:64%;border-radius:3px;
  background:var(--s5);margin-top:12px}
.ex-chromatic-aberration .cap.s{width:42%;margin-top:7px;background:var(--s4)}
@keyframes aExChabR{0%,10%{transform:translateX(0)}50%{transform:translateX(-2.6px)}
  90%,100%{transform:translateX(0)}}
@keyframes aExChabC{0%,10%{transform:translateX(0)}50%{transform:translateX(2.6px)}
  90%,100%{transform:translateX(0)}}

/* ---------------- glitch ----------------
   Un en-tete de site. Le logo se casse trois images toutes les six
   secondes, et RIEN d autre ne bouge, ni la nav, ni le visuel, ni les
   lignes. C est ce voisinage inerte qui dit que la corruption est
   ciblee et rare, les deux proprietes que la version en boucle courte
   perdait.
   Le logo est dessine quatre fois, la base plus trois copies decoupees
   en bandes a 0, 34 et 68 pour cent, decalees de -9, +7 et -5. La
   teinte magenta accompagne la bande haute sur les memes images.
   La rafale occupe de 80 a 85 pour cent d un cycle de 6.4s, soit 320ms.
   Gele, on verrait un en-tete propre. */
.ex-glitch{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-glitch .bar{position:relative;display:block;height:26px}
.ex-glitch .lay{position:absolute;left:0;top:5px;display:flex;align-items:center;gap:7px}
.ex-glitch .lay .lg{display:block;width:16px;height:16px;border-radius:5px;background:var(--lime)}
.ex-glitch .lay .lw{display:block;width:36px;height:6px;border-radius:3px;background:var(--ink);
  opacity:.85}
.ex-glitch .b1{clip-path:inset(0 0 66% 0);animation:aExGli1 6.4s steps(1,end) infinite}
.ex-glitch .b2{clip-path:inset(34% 0 34% 0);animation:aExGli2 6.4s steps(1,end) infinite}
.ex-glitch .b3{clip-path:inset(68% 0 0 0);animation:aExGli3 6.4s steps(1,end) infinite}
.ex-glitch .tint{position:absolute;left:0;top:5px;width:60px;height:16px;background:var(--a4);
  mix-blend-mode:screen;clip-path:inset(0 0 66% 0);opacity:0;
  animation:aExGliT 6.4s steps(1,end) infinite}
/* la nav ne recoit aucune animation, c est le voisin inerte */
.ex-glitch .nv{position:absolute;top:11px;display:block;width:18px;height:5px;border-radius:3px;
  background:var(--s4)}
.ex-glitch .nv:nth-of-type(1){right:52px}
.ex-glitch .nv:nth-of-type(2){right:26px}
.ex-glitch .nv:nth-of-type(3){right:0}
.ex-glitch .hero{display:block;height:62px;border-radius:8px;margin-top:10px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 55%,var(--accent-deep))}
.ex-glitch .t1{display:block;height:7px;width:78%;border-radius:3px;background:var(--s5);margin-top:10px}
.ex-glitch .t2{display:block;height:6px;width:52%;border-radius:3px;background:var(--s4);margin-top:7px}
@keyframes aExGli1{0%,79%{transform:translateX(0)}80%{transform:translateX(-9px)}
  82%{transform:translateX(4px)}84%,100%{transform:translateX(0)}}
@keyframes aExGli2{0%,79%{transform:translateX(0)}81%{transform:translateX(7px)}
  83%{transform:translateX(-3px)}85%,100%{transform:translateX(0)}}
@keyframes aExGli3{0%,79%{transform:translateX(0)}80%{transform:translateX(-5px)}
  83%{transform:translateX(6px)}85%,100%{transform:translateX(0)}}
@keyframes aExGliT{0%,79%{opacity:0}80%,83%{opacity:.5}84%,100%{opacity:0}}

/* ---------------- pixelation ----------------
   Une galerie ou une seule vignette remonte encore. Les trois autres
   sont deja nettes et ne bougent pas, ce qui donne au lecteur l echelle
   de ce qui a ete perdu et de ce qui est retrouve.
   Deux mosaiques REELLES, 4 sur 3 puis 8 sur 6, chaque case d une seule
   opacite. La taille de bloc est divisee par deux d un palier a
   l autre, et rien n est ajoute a aucun moment, aucun motif, aucune
   trame. C est ce qui separe le terme du dithering.
   Gele, on verrait une vignette en blocs, sans savoir si elle monte ou
   si elle descend. */
.ex-pixelation{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:grid;
  grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:8px}
.ex-pixelation .tl{position:relative;display:block;border-radius:7px;overflow:hidden;
  background:linear-gradient(135deg,var(--accent-deep),var(--lime))}
.ex-pixelation .smooth{position:absolute;inset:0;
  background:linear-gradient(135deg,var(--accent-deep),var(--lime));
  animation:aExPxlC 4.4s steps(1,end) infinite}
.ex-pixelation .mo{position:absolute;inset:0;display:grid}
.ex-pixelation .mo i{display:block;background:var(--lime)}
.ex-pixelation .a{grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(3,1fr);
  animation:aExPxlA 4.4s steps(1,end) infinite}
.ex-pixelation .b{grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(6,1fr);
  animation:aExPxlB 4.4s steps(1,end) infinite}
@keyframes aExPxlA{0%,20%{opacity:1}21%,79%{opacity:0}80%,100%{opacity:1}}
@keyframes aExPxlB{0%,20%{opacity:0}21%,40%{opacity:1}41%,59%{opacity:0}
  60%,79%{opacity:1}80%,100%{opacity:0}}
@keyframes aExPxlC{0%,40%{opacity:0}41%,59%{opacity:1}60%,100%{opacity:0}}

/* ---------------- ascii-shader ----------------
   Un portrait de page a propos, rendu en typographie. Le balayage de
   lumiere passe sur la grille et les rangees changent de glyphe l une
   apres l autre, dans son ordre de passage. Les cinq lignes de bio, a
   cote, restent du texte ordinaire et ne bougent pas, ce qui dit que le
   reencodage est une decision de composition et pas un filtre applique
   a la page.
   Cinq rangees, cycle de 3s, donc 0.6s de decalage par rangee, soit
   exactement le temps que met le balayage a franchir une rangee de 13.
   La bobine fait 8 fois 13, soit 104, et une periode vaut -50 pour cent.
   Gele, on verrait une grille de signes figee, sans le lien avec la
   lumiere. */
.ex-ascii-shader{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--void);padding:12px;box-sizing:border-box;display:flex;align-items:center;gap:12px}
.ex-ascii-shader .port{position:relative;flex:0 0 auto;width:54px;height:65px;overflow:hidden}
.ex-ascii-shader .gri{display:grid;grid-template-columns:repeat(6,9px);grid-auto-rows:13px}
.ex-ascii-shader .cl{display:block;width:9px;height:13px;overflow:hidden}
.ex-ascii-shader .reel{display:block;animation:aExAscReel 3s steps(4,end) infinite}
.ex-ascii-shader .reel b{display:block;height:13px;text-align:center;
  font:700 10px/13px var(--mono);color:var(--lime)}
.ex-ascii-shader .q1 .reel{animation-delay:.6s}
.ex-ascii-shader .q2 .reel{animation-delay:1.2s}
.ex-ascii-shader .q3 .reel{animation-delay:1.8s}
.ex-ascii-shader .q4 .reel{animation-delay:2.4s}
.ex-ascii-shader .sweep{position:absolute;left:-4px;right:-4px;top:0;height:13px;z-index:2;
  background:linear-gradient(180deg,transparent,rgba(var(--accent-rgb),.5),transparent);
  animation:aExAscSweep 3s linear infinite}
.ex-ascii-shader .bio{flex:1;display:flex;flex-direction:column;gap:9px;min-width:0}
.ex-ascii-shader .bio i{display:block;height:6px;border-radius:3px;background:var(--s5);opacity:.5}
.ex-ascii-shader .bio i:nth-child(2){width:82%}
.ex-ascii-shader .bio i:nth-child(4){width:68%}
.ex-ascii-shader .bio i:nth-child(5){width:88%}
@keyframes aExAscReel{from{transform:translateY(0)}to{transform:translateY(-50%)}}
@keyframes aExAscSweep{0%{transform:translateY(0);opacity:0}
  8%{opacity:1}92%{opacity:1}100%{transform:translateY(65px);opacity:0}}

/* ---------------- dithering ----------------
   La MAILLE VERROUILLEE, montree par l epreuve qui la met en defaut.
   Les trois cartes defilent. Le motif du cadre, lui, ne bouge pas d un
   pixel, il est pose sur la fenetre et pas sur le contenu. La carte du
   milieu porte au contraire son propre motif, colle a elle, et on le
   voit NAGER avec le defilement.
   C est exactement le piege de la consigne, et il ne se demontre qu en
   mouvement. Une capture des deux cartes serait rigoureusement
   identique.
   La colonne fait 204 dans une fenetre utile de 142, la course de 44
   reste donc sous les 62 disponibles et laisse toujours de la matiere
   sous le bord bas. L aller retour la ramene a son point de depart.
   Gele, les deux cartes seraient indiscernables. */
.ex-dithering{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-dithering .col{position:absolute;left:12px;right:12px;top:8px;
  animation:aExDthScroll 6.4s ease-in-out infinite}
.ex-dithering .cd{display:block;margin-bottom:10px}
.ex-dithering .ph{position:relative;display:block;height:46px;border-radius:7px;overflow:hidden;
  background:linear-gradient(90deg,var(--lime),var(--accent-deep))}
.ex-dithering .ln{display:block;height:6px;width:62%;border-radius:3px;background:var(--s4);
  margin-top:6px}
.ex-dithering .dit{display:flex;pointer-events:none}
.ex-dithering .dit i{display:block;flex:1;background-size:4px 4px}
.ex-dithering .c0{background-image:radial-gradient(rgba(var(--void-rgb),.62) 1.7px,transparent 1.8px)}
.ex-dithering .c1{background-image:radial-gradient(rgba(var(--void-rgb),.62) 1.5px,transparent 1.6px)}
.ex-dithering .c2{background-image:radial-gradient(rgba(var(--void-rgb),.62) 1.3px,transparent 1.4px)}
.ex-dithering .c3{background-image:radial-gradient(rgba(var(--void-rgb),.62) 1.1px,transparent 1.2px)}
.ex-dithering .c4{background-image:radial-gradient(rgba(var(--void-rgb),.62) .9px,transparent 1px)}
.ex-dithering .c5{background-image:radial-gradient(rgba(var(--void-rgb),.62) .7px,transparent .8px)}
.ex-dithering .c6{background-image:radial-gradient(rgba(var(--void-rgb),.62) .5px,transparent .6px)}
.ex-dithering .c7{background-image:radial-gradient(rgba(var(--void-rgb),.62) .3px,transparent .4px)}
/* pose sur la fenetre, il ne defile pas */
.ex-dithering .fix{position:absolute;inset:0;z-index:4}
/* pose sur la carte, il defile avec elle */
.ex-dithering .own{position:absolute;inset:0}
.ex-dithering .tag{position:absolute;right:9px;z-index:5;padding:2px 6px;border-radius:4px;
  font:700 6px/1 var(--mono);font-style:normal}
.ex-dithering .ok{top:8px;background:rgba(var(--accent-rgb),.25);color:var(--lime)}
.ex-dithering .ko{bottom:8px;background:rgba(var(--void-rgb),.4);color:var(--ink-3)}
/* colonne de 204 dans une fenetre utile de 142, course de 44 sur 62 */
@keyframes aExDthScroll{0%,100%{transform:translateY(0)}50%{transform:translateY(-44px)}}

/* ---------------- video-scrub ----------------
   Le texte defile, le cadre epingle SUIT, et au retour tout repasse a
   l envers. C est la reversibilite qui separe le scrub d une animation
   qui se declenche quand la section arrive, et elle ne se voit qu au
   moment ou la page remonte.
   Une seule course commande les trois, le texte, le curseur de la barre
   et la sequence. Les bascules du cadre sont posees a mi chemin entre
   deux vues, a 5, 15, 25, 35 et 45 pour cent, sinon le cadre montre une
   image et le curseur en designe une autre.
   Sept lignes de 6 avec 7 de gouttiere font 84 dans une fenetre de 52,
   la course vaut donc 32, et la barre fait 46 pour un curseur de 16,
   soit 30.
   Gele, on verrait une video au dessus d un texte, sans savoir qui
   commande. */
.ex-video-scrub{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-video-scrub .vp{position:relative;height:62px;overflow:hidden;border-radius:8px;
  background:var(--s1);flex:0 0 auto}
.ex-video-scrub .strip{position:absolute;left:0;top:0;display:flex;
  animation:aExVscStrip 5.6s steps(1,end) infinite}
.ex-video-scrub .fr{position:relative;display:block;width:148px;height:62px;flex:0 0 auto;
  background:linear-gradient(180deg,var(--s2),var(--s1))}
.ex-video-scrub .fr .f{position:absolute;top:50%;left:var(--x);width:20px;height:20px;
  margin:-10px 0 0 -10px;border-radius:6px;background:var(--lime)}
.ex-video-scrub .txt{position:relative;flex:1;overflow:hidden;margin-right:12px;min-height:0}
.ex-video-scrub .pg{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;gap:7px;
  animation:aExVscTxt 5.6s linear infinite}
.ex-video-scrub .pg i{display:block;height:6px;border-radius:3px;background:var(--s4);flex:0 0 auto}
.ex-video-scrub .sb{position:absolute;right:12px;bottom:12px;width:4px;height:46px;border-radius:2px;
  background:var(--s1)}
.ex-video-scrub .th{position:absolute;left:0;right:0;top:0;height:16px;border-radius:2px;
  background:var(--s5);animation:aExVscTh 5.6s linear infinite}
@keyframes aExVscTxt{0%,100%{transform:translateY(0)}50%{transform:translateY(-32px)}}
@keyframes aExVscTh{0%,100%{transform:translateY(0)}50%{transform:translateY(30px)}}
@keyframes aExVscStrip{0%,4.9%{transform:translateX(0)}
  5%,14.9%{transform:translateX(-148px)}
  15%,24.9%{transform:translateX(-296px)}
  25%,34.9%{transform:translateX(-444px)}
  35%,44.9%{transform:translateX(-592px)}
  45%,55%{transform:translateX(-740px)}
  55.1%,65%{transform:translateX(-592px)}
  65.1%,75%{transform:translateX(-444px)}
  75.1%,85%{transform:translateX(-296px)}
  85.1%,95%{transform:translateX(-148px)}
  95.1%,100%{transform:translateX(0)}}

/* ---------------- preloader ----------------
   Le site est derriere, entier, nav et hero. Les cinq pastilles sont
   les ressources et la barre franchit un cinquieme aux memes instants,
   12, 24, 36, 48 et 60 pour cent, donc on voit la barre ATTENDRE une
   ressource au lieu de suivre une horloge.
   La sortie de l ecran, de 60 a 74, et l entree du hero, de 66 a 80, se
   chevauchent sur huit pour cent du cycle. C est la consigne d une
   seule chronologie, elle demande 100 a 200ms, la demo est ralentie.
   La remise a l etat de depart du hero se fait a 30 pour cent, sous
   couvert, personne ne la voit.
   Gele, on verrait un ecran d attente devant un site, sans savoir ce
   qu il attend ni ce qui arrive apres. */
.ex-preloader{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--stage)}
.ex-preloader .site{position:absolute;inset:0;padding:12px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:10px;animation:aExPrlSite 4.6s cubic-bezier(.2,.8,.3,1) infinite}
.ex-preloader .nav{display:flex;align-items:center;gap:8px}
.ex-preloader .nav .lg{display:block;width:14px;height:14px;border-radius:4px;background:var(--lime);
  flex:0 0 auto}
.ex-preloader .nav i{display:block;width:20px;height:4px;border-radius:2px;background:var(--s4)}
.ex-preloader .site .im{display:block;height:62px;border-radius:8px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 60%,var(--accent-deep))}
.ex-preloader .site .t{display:block;height:7px;border-radius:3px;background:var(--s5)}
.ex-preloader .site .t2{width:54%}
.ex-preloader .veil{position:absolute;inset:0;background:var(--s0);display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:12px;
  animation:aExPrlV 4.6s cubic-bezier(.6,0,.2,1) infinite}
.ex-preloader .mrq{display:block;width:22px;height:22px;border-radius:7px;background:var(--lime)}
.ex-preloader .q{display:flex;gap:6px}
.ex-preloader .q i{display:block;width:13px;height:13px;border-radius:4px;background:var(--s3)}
.ex-preloader .bar{position:relative;display:block;width:104px;height:5px;border-radius:3px;
  background:var(--s2);overflow:hidden}
.ex-preloader .bar i{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:3px;
  background:var(--lime);animation:aExPrlBar 4.6s steps(1,end) infinite}
.ex-preloader .q1{animation:aExPrlQ1 4.6s steps(1,end) infinite}
.ex-preloader .q2{animation:aExPrlQ2 4.6s steps(1,end) infinite}
.ex-preloader .q3{animation:aExPrlQ3 4.6s steps(1,end) infinite}
.ex-preloader .q4{animation:aExPrlQ4 4.6s steps(1,end) infinite}
.ex-preloader .q5{animation:aExPrlQ5 4.6s steps(1,end) infinite}
@keyframes aExPrlQ1{0%,10%{background:var(--s3)}12%,90%{background:var(--lime)}92%,100%{background:var(--s3)}}
@keyframes aExPrlQ2{0%,22%{background:var(--s3)}24%,90%{background:var(--lime)}92%,100%{background:var(--s3)}}
@keyframes aExPrlQ3{0%,34%{background:var(--s3)}36%,90%{background:var(--lime)}92%,100%{background:var(--s3)}}
@keyframes aExPrlQ4{0%,46%{background:var(--s3)}48%,90%{background:var(--lime)}92%,100%{background:var(--s3)}}
@keyframes aExPrlQ5{0%,58%{background:var(--s3)}60%,90%{background:var(--lime)}92%,100%{background:var(--s3)}}
@keyframes aExPrlBar{0%,11%{width:0}12%,23%{width:20%}24%,35%{width:40%}36%,47%{width:60%}
  48%,59%{width:80%}60%,90%{width:100%}92%,100%{width:0}}
@keyframes aExPrlV{0%{transform:translateY(0);opacity:0}
  1%,60%{transform:translateY(0);opacity:1}
  74%{transform:translateY(-100%);opacity:1}
  77%{transform:translateY(-100%);opacity:0}
  78%,100%{transform:translateY(0);opacity:0}}
@keyframes aExPrlSite{0%,20%{transform:translateY(0);opacity:1}
  30%,66%{transform:translateY(16px);opacity:0}
  80%,100%{transform:translateY(0);opacity:1}}

/* ---------------- curtain-transition ----------------
   La nav SURVIT. Le panneau ne couvre que la zone de contenu, et c est
   la vraie decision de conception derriere ce motif, decider ce qui
   persiste a travers l echange. La nav inerte au dessus est aussi ce
   qui prouve qu il ne s agit pas d un rechargement complet.
   L echange se fait a 29 pour cent, pendant que le panneau couvre tout
   de 24 a 34, et le retour a 79, pendant la seconde couverture. Deux
   balayages par cycle, ce qui laisse la boucle revenir a son point de
   depart sans qu on voie l echange.
   La pastille se remplit pendant la couverture et finit avant le
   depart du panneau, la ressource etait donc prechargee derriere.
   Gele, on verrait une nav, un panneau, et rien de ce qui compte. */
.ex-curtain-transition{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-curtain-transition .shell{display:flex;align-items:center;gap:9px;height:16px;flex:0 0 auto}
.ex-curtain-transition .shell .lg{display:block;width:16px;height:16px;border-radius:5px;
  background:var(--lime);flex:0 0 auto}
.ex-curtain-transition .shell .nv{display:block;width:22px;height:5px;border-radius:3px;
  background:var(--s4)}
.ex-curtain-transition .zone{position:relative;flex:1;border-radius:8px;overflow:hidden;min-height:0}
.ex-curtain-transition .pg{position:absolute;inset:0;padding:9px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:7px;background:var(--s1)}
.ex-curtain-transition .pg .im{display:block;height:44px;border-radius:7px}
.ex-curtain-transition .pg .t{display:block;height:6px;border-radius:3px}
.ex-curtain-transition .pg .t2{width:56%}
.ex-curtain-transition .pgA .im{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-curtain-transition .pgA .t{background:var(--s5)}
.ex-curtain-transition .pgB{opacity:0;animation:aExCtnB 5.6s steps(1,end) infinite}
.ex-curtain-transition .pgB .im{background:linear-gradient(135deg,var(--a2),var(--accent-deep))}
.ex-curtain-transition .pgB .t{background:var(--s4)}
.ex-curtain-transition .curtain{position:absolute;inset:0;background:var(--void);display:grid;
  place-items:center;animation:aExCtnC 5.6s cubic-bezier(.6,0,.2,1) infinite}
.ex-curtain-transition .pip{display:block;width:52px;height:4px;border-radius:2px;
  background:rgba(var(--ink-rgb),.2);overflow:hidden}
.ex-curtain-transition .pip b{display:block;height:4px;border-radius:2px;background:var(--lime);
  transform-origin:left;transform:scaleX(0);animation:aExCtnPip 5.6s cubic-bezier(.3,0,.3,1) infinite}
/* entre par le bas, sort par le haut, se replace hors cadre a 56.1 */
@keyframes aExCtnC{0%,6%{transform:translateY(100%)}
  24%,34%{transform:translateY(0)}
  46%,56%{transform:translateY(-100%)}
  56.1%{transform:translateY(100%)}
  74%,84%{transform:translateY(0)}
  96%,100%{transform:translateY(-100%)}}
@keyframes aExCtnB{0%,27%{opacity:0}29%,77%{opacity:1}79%,100%{opacity:0}}
@keyframes aExCtnPip{0%,22%{transform:scaleX(0)}32%,34%{transform:scaleX(1)}
  36%,72%{transform:scaleX(0)}82%,84%{transform:scaleX(1)}86%,100%{transform:scaleX(0)}}

/* ---------------- sticky-footer ----------------
   Le footer porte un formulaire et trois liens, l article porte un
   titre et quatre lignes. Ils sont differents, on peut donc dire lequel
   des deux voyage, et c est le seul moyen de montrer que le footer etait
   la depuis le premier rendu.
   Le footer fait 62 et la course vaut 62, la consigne d accorder la
   distance a la hauteur. La jauge de droite arrive en butee au meme
   instant que la fin de la decouverte, a 56 pour cent, ce qui dit que
   cela se produit en FIN de scroll.
   Gele, on verrait un article au dessus d un footer. */
.ex-sticky-footer{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-sticky-footer .ftr{position:absolute;left:0;right:12px;bottom:0;height:62px;background:var(--lime);
  padding:10px;box-sizing:border-box;display:flex;flex-direction:column;justify-content:space-between}
.ex-sticky-footer .ftr .fl{display:block;width:18px;height:18px;border-radius:5px;
  background:rgba(var(--on-accent-rgb),.75)}
.ex-sticky-footer .form{display:flex;gap:6px}
.ex-sticky-footer .form .in{display:block;flex:1;height:12px;border-radius:6px;
  background:rgba(var(--on-accent-rgb),.25)}
.ex-sticky-footer .form .bt{display:block;width:30px;height:12px;border-radius:6px;
  background:rgba(var(--on-accent-rgb),.75)}
.ex-sticky-footer .lk{display:flex;gap:7px}
.ex-sticky-footer .lk i{display:block;width:24px;height:4px;border-radius:2px;
  background:rgba(var(--on-accent-rgb),.45)}
.ex-sticky-footer .main{position:absolute;left:0;right:12px;top:0;bottom:0;background:var(--s1);
  padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:9px;
  animation:aExSfr 4.8s cubic-bezier(.4,0,.3,1) infinite}
.ex-sticky-footer .main i{display:block;height:6px;border-radius:3px;background:var(--s4)}
.ex-sticky-footer .main .h{height:11px;width:66%;border-radius:5px;background:var(--ink);opacity:.85}
.ex-sticky-footer .gg{position:absolute;right:0;top:8px;bottom:8px;width:4px;border-radius:2px;
  background:var(--s1)}
.ex-sticky-footer .gg i{position:absolute;left:0;right:0;top:0;height:26px;border-radius:2px;
  background:var(--s5);animation:aExSfrG 4.8s cubic-bezier(.4,0,.3,1) infinite}
/* footer de 62, course de 62 */
@keyframes aExSfr{0%,12%{transform:translateY(0)}56%,88%{transform:translateY(-62px)}
  100%{transform:translateY(0)}}
/* jauge de 134 pour un curseur de 26, course de 108, meme instant */
@keyframes aExSfrG{0%,12%{transform:translateY(0)}56%,88%{transform:translateY(108px)}
  100%{transform:translateY(0)}}

/* ---------------- odometer ----------------
   Le compteur repond a QUELQUE CHOSE. La pastille de periode active
   change a 8 pour cent et le nombre part juste apres, ce qui le
   rattache a une cause au lieu d en faire une decoration qui compte
   toute seule.
   Les colonnes ne parcourent pas la meme distance. Pour aller a 274 les
   centaines avancent de 2 crans soit 76, les dizaines de 7 soit 266, et
   les unites de 24, deux tours plus quatre, soit 912 sur une bobine de
   trente crans egale a 1140.
   Decalage de 60ms par rang depuis la gauche, les unites se posent donc
   en dernier.
   Gele, on verrait un chiffre sur une carte. */
.ex-odometer{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-odometer .chips{display:flex;gap:6px;flex:0 0 auto}
.ex-odometer .ch{display:block;width:26px;height:12px;border-radius:6px;background:var(--s2)}
.ex-odometer .c1{animation:aExOdoC1 5.2s steps(1,end) infinite}
.ex-odometer .c2{animation:aExOdoC2 5.2s steps(1,end) infinite}
.ex-odometer .lb{display:block;width:44px;height:5px;border-radius:3px;background:var(--s4)}
.ex-odometer .num{display:flex;align-items:center;gap:4px}
.ex-odometer .win{display:block;width:26px;height:38px;overflow:hidden;border-radius:6px;
  background:var(--s1);flex:0 0 auto}
.ex-odometer .reel{display:block;font:800 26px/38px var(--mono);color:var(--lime);text-align:center}
.ex-odometer .reel b{display:block;height:38px}
.ex-odometer .rh{animation:aExOdoH 5.2s cubic-bezier(.25,.9,.3,1) infinite}
.ex-odometer .rt{animation:aExOdoT 5.2s cubic-bezier(.25,.9,.3,1) infinite;animation-delay:.06s}
.ex-odometer .ru{animation:aExOdoU 5.2s cubic-bezier(.25,.9,.3,1) infinite;animation-delay:.12s}
.ex-odometer .delta{display:block;width:22px;height:10px;border-radius:5px;background:var(--a2);
  margin-left:6px;opacity:0;animation:aExOdoD 5.2s ease infinite}
.ex-odometer .spark{display:flex;align-items:flex-end;gap:4px;height:22px;margin-top:auto}
.ex-odometer .spark i{display:block;flex:1;border-radius:2px;background:var(--s3)}
.ex-odometer .spark i:nth-child(1){height:38%}
.ex-odometer .spark i:nth-child(2){height:56%}
.ex-odometer .spark i:nth-child(3){height:44%}
.ex-odometer .spark i:nth-child(4){height:72%}
.ex-odometer .spark i:nth-child(5){height:60%}
.ex-odometer .spark i:nth-child(6){height:88%}
.ex-odometer .spark i:nth-child(7){height:100%;background:var(--lime)}
/* la periode bascule a 8, le nombre part a 10, la cause precede l effet */
@keyframes aExOdoC1{0%,7%{background:var(--lime)}8%,92%{background:var(--s2)}93%,100%{background:var(--lime)}}
@keyframes aExOdoC2{0%,7%{background:var(--s2)}8%,92%{background:var(--lime)}93%,100%{background:var(--s2)}}
@keyframes aExOdoH{0%,10%{transform:translateY(0)}46%,60%{transform:translateY(-76px)}
  96%,100%{transform:translateY(0)}}
@keyframes aExOdoT{0%,10%{transform:translateY(0)}46%,60%{transform:translateY(-266px)}
  96%,100%{transform:translateY(0)}}
@keyframes aExOdoU{0%,10%{transform:translateY(0)}46%,60%{transform:translateY(-912px)}
  96%,100%{transform:translateY(0)}}
@keyframes aExOdoD{0%,44%{opacity:0}52%,88%{opacity:1}94%,100%{opacity:0}}

/* ---------------- gooey ----------------
   Un bouton d action et ses trois items. Le col se forme au moment du
   DETACHEMENT, ce qui est le seul instant ou la fusion se lit, et c est
   pour cela que la scene montre la sortie et non trois pastilles deja
   posees.
   Le filtre est sur le GROUPE. Les libelles sont dans une couche
   soeur, au dessus, jamais dans le groupe, parce que tout ce qui passe
   sous un seuillage perd son antialiasing et ressort crenele. C est la
   consigne, et ici elle est visible dans la structure.
   Le fond du groupe est opaque, sans quoi le contraste CSS, qui seuille
   le RVB et non l alpha, n aurait rien sur quoi mordre.
   Gele, on verrait un bouton et trois pastilles. */
.ex-gooey{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--void)}
.ex-gooey .page{position:absolute;left:12px;top:12px;right:64px;display:flex;flex-direction:column;gap:9px}
.ex-gooey .page i{display:block;height:6px;border-radius:3px;background:var(--s5);opacity:.45}
.ex-gooey .page i:nth-child(2){width:76%}
.ex-gooey .page i:nth-child(3){width:58%}
.ex-gooey .grp{position:absolute;right:0;bottom:0;width:70px;height:148px;
  filter:blur(7px) contrast(20)}
.ex-gooey .fab{position:absolute;right:16px;bottom:14px;display:block;width:34px;height:34px;
  border-radius:50%;background:var(--lime)}
.ex-gooey .it{position:absolute;right:20px;bottom:18px;display:block;width:26px;height:26px;
  border-radius:50%;background:var(--lime)}
.ex-gooey .i1{animation:aExGooI1 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-gooey .i2{animation:aExGooI2 3.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-gooey .i3{animation:aExGooI3 3.6s cubic-bezier(.3,.9,.3,1) infinite}
/* couche soeur, au dessus du filtre, jamais dedans */
.ex-gooey .labels{position:absolute;right:54px;bottom:0;z-index:3}
.ex-gooey .labels i{position:absolute;right:0;display:block;width:34px;height:5px;border-radius:3px;
  background:var(--ink);opacity:0}
.ex-gooey .l1{bottom:62px;animation:aExGooL1 3.6s ease infinite}
.ex-gooey .l2{bottom:90px;animation:aExGooL2 3.6s ease infinite}
.ex-gooey .l3{bottom:118px;animation:aExGooL3 3.6s ease infinite}
@keyframes aExGooI1{0%,10%{transform:translateY(0)}38%,72%{transform:translateY(-34px)}
  92%,100%{transform:translateY(0)}}
@keyframes aExGooI2{0%,14%{transform:translateY(0)}42%,72%{transform:translateY(-62px)}
  88%,100%{transform:translateY(0)}}
@keyframes aExGooI3{0%,18%{transform:translateY(0)}46%,72%{transform:translateY(-90px)}
  84%,100%{transform:translateY(0)}}
@keyframes aExGooL1{0%,36%{opacity:0}44%,72%{opacity:.8}82%,100%{opacity:0}}
@keyframes aExGooL2{0%,40%{opacity:0}48%,72%{opacity:.8}80%,100%{opacity:0}}
@keyframes aExGooL3{0%,44%{opacity:0}52%,72%{opacity:.8}78%,100%{opacity:0}}

/* ---------------- physics-playground ----------------
   Une page introuvable, le seul endroit ou ce jouet ne coute rien,
   puisqu il n y a rien a lire dessous. Le titre reste hors simulation,
   ce que la consigne demande.
   Les cinq blocs se posent LES UNS SUR LES AUTRES. Le sol est a 138,
   les blocs font 16, les trois du bas reposent donc a 122 et les deux
   du haut a 106, exactement sur le dessus des premiers. Ceux du haut
   arrivent en retard et prennent une rotation, ce que fait un corps qui
   touche hors de son axe.
   Les murs ont une epaisseur, un mur sans largeur laisse passer un
   corps rapide entre deux images.
   Gele, on verrait une pile, sans l ordre qui l a faite. */
.ex-physics-playground{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-physics-playground .h1{position:absolute;left:14px;top:14px;display:block;width:52px;height:12px;
  border-radius:5px;background:var(--ink);opacity:.85}
.ex-physics-playground .h2{position:absolute;left:14px;top:32px;display:block;width:88px;height:6px;
  border-radius:3px;background:var(--s4)}
.ex-physics-playground .wl{position:absolute;top:52px;bottom:6px;display:block;width:4px;
  border-radius:2px;background:var(--s2)}
.ex-physics-playground .left{left:6px}
.ex-physics-playground .right{right:6px}
.ex-physics-playground .floor{position:absolute;left:6px;right:6px;bottom:6px;display:block;height:4px;
  border-radius:2px;background:var(--s3)}
.ex-physics-playground .bd{position:absolute;display:block;width:32px;height:16px;border-radius:7px;
  background:var(--lime);opacity:0}
.ex-physics-playground .b1{left:18px;top:122px;animation:aExPhyF 4s cubic-bezier(.4,0,.7,1) infinite}
.ex-physics-playground .b2{left:56px;top:122px;animation:aExPhyF 4s cubic-bezier(.4,0,.7,1) infinite;
  animation-delay:.2s}
.ex-physics-playground .b3{left:94px;top:122px;animation:aExPhyF 4s cubic-bezier(.4,0,.7,1) infinite;
  animation-delay:.4s}
.ex-physics-playground .b4{left:36px;top:106px;animation:aExPhySA 4s cubic-bezier(.4,0,.7,1) infinite;
  animation-delay:.66s}
.ex-physics-playground .b5{left:74px;top:106px;animation:aExPhySB 4s cubic-bezier(.4,0,.7,1) infinite;
  animation-delay:.88s}
@keyframes aExPhyF{0%{transform:translateY(-88px);opacity:0}
  6%{transform:translateY(-88px);opacity:1}
  44%{transform:translateY(0)}52%{transform:translateY(-6px)}
  60%,92%{transform:translateY(0)}
  95%{transform:translateY(0);opacity:0}
  100%{transform:translateY(-88px);opacity:0}}
@keyframes aExPhySA{0%{transform:translateY(-72px) rotate(0);opacity:0}
  6%{transform:translateY(-72px) rotate(0);opacity:1}
  44%{transform:translateY(0) rotate(-7deg)}52%{transform:translateY(-5px) rotate(-3deg)}
  60%,92%{transform:translateY(0) rotate(-5deg)}
  95%{transform:translateY(0) rotate(-5deg);opacity:0}
  100%{transform:translateY(-72px) rotate(0);opacity:0}}
@keyframes aExPhySB{0%{transform:translateY(-72px) rotate(0);opacity:0}
  6%{transform:translateY(-72px) rotate(0);opacity:1}
  44%{transform:translateY(0) rotate(6deg)}52%{transform:translateY(-5px) rotate(2deg)}
  60%,92%{transform:translateY(0) rotate(4deg)}
  95%{transform:translateY(0) rotate(4deg);opacity:0}
  100%{transform:translateY(-72px) rotate(0);opacity:0}}

/* ---------------- zoom-transition ----------------
   Une grille de photos. Les huit autres cases NE BOUGENT PAS et gardent
   leur place, c est ce qui permet de dire que la vue revient dans son
   propre emplacement et pas simplement quelque part.
   Le cadre est interpole, gauche haut largeur hauteur, et le rayon
   passe de 8 a 12. Un scale non uniforme serait moins cher et
   ecraserait tout ce qui est dedans, y compris la barre d outils qui
   apparait a l ouverture.
   Le retour porte une autre courbe, plus lente, parce qu il est pilote
   par un geste et non par une duree.
   Grille de 3 sur 3 en cases de 48 sur 40 avec 6 de gouttiere, soit 156
   sur 132, la zone exacte que la case ouverte vient occuper, et qui est
   aussi l interieur exact du cadre une fois le padding de 8 retire.
   Gele, on verrait une grille avec une case ouverte, sans savoir
   laquelle. */
.ex-zoom-transition{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:8px;box-sizing:border-box}
.ex-zoom-transition .cell{position:absolute;display:block;width:48px;height:40px;border-radius:8px;
  background:var(--s3)}
.ex-zoom-transition .cell:nth-of-type(1){left:8px;top:8px}
.ex-zoom-transition .cell:nth-of-type(2){left:62px;top:8px}
.ex-zoom-transition .cell:nth-of-type(3){left:116px;top:8px}
.ex-zoom-transition .cell:nth-of-type(4){left:8px;top:54px}
.ex-zoom-transition .cell:nth-of-type(5){left:62px;top:54px}
.ex-zoom-transition .cell:nth-of-type(6){left:116px;top:54px}
.ex-zoom-transition .cell:nth-of-type(7){left:8px;top:100px}
.ex-zoom-transition .cell:nth-of-type(8){left:62px;top:100px}
.ex-zoom-transition .hot{left:116px;top:100px;z-index:3;overflow:hidden;padding:9px;box-sizing:border-box;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 60%,var(--accent-deep));
  display:flex;flex-direction:column;gap:7px;justify-content:flex-end;
  animation:aExZoomT 5s cubic-bezier(.3,.9,.3,1) infinite}
.ex-zoom-transition .tb{position:absolute;left:9px;right:9px;top:9px;display:flex;
  justify-content:space-between;opacity:0;animation:aExZoomD 5s ease infinite}
.ex-zoom-transition .tb i{display:block;width:12px;height:12px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.7)}
.ex-zoom-transition .hot .d1,.ex-zoom-transition .hot .d2{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.65);opacity:0;animation:aExZoomD 5s ease infinite}
.ex-zoom-transition .hot .d1{width:64%}
.ex-zoom-transition .hot .d2{width:42%}
@keyframes aExZoomT{0%,12%{left:116px;top:100px;width:48px;height:40px;border-radius:8px}
  46%,74%{left:8px;top:8px;width:156px;height:132px;border-radius:12px;
    animation-timing-function:cubic-bezier(.4,0,.2,1)}
  96%,100%{left:116px;top:100px;width:48px;height:40px;border-radius:8px}}
@keyframes aExZoomD{0%,34%{opacity:0}48%,72%{opacity:1}84%,100%{opacity:0}}

/* ---------------- sheet-detent ----------------
   Une feuille de recherche sur une carte. Le cran se prouve en le
   RATANT, la feuille est tiree jusqu a 48 pour cent, au dessus du cran
   pose a 58, puis relachee, et elle redescend s y poser. Sans ce
   depassement suivi du retour, une feuille qui s arrete quelque part ne
   dit pas qu il y avait un cran.
   Le contenu change d un cran a l autre, deux lignes de resume a medium
   et quatre resultats a large, la consigne explicite. Un cran est un
   etat du panneau, pas une taille.
   Le curseur suit le bord haut pendant les glissements et se leve au
   relachement, ce qui separe le geste du ressort.
   La feuille fait 150. translateY 58 pour cent laisse 63 au dessus du
   bas, position du repere s1, et 14 pour cent en laisse 129, position
   de s2.
   Gele, on verrait un panneau a mi hauteur sur une carte. */
.ex-sheet-detent{position:relative;width:150px;height:150px;border-radius:14px;overflow:hidden;
  background:var(--s1)}
.ex-sheet-detent .map{position:absolute;inset:0;background:var(--s0)}
.ex-sheet-detent .rd{position:absolute;display:block;background:var(--s2);border-radius:2px}
.ex-sheet-detent .r1{left:-10px;top:34px;right:-10px;height:7px;transform:rotate(-9deg)}
.ex-sheet-detent .r2{left:44px;top:-10px;width:7px;bottom:-10px;transform:rotate(7deg)}
.ex-sheet-detent .pin{position:absolute;left:92px;top:26px;display:block;width:12px;height:12px;
  border-radius:50% 50% 50% 2px;background:var(--a2);transform:rotate(45deg)}
.ex-sheet-detent .stop{position:absolute;left:0;right:0;display:block;height:1px;
  background:rgba(var(--accent-rgb),.3)}
.ex-sheet-detent .s1{bottom:63px}
.ex-sheet-detent .s2{bottom:129px}
.ex-sheet-detent .sheet{position:absolute;left:0;right:0;bottom:0;height:150px;
  border-radius:14px 14px 0 0;background:var(--lime);padding:14px 12px 0;box-sizing:border-box;
  animation:aExDet 6.2s cubic-bezier(.2,1.5,.3,1) infinite}
.ex-sheet-detent .grab{position:absolute;top:6px;left:50%;margin-left:-13px;display:block;width:26px;
  height:3px;border-radius:2px;background:rgba(var(--on-accent-rgb),.4)}
.ex-sheet-detent .field{display:block;height:14px;border-radius:7px;
  background:rgba(var(--on-accent-rgb),.28)}
.ex-sheet-detent .ct{position:absolute;left:12px;right:12px;top:38px;display:flex;
  flex-direction:column;gap:7px;opacity:0}
.ex-sheet-detent .ct i{display:block;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.55)}
.ex-sheet-detent .sum i:first-child{width:70%}
.ex-sheet-detent .list i:nth-child(2){width:78%}
.ex-sheet-detent .list i:nth-child(4){width:58%}
.ex-sheet-detent .sum{animation:aExDetSum 6.2s steps(1,end) infinite}
.ex-sheet-detent .list{animation:aExDetList 6.2s steps(1,end) infinite}
.ex-sheet-detent .cur{left:62px;top:-8px;animation:aExDetCur 6.2s cubic-bezier(.2,1.5,.3,1) infinite}
@keyframes aExDet{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 aExDetSum{0%,27%{opacity:0}29%,53%{opacity:1}55%,100%{opacity:0}}
@keyframes aExDetList{0%,60%{opacity:0}62%,86%{opacity:1}88%,100%{opacity:0}}
@keyframes aExDetCur{0%{transform:translateY(158px);opacity:0}
  8%{transform:translateY(158px);opacity:1}
  20%{transform:translateY(73px);opacity:1}
  24%{transform:translateY(73px);opacity:0}
  42%{transform:translateY(88px);opacity:0}
  46%{transform:translateY(88px);opacity:1}
  54%{transform:translateY(10px);opacity:1}
  58%{transform:translateY(10px);opacity:0}
  99%,100%{transform:translateY(158px);opacity:0}}

/* ---------------- interactive-dismiss ----------------
   Un visionneur de photo. La grille du dessous reste VIVANTE, sa
   derniere vignette continue de battre pendant toute la fermeture, ce
   que la consigne demande explicitement.
   Le seuil pointille est pose a 44. Premier temps, un glissement LENT
   mene jusqu a 62, bien au dela, puis relache, et la photo revient.
   Second temps, une chiquenaude a 28 seulement, en deca du seuil, et
   elle part. La vitesse decide, pas la distance, et un lecteur qui
   croit le contraire se trompe deux fois dans la meme scene.
   La photo est repropose de 92 a 100 pour cent, la boucle revient donc
   a son point de depart sans rembobinage visible.
   Gele, on verrait une photo a mi hauteur, sans savoir si elle part. */
.ex-interactive-dismiss{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:10px;box-sizing:border-box}
.ex-interactive-dismiss .gri{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ex-interactive-dismiss .gri i{display:block;height:38px;border-radius:6px;background:var(--s3)}
.ex-interactive-dismiss .beat{background:var(--a2);animation:aExIdmBeat 1.2s ease-in-out infinite}
.ex-interactive-dismiss .thr{position:absolute;left:0;right:0;top:76px;display:block;height:1px;z-index:2;
  background:repeating-linear-gradient(90deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.ex-interactive-dismiss .shot{position:absolute;left:0;right:0;bottom:0;height:118px;
  border-radius:12px 12px 0 0;z-index:3;
  background:linear-gradient(135deg,var(--lime),var(--accent-2) 60%,var(--accent-deep));
  animation:aExIdm 5.2s cubic-bezier(.3,.8,.3,1) infinite}
.ex-interactive-dismiss .shot .bar{position:absolute;top:7px;left:50%;margin-left:-13px;display:block;
  width:26px;height:3px;border-radius:2px;background:rgba(var(--on-accent-rgb),.5)}
.ex-interactive-dismiss .cur{left:78px;top:44px;z-index:4;
  animation:aExIdmCur 5.2s cubic-bezier(.3,.8,.3,1) infinite}
/* lent jusqu a 62, au dela du seuil de 44, annule quand meme.
   rapide jusqu a 28, en deca du seuil, valide quand meme. */
@keyframes aExIdm{0%,6%{transform:translateY(0)}
  34%,38%{transform:translateY(62px)}
  52%,58%{transform:translateY(0)}
  64%,66%{transform:translateY(28px)}
  80%,92%{transform:translateY(150px)}
  100%{transform:translateY(0)}}
@keyframes aExIdmCur{0%{transform:translateY(0);opacity:0}
  6%{transform:translateY(0);opacity:1}
  34%{transform:translateY(62px);opacity:1}
  38%{transform:translateY(62px);opacity:0}
  57%{transform:translateY(0);opacity:0}
  59%{transform:translateY(0);opacity:1}
  64%{transform:translateY(28px);opacity:1}
  67%{transform:translateY(28px);opacity:0}
  99%,100%{transform:translateY(0);opacity:0}}
@keyframes aExIdmBeat{0%,100%{opacity:.45}50%{opacity:1}}

/* ---------------- symbol-effect ----------------
   Trois lignes de reglages. Seule la premiere est en cours de
   connexion, et son icone allume ses arcs vers l exterieur, couche par
   couche. Les deux autres portent le MEME symbole, entierement
   immobile, l une allumee et l autre eteinte.
   C est le meilleur usage reel de cette famille, l animation EST
   l etat. Elle s arrete quand la connexion aboutit, elle ne decore pas.
   Les deux lignes inertes prouvent que l effet appartient au symbole
   qui a quelque chose a dire, pas au panneau.
   Une keyframe par couche plutot qu un delai partage, sinon la remise a
   zero de la derniere deborderait du cycle.
   Gele, on verrait trois lignes dont une a moitie allumee. */
.ex-symbol-effect{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-symbol-effect .rw{display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;
  box-sizing:border-box;border-radius:9px;background:var(--s1)}
.ex-symbol-effect .ico{display:block;width:26px;height:21px;flex:0 0 auto}
.ex-symbol-effect .gl{display:block;width:26px;height:21px}
.ex-symbol-effect .gl path{fill:none;stroke-width:9;stroke-linecap:round}
.ex-symbol-effect .tx{display:block;flex:1;height:6px;border-radius:3px;background:var(--s5)}
.ex-symbol-effect .t2{max-width:52%}
.ex-symbol-effect .t3{max-width:38%}
.ex-symbol-effect .st{display:block;width:10px;height:10px;border-radius:50%;background:var(--lime);
  flex:0 0 auto;animation:aExSfxPend 2.8s ease-in-out infinite}
.ex-symbol-effect .sw{width:22px;height:12px;border-radius:7px;background:var(--s3);animation:none}
/* la ligne en cours de connexion, les couches s allument vers l exterieur */
.ex-symbol-effect .vc .a0{fill:var(--s3);animation:aExSfxL0 2.8s steps(1,end) infinite}
.ex-symbol-effect .vc .a1{stroke:var(--s3);animation:aExSfxL1 2.8s steps(1,end) infinite}
.ex-symbol-effect .vc .a2{stroke:var(--s3);animation:aExSfxL2 2.8s steps(1,end) infinite}
.ex-symbol-effect .vc .a3{stroke:var(--s3);animation:aExSfxL3 2.8s steps(1,end) infinite}
/* les deux voisines, entierement immobiles */
.ex-symbol-effect .on .a0{fill:var(--lime)}
.ex-symbol-effect .on path{stroke:var(--lime)}
.ex-symbol-effect .off .a0{fill:var(--s3)}
.ex-symbol-effect .off path{stroke:var(--s3)}
@keyframes aExSfxL0{0%,7%{fill:var(--s3)}8%,92%{fill:var(--lime)}94%,100%{fill:var(--s3)}}
@keyframes aExSfxL1{0%,23%{stroke:var(--s3)}24%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aExSfxL2{0%,39%{stroke:var(--s3)}40%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aExSfxL3{0%,55%{stroke:var(--s3)}56%,92%{stroke:var(--lime)}94%,100%{stroke:var(--s3)}}
@keyframes aExSfxPend{0%,100%{opacity:.35}50%{opacity:1}}

/* ---------------- phase-animator ----------------
   Un bouton d envoi et quatre phases. A chaque frontiere, position,
   echelle ET rotation basculent ENSEMBLE, ce qui est la difference avec
   les pistes de keyframes. Chaque phase porte sa propre courbe, un
   ressort a l appui, une detente lineaire a l envoi, un amorti a la
   confirmation.
   Les quatre pastilles disent quelle phase est en cours. Sans elles une
   liste ordonnee d etats ne se distingue pas d une animation continue,
   et c est justement la liste qui est le terme.
   Le champ du bas reste inerte, il donne au bouton son contexte.
   Gele, on verrait un bouton quelque part sur son trajet. */
.ex-phase-animator{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-phase-animator .pips{position:absolute;left:0;right:0;top:12px;display:flex;justify-content:center;
  gap:8px}
.ex-phase-animator .pips i{display:block;width:7px;height:7px;border-radius:50%;background:var(--s3)}
.ex-phase-animator .p1{animation:aExPhaP1 3.4s steps(1,end) infinite}
.ex-phase-animator .p2{animation:aExPhaP2 3.4s steps(1,end) infinite}
.ex-phase-animator .p3{animation:aExPhaP3 3.4s steps(1,end) infinite}
.ex-phase-animator .p4{animation:aExPhaP4 3.4s steps(1,end) infinite}
.ex-phase-animator .btn{position:absolute;left:50%;top:66px;margin-left:-30px;display:grid;
  place-items:center;width:60px;height:30px;border-radius:15px;background:var(--lime);
  animation:aExPha 3.4s infinite}
.ex-phase-animator .glyph{display:block;width:22px;height:5px;border-radius:3px;
  background:var(--on-accent);opacity:.85}
.ex-phase-animator .fld{position:absolute;left:12px;right:12px;top:104px;display:flex;
  flex-direction:column;gap:8px;padding:8px;box-sizing:border-box;border-radius:8px;background:var(--s1)}
.ex-phase-animator .fld i{display:block;height:6px;border-radius:3px;background:var(--s4)}
.ex-phase-animator .f2{width:56%}
/* une courbe par phase, et toutes les proprietes basculent ensemble */
@keyframes aExPha{
  0%{transform:translateY(0) scale(1) rotate(0);animation-timing-function:cubic-bezier(.3,1.5,.4,1)}
  25%{transform:translateY(4px) scale(.9) rotate(0);animation-timing-function:linear}
  50%{transform:translateY(-32px) scale(.66) rotate(-10deg);animation-timing-function:cubic-bezier(.5,0,.8,.3)}
  75%{transform:translateY(-6px) scale(1.08) rotate(0);animation-timing-function:cubic-bezier(.2,.9,.3,1)}
  100%{transform:translateY(0) scale(1) rotate(0)}}
@keyframes aExPhaP1{0%,24%{background:var(--lime)}25%,99%{background:var(--s3)}100%{background:var(--lime)}}
@keyframes aExPhaP2{0%,24%{background:var(--s3)}25%,49%{background:var(--lime)}50%,100%{background:var(--s3)}}
@keyframes aExPhaP3{0%,49%{background:var(--s3)}50%,74%{background:var(--lime)}75%,100%{background:var(--s3)}}
@keyframes aExPhaP4{0%,74%{background:var(--s3)}75%,99%{background:var(--lime)}100%{background:var(--s3)}}

/* ---------------- keyframe-track ----------------
   Une icone d app et trois pistes sur UNE SEULE duree partagee. Les
   trois tetes de lecture avancent en verrouillage et chacune atteint
   son repere a un instant different, 30, 45 puis 62 pour cent. L
   echelle culmine quand la tete de sa piste touche son repere, le
   decalage plus tard, la rotation en dernier, donc les proprietes se
   RECOUVRENT au lieu de basculer ensemble.
   C est exactement le contraire de phase-animator, et c est pour cela
   que les deux fiches se citent.
   La piste fait 96 et la tete 2, la course vaut donc 96, et un repere a
   30 pour cent est pose a 28.8, un a 45 pour cent a 43.2, un a 62 pour
   cent a 59.5.
   Trois elements imbriques, un par propriete, comme le terme le decrit.
   Gele, on verrait une icone et trois barres. */
.ex-keyframe-track{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-keyframe-track .stz{display:grid;place-items:center;height:54px;flex:0 0 auto}
.ex-keyframe-track .sq{display:block;width:38px;height:38px;
  animation:aExKftOff 3.4s ease-in-out infinite}
.ex-keyframe-track .sqR{display:block;width:38px;height:38px;
  animation:aExKftRot 3.4s ease-in-out infinite}
.ex-keyframe-track .sqS{display:block;width:38px;height:38px;border-radius:11px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExKftScale 3.4s ease-in-out infinite}
.ex-keyframe-track .lanes{display:flex;flex-direction:column;gap:9px}
.ex-keyframe-track .lane{display:flex;align-items:center;gap:8px}
.ex-keyframe-track .lb{font:600 9px/1 var(--mono);color:var(--ink-3);width:40px;text-align:right;
  font-style:normal;letter-spacing:.04em}
.ex-keyframe-track .tk{position:relative;display:block;width:96px;height:7px;border-radius:4px;
  background:var(--s2)}
.ex-keyframe-track .mk{position:absolute;top:-2px;display:block;width:2px;height:11px;border-radius:1px;
  background:rgba(var(--accent-rgb),.55)}
.ex-keyframe-track .m1{left:28.8px}
.ex-keyframe-track .m2{left:43.2px}
.ex-keyframe-track .m3{left:59.5px}
.ex-keyframe-track .ph{position:absolute;left:0;top:-3px;display:block;width:2px;height:13px;
  border-radius:1px;background:var(--ink);animation:aExKftHead 3.4s linear infinite}
/* la tete s eteint aux deux bouts, le rembobinage ne se voit pas */
@keyframes aExKftHead{0%{transform:translateX(0);opacity:0}
  4%{opacity:1}96%{opacity:1}
  100%{transform:translateX(96px);opacity:0}}
@keyframes aExKftScale{0%,100%{transform:scale(1)}30%{transform:scale(1.34)}}
@keyframes aExKftOff{0%,100%{transform:translateY(0)}45%{transform:translateY(-16px)}}
@keyframes aExKftRot{0%,100%{transform:rotate(0)}62%{transform:rotate(96deg)}}

/* ---------------- context-menu-lift ----------------
   Un fil de conversation. L appui se voit AVANT la levee, l anneau
   grandit de 10 a 26 pour cent, soit les 400 a 500ms de la consigne,
   puis la bulle se souleve, puis la barre de reactions et le menu
   arrivent. Sans le temps d appui, rien ne dirait pourquoi la bulle
   monte.
   Le flou et l assombrissement tombent sur LES MEMES images que la
   levee. Les separer se lirait comme deux evenements.
   L echelle vaut 1.03, la valeur de la consigne, et c est pour cela que
   la profondeur est portee par le contraste avec le fond et non par le
   grandissement, qui seul serait presque invisible.
   Gele, on verrait une bulle nette entre trois bulles floues. */
.ex-context-menu-lift{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:10px;box-sizing:border-box}
.ex-context-menu-lift .bb{position:absolute;display:block;height:22px;border-radius:11px;
  background:var(--s3)}
.ex-context-menu-lift .me{background:var(--s5)}
.ex-context-menu-lift .b1{left:10px;top:10px;width:88px;animation:aExCmlBg 4.6s ease infinite}
.ex-context-menu-lift .b2{right:10px;top:40px;width:74px;animation:aExCmlBg 4.6s ease infinite;
  animation-delay:.03s}
.ex-context-menu-lift .b3{right:10px;top:70px;width:100px;background:var(--lime);z-index:4;
  animation:aExCmlLift 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-context-menu-lift .b4{left:10px;top:100px;width:66px;animation:aExCmlBg 4.6s ease infinite;
  animation-delay:.06s}
.ex-context-menu-lift .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:aExCmlPress 4.6s cubic-bezier(.3,0,.4,1) infinite}
.ex-context-menu-lift .react{position:absolute;right:10px;top:44px;display:flex;gap:6px;padding:5px 7px;
  border-radius:12px;background:var(--s1);z-index:5;transform-origin:100% 100%;
  animation:aExCmlReact 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-context-menu-lift .react i{display:block;width:12px;height:12px;border-radius:50%;
  background:var(--s5)}
.ex-context-menu-lift .menu{position:absolute;right:10px;top:98px;display:flex;flex-direction:column;
  gap:7px;width:104px;padding:9px;box-sizing:border-box;border-radius:9px;background:var(--s1);z-index:5;
  transform-origin:100% 0;animation:aExCmlMenu 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-context-menu-lift .menu i{display:block;height:5px;border-radius:3px;background:var(--s5)}
.ex-context-menu-lift .menu i:nth-child(2){width:72%}
.ex-context-menu-lift .menu i:nth-child(3){width:56%}
@keyframes aExCmlLift{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 aExCmlBg{0%,26%{filter:blur(0);opacity:1}
  34%,80%{filter:blur(2.6px);opacity:.4}
  92%,100%{filter:blur(0);opacity:1}}
@keyframes aExCmlPress{0%,10%{transform:scale(1);opacity:0}
  14%{opacity:.9}26%{transform:scale(5);opacity:0}100%{transform:scale(1);opacity:0}}
@keyframes aExCmlReact{0%,34%{transform:scale(.6);opacity:0}
  44%,80%{transform:scale(1);opacity:1}90%,100%{transform:scale(.6);opacity:0}}
@keyframes aExCmlMenu{0%,38%{transform:scaleY(.6);opacity:0}
  48%,80%{transform:scaleY(1);opacity:1}90%,100%{transform:scaleY(.6);opacity:0}}

/* ---------------- dynamic-island ----------------
   Une activite musicale, ses TROIS presentations declarees. Minimal 26
   sur 18, compact 92 sur 24, expanded 130 sur 62, et le rayon vaut
   toujours la moitie de la plus petite dimension, donc 9, 12 puis 31.
   La surface reste une vraie capsule a chaque taille intermediaire.
   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 donnerait 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 avec du contenu dedans. */
.ex-dynamic-island{position:relative;width:172px;height:140px;border-radius:11px;overflow:hidden}
.ex-dynamic-island .wall{position:absolute;inset:0;background:var(--void)}
.ex-dynamic-island .pill{position:absolute;left:50%;top:16px;margin-left:-13px;display:block;
  width:26px;height:18px;border-radius:9px;background:var(--s0);overflow:hidden;
  animation:aExDin 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-dynamic-island .st{position:absolute;inset:0;display:flex;align-items:center;gap:8px;
  padding:0 9px;box-sizing:border-box;opacity:0}
.ex-dynamic-island .minimal{justify-content:center;animation:aExDinS1 5.4s ease infinite}
.ex-dynamic-island .compact{animation:aExDinS2 5.4s ease infinite}
.ex-dynamic-island .expanded{animation:aExDinS3 5.4s ease infinite;padding:10px}
.ex-dynamic-island .minimal i{display:block;width:7px;height:7px;border-radius:50%;background:var(--lime)}
.ex-dynamic-island .art{display:block;width:14px;height:14px;border-radius:4px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));flex:0 0 auto}
.ex-dynamic-island .big{width:40px;height:40px;border-radius:9px}
.ex-dynamic-island .wv{display:flex;align-items:flex-end;gap:3px;height:12px}
.ex-dynamic-island .wv b{display:block;width:3px;border-radius:2px;background:var(--lime)}
.ex-dynamic-island .wv b:nth-child(1){height:40%}
.ex-dynamic-island .wv b:nth-child(2){height:90%}
.ex-dynamic-island .wv b:nth-child(3){height:60%}
.ex-dynamic-island .wv b:nth-child(4){height:100%}
.ex-dynamic-island .col{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.ex-dynamic-island .col>i{display:block;height:5px;border-radius:3px;background:var(--s5)}
.ex-dynamic-island .col>i:nth-child(2){width:58%}
.ex-dynamic-island .ctl{display:flex;gap:8px;margin-top:2px}
.ex-dynamic-island .ctl b{display:block;width:10px;height:10px;border-radius:3px;background:var(--s4)}
.ex-dynamic-island .ctl b:nth-child(2){background:var(--lime)}
/* rayon toujours egal a la moitie de la plus petite dimension */
@keyframes aExDin{0%,10%{width:26px;height:18px;margin-left:-13px;border-radius:9px}
  22%,40%{width:92px;height:24px;margin-left:-46px;border-radius:12px}
  54%,76%{width:130px;height:62px;margin-left:-65px;border-radius:31px}
  90%,100%{width:26px;height:18px;margin-left:-13px;border-radius:9px}}
@keyframes aExDinS1{0%,8%{opacity:1}12%,88%{opacity:0}94%,100%{opacity:1}}
@keyframes aExDinS2{0%,20%{opacity:0}26%,44%{opacity:1}50%,100%{opacity:0}}
@keyframes aExDinS3{0%,52%{opacity:0}58%,76%{opacity:1}82%,100%{opacity:0}}

/* ---------------- liquid-glass ----------------
   Une barre flottante sur une photo. Le reflet est CALCULE sur le
   decalage de la barre, memes instants, signe oppose, facteur 0.45,
   donc quand la barre descend de 46 le reflet remonte de 21. Un vrai
   reflet speculaire se deplace a l inverse de la surface qui le porte.
   Un reflet sur sa propre timeline continuerait de balayer la barre a
   l arret, ce qui est une brillance et non un materiau.
   Les coins du BAS se resserrent de 20 a 8 pendant l accostage, ce que
   la consigne demande. Une forme sur le point de fusionner avec un bord
   cesse de pretendre flotter.
   La barre repose a 62 et fait 40, son bord bas est donc a 102, et une
   course de 46 l amene exactement a 148, le bord du cadre.
   Gele, on verrait une barre de verre sur une photo. */
.ex-liquid-glass{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden}
.ex-liquid-glass .photo{position:absolute;inset:0;
  background:linear-gradient(120deg,var(--lime),var(--a3) 45%,var(--a4) 75%,var(--accent-deep))}
.ex-liquid-glass .glass{position:absolute;left:18px;top:62px;width:136px;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;display:flex;align-items:center;
  animation:aExLgl 5.4s ease-in-out infinite}
.ex-liquid-glass .spec{position:absolute;left:-30px;top:-24px;display:block;width:100px;height:90px;
  background:linear-gradient(160deg,transparent,rgba(var(--ink-rgb),.7),transparent);
  animation:aExLglSpec 5.4s ease-in-out infinite}
.ex-liquid-glass .tools{position:relative;z-index:2;display:flex;justify-content:space-between;
  width:100%;padding:0 14px;box-sizing:border-box}
.ex-liquid-glass .tools b{display:block;width:14px;height:14px;border-radius:4px;
  background:rgba(var(--ink-rgb),.75)}
/* les coins du bas se resserrent pendant l accostage */
@keyframes aExLgl{0%,8%{transform:translateY(0);border-radius:20px}
  46%,62%{transform:translateY(46px);border-radius:20px 20px 8px 8px}
  94%,100%{transform:translateY(0);border-radius:20px}}
/* meme cycle, memes instants, signe oppose, 0.45 fois la course */
@keyframes aExLglSpec{0%,8%{transform:translateY(0)}
  46%,62%{transform:translateY(-21px)}
  94%,100%{transform:translateY(0)}}

/* ---------------- haptic-sync ----------------
   Un controle segmente. Le glissement du curseur ne declenche RIEN, et
   c est le point. Un anneau part a l instant exact ou le curseur se
   pose sur un cran, une fois par arrivee, jamais entre les deux.
   La consigne interdit nommement de frapper a chaque image d un
   glissement continu, et cette scene rend l interdit lisible en
   montrant les longs silences pendant les trajets.
   Le controle fait 148 pour trois crans, soit 47.3 par cran. Le curseur
   fait 44 et part de 3, ses trois positions sont donc 3, 50.3 et 97.6,
   pour des courses de 47.3 et 94.6.
   Chaque anneau est pose sur la position ou il se declenche, pas au
   milieu du controle.
   Gele, on verrait un controle segmente. */
.ex-haptic-sync{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-haptic-sync .lb{display:block;width:52px;height:5px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-haptic-sync .sgm{position:relative;display:flex;height:28px;border-radius:9px;background:var(--s2);
  padding:3px;box-sizing:border-box;flex:0 0 auto}
.ex-haptic-sync .sl{display:block;flex:1;z-index:2}
.ex-haptic-sync .thumb{position:absolute;left:3px;top:3px;display:block;width:44px;height:22px;
  border-radius:7px;background:var(--lime);animation:aExHptThumb 5.2s cubic-bezier(.3,1.4,.35,1) infinite}
.ex-haptic-sync .ring{position:absolute;top:0;display:block;width:44px;height:28px;border-radius:9px;
  box-sizing:border-box;border:2px solid var(--lime);opacity:0}
.ex-haptic-sync .r1{left:3px;animation:aExHptR1 5.2s cubic-bezier(.2,.7,.3,1) infinite}
.ex-haptic-sync .r2{left:50.3px;animation:aExHptR2 5.2s cubic-bezier(.2,.7,.3,1) infinite}
.ex-haptic-sync .r3{left:97.6px;animation:aExHptR3 5.2s cubic-bezier(.2,.7,.3,1) infinite}
.ex-haptic-sync .bdy{display:flex;flex-direction:column;gap:9px;margin-top:4px}
.ex-haptic-sync .bdy i{display:block;height:6px;border-radius:3px;background:var(--s3)}
.ex-haptic-sync .bdy i:nth-child(2){width:74%}
.ex-haptic-sync .bdy i:nth-child(3){width:58%}
/* trois crans, 3 puis 50.3 puis 97.6, arrivees a 22, 46 et 74 pour cent */
@keyframes aExHptThumb{0%,10%{transform:translateX(0)}
  22%,34%{transform:translateX(47.3px)}
  46%,62%{transform:translateX(94.6px)}
  74%,86%{transform:translateX(47.3px)}
  98%,100%{transform:translateX(0)}}
/* une frappe par arrivee, rien pendant les trajets */
@keyframes aExHptR1{0%,96%{transform:scale(.9);opacity:0}
  98%{transform:scale(.9);opacity:.9}
  100%{transform:scale(1.5);opacity:0}}
@keyframes aExHptR2{0%,20%{transform:scale(.9);opacity:0}
  22%{transform:scale(.9);opacity:.9}
  30%{transform:scale(1.5);opacity:0}
  72%{transform:scale(.9);opacity:0}
  74%{transform:scale(.9);opacity:.9}
  82%,100%{transform:scale(1.5);opacity:0}}
@keyframes aExHptR3{0%,44%{transform:scale(.9);opacity:0}
  46%{transform:scale(.9);opacity:.9}
  54%,100%{transform:scale(1.5);opacity:0}}

/* ---------------- gaze-hover ----------------
   Une barre de controle spatiale, trois formes differentes. Le
   surlignage suit le rayon de CHACUNE, pastille, disque et carre, ce qui
   est la consigne de decouper l effet sur la vraie zone cliquable.
   Une seule 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.
   Les deux formes non regardees sont les voisins inertes qui prouvent
   que l effet appartient au regard.
   La barre fait 52 plus 7 plus 30 plus 7 plus 30, soit 126, la largeur
   exacte de l interieur du panneau. Les centres tombent donc a 48, 96 et
   133, pour des courses de 48 et 85 depuis le premier.
   Gele, on verrait trois controles dont un allume, sans savoir pourquoi. */
.ex-gaze-hover{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:linear-gradient(150deg,var(--accent-deep),var(--void))}
.ex-gaze-hover .panel{position:absolute;left:10px;right:10px;top:10px;bottom:10px;border-radius:14px;
  background:rgba(var(--ink-rgb),.1);border:1px solid rgba(var(--ink-rgb),.18);padding:12px;
  box-sizing:border-box;display:flex;flex-direction:column}
.ex-gaze-hover .ttl{display:block;height:9px;width:58%;border-radius:4px;background:var(--ink);
  opacity:.85}
.ex-gaze-hover .sub{display:block;height:6px;width:38%;border-radius:3px;background:var(--ink);
  opacity:.4;margin-top:9px}
.ex-gaze-hover .bar{display:flex;align-items:center;gap:7px;margin-top:auto}
.ex-gaze-hover .tg{display:block;background:rgba(var(--ink-rgb),.2)}
.ex-gaze-hover .pill{width:52px;height:28px;border-radius:14px;
  animation:aExGazeA 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-gaze-hover .round{width:30px;height:30px;border-radius:50%;
  animation:aExGazeB 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-gaze-hover .sq{width:30px;height:30px;border-radius:9px;
  animation:aExGazeC 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-gaze-hover .eye{position:absolute;left:43px;top:106px;display:block;width:10px;height:10px;
  border-radius:50%;background:var(--ink);box-shadow:0 0 0 5px rgba(var(--ink-rgb),.16);z-index:3;
  animation:aExGazeEye 4.6s ease-in-out infinite}
@keyframes aExGazeEye{0%,8%{transform:translateX(0)}
  20%,34%{transform:translateX(48px)}
  46%,60%{transform:translateX(85px)}
  72%,100%{transform:translateX(0)}}
@keyframes aExGazeA{0%,8%{background:rgba(var(--ink-rgb),.42);transform:translateY(-2px)}
  9%,71%{background:rgba(var(--ink-rgb),.2);transform:translateY(0)}
  72%,100%{background:rgba(var(--ink-rgb),.42);transform:translateY(-2px)}}
@keyframes aExGazeB{0%,19%{background:rgba(var(--ink-rgb),.2);transform:translateY(0)}
  20%,34%{background:rgba(var(--ink-rgb),.42);transform:translateY(-2px)}
  35%,100%{background:rgba(var(--ink-rgb),.2);transform:translateY(0)}}
@keyframes aExGazeC{0%,45%{background:rgba(var(--ink-rgb),.2);transform:translateY(0)}
  46%,60%{background:rgba(var(--ink-rgb),.42);transform:translateY(-2px)}
  61%,100%{background:rgba(var(--ink-rgb),.2);transform:translateY(0)}}

/* ---------------- scroll-snap ----------------
   La VITESSE choisit l index. Le glissement lent de 8 a 28 pour cent
   parcourt 68, une carte. La chiquenaude de 40 a 48 parcourt 136, deux
   cartes, dans un temps deux fois et demie plus court. La jauge du bas
   dit ou on en est et rend la difference de course mesurable.
   Le trait pointille est la ligne d alignement, les bords des cartes
   viennent s y poser, et une carte partielle reste toujours visible a
   droite.
   Le rail fait 6 fois 68 moins 8, soit 400, pour une fenetre de 148. La
   course utile vaut donc 252 et le palier le plus loin, 204, tient.
   Le retour se fait par de vrais defilements en sens inverse.
   Gele, on verrait un rail aligne sur un trait. */
.ex-scroll-snap{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-scroll-snap .hd{display:block;height:8px;width:52px;border-radius:4px;background:var(--ink);
  opacity:.85;flex:0 0 auto}
.ex-scroll-snap .line{position:absolute;left:12px;top:32px;bottom:28px;display:block;width:1px;z-index:3;
  background:repeating-linear-gradient(180deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.ex-scroll-snap .vp{position:relative;flex:1;overflow:hidden;min-height:0}
.ex-scroll-snap .rail{position:absolute;left:0;top:0;display:flex;gap:8px;
  animation:aExSnpRail 5.6s infinite}
.ex-scroll-snap .cd{display:flex;flex-direction:column;gap:7px;width:60px;flex:none}
.ex-scroll-snap .cd .im{display:block;height:56px;border-radius:8px;background:var(--s3)}
.ex-scroll-snap .cd .pr{display:block;height:6px;width:64%;border-radius:3px;background:var(--s4)}
.ex-scroll-snap .cd:first-child .im{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-scroll-snap .prg{position:relative;display:block;height:4px;border-radius:2px;background:var(--s2);
  flex:0 0 auto}
.ex-scroll-snap .prg b{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:var(--lime);animation:aExSnpPrg 5.6s infinite}
/* lent, une carte. rapide, deux. ressort a chaque arrivee. */
@keyframes aExSnpRail{
  0%,8%{transform:translateX(0);animation-timing-function:cubic-bezier(.3,1.4,.35,1)}
  28%,40%{transform:translateX(-68px);animation-timing-function:cubic-bezier(.2,1.5,.3,1)}
  48%,62%{transform:translateX(-204px);animation-timing-function:cubic-bezier(.3,1.4,.35,1)}
  78%,86%{transform:translateX(-136px);animation-timing-function:cubic-bezier(.2,1.5,.3,1)}
  96%,100%{transform:translateX(0)}}
/* 204 sur 252 de course utile, soit 81 pour cent au plus loin */
@keyframes aExSnpPrg{0%,8%{width:0}28%,40%{width:27%}48%,62%{width:81%}
  78%,86%{width:54%}96%,100%{width:0}}

/* ---------------- container-transform ----------------
   Une liste de courrier. Les trois autres lignes gardent leur place et
   ne bougent jamais, c est ce qui dit que la surface etait l une
   d elles.
   L avatar est le meme objet du debut a la fin, il ne fait que se
   deplacer avec le conteneur. 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, ce qui est la consigne.
   Les limites sont interpolees et non mises a l echelle, sinon l avatar
   et les lignes s etireraient.
   Un seul fond partage sous les deux etats.
   Gele, on verrait un message ouvert au dessus d une liste. */
.ex-container-transform{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-container-transform .rw{position:absolute;left:12px;display:flex;align-items:center;gap:9px;
  width:148px;height:26px;padding:0 9px;box-sizing:border-box;border-radius:9px;background:var(--s2)}
.ex-container-transform .rw .av{display:block;width:16px;height:16px;border-radius:50%;
  background:var(--s4);flex:0 0 auto}
.ex-container-transform .rw .ln{display:block;flex:1;height:6px;border-radius:3px;background:var(--s4)}
.ex-container-transform .r1{top:10px}
.ex-container-transform .r3{top:74px}
.ex-container-transform .r4{top:106px}
.ex-container-transform .box{position:absolute;left:12px;top:42px;width:148px;height:26px;
  border-radius:9px;background:var(--lime);overflow:hidden;z-index:3;
  animation:aExCtf 4.8s cubic-bezier(.2,.9,.25,1) infinite}
.ex-container-transform .box .av{position:absolute;left:9px;top:5px;display:block;width:16px;height:16px;
  border-radius:50%;background:rgba(var(--on-accent-rgb),.7)}
.ex-container-transform .out{position:absolute;left:34px;top:10px;display:block;width:88px;height:6px;
  border-radius:3px;background:rgba(var(--on-accent-rgb),.55);
  animation:aExCtfOut 4.8s linear infinite}
.ex-container-transform .in{position:absolute;left:10px;display:block;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.42);opacity:0;animation:aExCtfIn 4.8s ease infinite}
.ex-container-transform .b1{top:34px;width:118px}
.ex-container-transform .b2{top:48px;width:94px}
.ex-container-transform .b3{top:62px;width:110px}
@keyframes aExCtf{0%,12%{left:12px;top:42px;width:148px;height:26px;border-radius:9px}
  46%,78%{left:8px;top:8px;width:156px;height:134px;border-radius:14px}
  94%,100%{left:12px;top:42px;width:148px;height:26px;border-radius:9px}}
@keyframes aExCtfOut{0%,12%{opacity:1}20%,86%{opacity:0}94%,100%{opacity:1}}
@keyframes aExCtfIn{0%,30%{opacity:0}52%,78%{opacity:1}86%,100%{opacity:0}}

/* ---------------- shared-axis ----------------
   Un onboarding, et les DEUX SENS. En avant de 12 a 36 pour cent, les
   deux vues glissent vers la gauche. En arriere de 60 a 84, elles
   glissent vers la DROITE. Rejouer la transition avant sur le retour est
   la facon la plus courante dont ce motif ment, et une scene a sens
   unique ne peut pas le montrer.
   Les deux vues bougent du meme cote et du meme decalage, 30, et non
   d une largeur d ecran.
   Les pastilles disent l etape, 1 puis 2 puis 1, et les deux boutons du
   bas donnent la cause de chaque sens.
   Gele, on verrait deux vues superposees a mi course. */
.ex-shared-axis{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-shared-axis .pips{display:flex;justify-content:center;gap:7px;flex:0 0 auto}
.ex-shared-axis .pips i{display:block;width:7px;height:7px;border-radius:50%;background:var(--s3)}
.ex-shared-axis .q1{animation:aExSaxQ1 5.4s steps(1,end) infinite}
.ex-shared-axis .q2{animation:aExSaxQ2 5.4s steps(1,end) infinite}
.ex-shared-axis .vp{position:relative;flex:1;overflow:hidden;border-radius:9px;min-height:0}
.ex-shared-axis .pg{position:absolute;inset:0;display:flex;flex-direction:column;gap:8px;padding:11px;
  box-sizing:border-box;border-radius:9px}
.ex-shared-axis .pg .il{display:block;height:34px;border-radius:7px}
.ex-shared-axis .pg .tt{display:block;height:8px;width:62%;border-radius:4px}
.ex-shared-axis .pg .tx{display:block;height:6px;width:84%;border-radius:3px}
.ex-shared-axis .a{background:var(--s1);animation:aExSaxA 5.4s cubic-bezier(.2,0,0,1) infinite}
.ex-shared-axis .a .il{background:var(--s3)}
.ex-shared-axis .a .tt{background:var(--s5)}
.ex-shared-axis .a .tx{background:var(--s4)}
.ex-shared-axis .b{background:var(--lime);animation:aExSaxB 5.4s cubic-bezier(.2,0,0,1) infinite}
.ex-shared-axis .b .il{background:rgba(var(--on-accent-rgb),.35)}
.ex-shared-axis .b .tt{background:rgba(var(--on-accent-rgb),.6)}
.ex-shared-axis .b .tx{background:rgba(var(--on-accent-rgb),.45)}
.ex-shared-axis .nav{display:flex;justify-content:space-between;flex:0 0 auto}
.ex-shared-axis .bk{display:block;width:34px;height:10px;border-radius:5px;background:var(--s3);
  animation:aExSaxBk 5.4s steps(1,end) infinite}
.ex-shared-axis .nx{display:block;width:44px;height:10px;border-radius:5px;background:var(--s3);
  animation:aExSaxNx 5.4s steps(1,end) infinite}
/* en avant les deux vont a gauche, en arriere les deux vont a droite */
@keyframes aExSaxA{0%,12%{transform:translateX(0);opacity:1}
  36%,60%{transform:translateX(-30px);opacity:0}
  84%,100%{transform:translateX(0);opacity:1}}
@keyframes aExSaxB{0%,12%{transform:translateX(30px);opacity:0}
  36%,60%{transform:translateX(0);opacity:1}
  84%,100%{transform:translateX(30px);opacity:0}}
@keyframes aExSaxQ1{0%,23%{background:var(--lime)}24%,71%{background:var(--s3)}
  72%,100%{background:var(--lime)}}
@keyframes aExSaxQ2{0%,23%{background:var(--s3)}24%,71%{background:var(--lime)}
  72%,100%{background:var(--s3)}}
/* la cause precede l effet, le bouton s allume avant que les vues partent */
@keyframes aExSaxNx{0%,7%{background:var(--s3)}8%,14%{background:var(--lime)}15%,100%{background:var(--s3)}}
@keyframes aExSaxBk{0%,55%{background:var(--s3)}56%,62%{background:var(--lime)}
  63%,100%{background:var(--s3)}}

/* ---------------- fade-through ----------------
   Le TROU est visible dans une vraie application. De 26 a 33 pour cent
   la zone de contenu ne tient rien du tout, et c est exactement ce que
   voit l utilisateur. Ce vide de 90ms est ce qui dit que les deux
   destinations n ont aucun rapport.
   La cause precede l effet, l onglet s allume a 8 pour cent et le
   contenu ne commence a partir qu a 12.
   L entrante passe de 0.92 a 1, la valeur de la consigne, et la
   sortante reste a l echelle 1. Les mettre toutes les deux a l echelle
   donnerait une seule chose qui zoome.
   La zone de contenu garde sa taille pendant tout l echange, sinon le
   conteneur ajouterait une direction que le motif ne doit pas avoir.
   Gele, on verrait un ecran vide, ou un ecran plein, sans le lien. */
.ex-fade-through{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-fade-through .vp{position:relative;flex:1;border-radius:9px;background:var(--s1);min-height:0}
.ex-fade-through .sc{position:absolute;inset:0;display:flex;flex-direction:column;gap:9px;padding:11px;
  box-sizing:border-box}
.ex-fade-through .sc i{display:block;height:7px;border-radius:3px;background:var(--s4)}
.ex-fade-through .sc .hh{height:11px;width:58%;border-radius:5px;background:var(--ink);opacity:.8}
.ex-fade-through .sc .s{width:52%}
.ex-fade-through .a{animation:aExFthA 4.2s cubic-bezier(.2,0,0,1) infinite}
.ex-fade-through .b{animation:aExFthB 4.2s cubic-bezier(.2,0,0,1) infinite}
.ex-fade-through .b .hh{background:var(--lime);opacity:1}
.ex-fade-through .tbs{display:flex;justify-content:space-around;flex:0 0 auto}
.ex-fade-through .tbs i{display:block;width:26px;height:10px;border-radius:5px;background:var(--s3)}
.ex-fade-through .t1{animation:aExFthT1 4.2s steps(1,end) infinite}
.ex-fade-through .t2{animation:aExFthT2 4.2s steps(1,end) infinite}
/* sortie de 12 a 26, TROU de 26 a 33, entree de 33 a 54 */
@keyframes aExFthA{0%,12%{opacity:1;transform:scale(1)}
  26%,80%{opacity:0;transform:scale(.92)}
  94%,100%{opacity:1;transform:scale(1)}}
@keyframes aExFthB{0%,33%{opacity:0;transform:scale(.92)}
  54%,66%{opacity:1;transform:scale(1)}
  80%,100%{opacity:0;transform:scale(.92)}}
@keyframes aExFthT1{0%,7%{background:var(--lime)}8%,73%{background:var(--s3)}
  74%,100%{background:var(--lime)}}
@keyframes aExFthT2{0%,7%{background:var(--s3)}8%,73%{background:var(--lime)}
  74%,100%{background:var(--s3)}}

/* ---------------- motion-scheme ----------------
   Le MEME ecran monte deux fois. Trois choses bougent dans chaque
   colonne, une carte, une puce, un interrupteur, et elles suivent
   toutes la courbe de leur jeu. C est ce que veut dire remplacer tous
   les ressorts a la fois.
   Le trait pointille est la cible de la carte. A gauche elle s y pose,
   a droite elle la depasse puis revient. Meme duree des deux cotes,
   3.4s, la difference est l amortissement et rien d autre.
   Gele, les deux colonnes seraient identiques, ce qui est precisement le
   probleme d un jeu de tokens, il ne se voit qu en mouvement. */
.ex-motion-scheme{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:10px}
.ex-motion-scheme .side{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.ex-motion-scheme em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-motion-scheme .exp em{color:var(--lime)}
.ex-motion-scheme .scr{position:relative;flex:1;border-radius:9px;background:var(--s1);overflow:hidden}
.ex-motion-scheme .tgt{position:absolute;left:40px;top:0;bottom:0;display:block;width:1px;
  background:repeating-linear-gradient(180deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.ex-motion-scheme .crd{position:absolute;left:6px;top:12px;display:block;width:26px;height:22px;
  border-radius:6px;background:var(--lime)}
.ex-motion-scheme .chp{position:absolute;left:12px;top:48px;display:block;width:18px;height:12px;
  border-radius:6px;background:var(--a2)}
.ex-motion-scheme .sw{position:absolute;left:12px;top:76px;display:block;width:24px;height:12px;
  border-radius:7px;background:var(--s4)}
.ex-motion-scheme .std .crd{animation:aExMscCard 3.4s cubic-bezier(.2,0,0,1) infinite}
.ex-motion-scheme .std .chp{animation:aExMscChip 3.4s cubic-bezier(.2,0,0,1) infinite}
.ex-motion-scheme .std .sw{animation:aExMscSw 3.4s cubic-bezier(.2,0,0,1) infinite}
.ex-motion-scheme .exp .crd{animation:aExMscCard 3.4s cubic-bezier(.15,1.7,.35,1) infinite}
.ex-motion-scheme .exp .chp{animation:aExMscChip 3.4s cubic-bezier(.15,1.7,.35,1) infinite}
.ex-motion-scheme .exp .sw{animation:aExMscSw 3.4s cubic-bezier(.15,1.7,.35,1) infinite}
/* la carte fait 26, part de 6 et sa cible est a 40, la course vaut donc
   34, ce qui la laisse a 66 dans une colonne de 69 */
@keyframes aExMscCard{0%,12%{transform:translateX(0)}58%,88%{transform:translateX(34px)}
  100%{transform:translateX(0)}}
@keyframes aExMscChip{0%,12%{transform:scale(1)}58%,88%{transform:scale(1.5)}100%{transform:scale(1)}}
@keyframes aExMscSw{0%,12%{transform:translateX(0)}58%,88%{transform:translateX(22px)}
  100%{transform:translateX(0)}}

/* ---------------- spatial-spring ----------------
   Un seul element, DEUX categories de ressort. Le bandeau occupe de
   l espace, il a donc le droit de depasser sa ligne de repos puis de
   revenir s y poser. Son texte et son action sont des opacites, ils
   n occupent aucun espace et arrivent sans jamais depasser, parce
   qu au dela de la cible il n existe aucune valeur ou aller.
   Les deux sont accordes, ils finissent ensemble a 62 pour cent. Un
   ressort spatial qui s installerait encore alors que son ressort
   d effet a fini ferait lire l element comme deux objets, ce que la
   consigne demande d eviter.
   Le bandeau fait 34 de haut et sa ligne de repos est a 96, il monte a
   90 puis redescend a 96, soit un depassement de 6.
   Gele, on verrait un bandeau pose sur sa ligne. */
.ex-spatial-spring{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-spatial-spring .page{display:flex;flex-direction:column;gap:9px}
.ex-spatial-spring .page i{display:block;height:7px;border-radius:3px;background:var(--s3)}
.ex-spatial-spring .page .s{width:56%}
.ex-spatial-spring .rest{position:absolute;left:0;right:0;top:96px;display:block;height:1px;
  background:repeating-linear-gradient(90deg,var(--ink-3) 0 3px,transparent 3px 6px)}
.ex-spatial-spring .snack{position:absolute;left:12px;right:12px;top:96px;display:flex;
  align-items:center;gap:10px;height:34px;padding:0 11px;box-sizing:border-box;border-radius:9px;
  background:var(--void);animation:aExSprSnack 3.8s cubic-bezier(.2,0,0,1) infinite}
.ex-spatial-spring .tx{display:block;flex:1;height:6px;border-radius:3px;background:var(--ink);
  opacity:0;animation:aExSprFx 3.8s cubic-bezier(.2,0,0,1) infinite}
.ex-spatial-spring .act{display:block;width:26px;height:8px;border-radius:4px;background:var(--lime);
  opacity:0;animation:aExSprFx 3.8s cubic-bezier(.2,0,0,1) infinite}
/* spatial, il depasse de 6 puis revient se poser sur la ligne */
@keyframes aExSprSnack{0%,10%{transform:translateY(46px)}
  44%{transform:translateY(-6px)}
  62%,88%{transform:translateY(0)}
  100%{transform:translateY(46px)}}
/* effets, l opacite arrive pile a 1 et ne la depasse jamais */
@keyframes aExSprFx{0%,18%{opacity:0}62%,88%{opacity:1}100%{opacity:0}}

/* ---------------- emphasized-easing ----------------
   Les TROIS variantes, chacune la ou son bout est hors ecran, ce qui est
   la vraie decision de conception derriere ce token.
   La feuille entre par le bas, son depart n a pas ete vu, elle prend
   donc decelerate. Le bouton s en va hors cadre, son arrivee ne sera pas
   vue, il prend accelerate. Les deux lignes a l interieur vont d une
   position visible a une autre, elles prennent emphasized, la seule qui
   adoucit les deux bouts.
   Aucune des trois ne depasse sa cible, l emphase vient de la forme du
   timing.
   Les trois etiquettes sont posees sur l element qui porte la variante,
   pas dans une legende a cote.
   Gele, on verrait une feuille ouverte, et les trois courbes seraient
   indiscernables. */
.ex-emphasized-easing{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-emphasized-easing .page{display:flex;flex-direction:column;gap:9px}
.ex-emphasized-easing .page i{display:block;height:7px;border-radius:3px;background:var(--s3)}
.ex-emphasized-easing .page .s{width:52%}
.ex-emphasized-easing .fab{position:absolute;right:14px;top:56px;display:block;width:34px;height:34px;
  border-radius:11px;background:var(--a2);z-index:3;
  animation:aExEmpFab 4.4s cubic-bezier(.3,0,.8,.15) infinite}
.ex-emphasized-easing .sheet{position:absolute;left:0;right:0;bottom:0;height:82px;
  border-radius:14px 14px 0 0;background:var(--lime);padding:14px 12px 0;box-sizing:border-box;
  animation:aExEmpSheet 4.4s cubic-bezier(.05,.7,.1,1) infinite}
.ex-emphasized-easing .grab{position:absolute;top:6px;left:50%;margin-left:-12px;display:block;
  width:24px;height:3px;border-radius:2px;background:rgba(var(--on-accent-rgb),.4)}
.ex-emphasized-easing .row{display:block;height:8px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.55);margin-bottom:9px;
  animation:aExEmpRow 4.4s cubic-bezier(.2,0,0,1) infinite}
.ex-emphasized-easing .r1{width:76%}
.ex-emphasized-easing .r2{width:52%}
.ex-emphasized-easing .tags{position:absolute;left:12px;top:12px;display:flex;flex-direction:column;
  gap:5px;z-index:4}
.ex-emphasized-easing .tags em{font:600 6px/1 var(--mono);font-style:normal;padding:2px 5px;
  border-radius:3px}
.ex-emphasized-easing .e1{background:rgba(var(--accent-rgb),.22);color:var(--lime)}
.ex-emphasized-easing .e2{background:rgba(var(--accent-rgb),.14);color:var(--lime)}
.ex-emphasized-easing .e3{background:rgba(var(--ink-rgb),.12);color:var(--ink-3)}
/* decelerate, elle entre de hors ecran, son depart n a pas ete vu */
@keyframes aExEmpSheet{0%,10%{transform:translateY(82px)}
  52%,84%{transform:translateY(0)}100%{transform:translateY(82px)}}
/* accelerate, il sort du cadre, son arrivee ne sera pas vue */
@keyframes aExEmpFab{0%,10%{transform:translateX(0);opacity:1}
  46%,84%{transform:translateX(58px);opacity:1}
  92%,100%{transform:translateX(0);opacity:1}}
/* emphasized, d une position visible a une autre, les deux bouts adoucis */
@keyframes aExEmpRow{0%,24%{transform:translateY(12px);opacity:0}
  60%,84%{transform:translateY(0);opacity:1}
  96%,100%{transform:translateY(12px);opacity:0}}

/* ---------------- predictive-back ----------------
   Un ecran de reglages sur sa liste. La liste derriere est une VRAIE
   destination, avec son titre et ses quatre lignes, et son opacite est
   calculee sur la meme progression que l ecran du dessus.
   Le geste est mene une premiere fois jusqu a 52, bien au dela du seuil
   ou l utilisateur semble engage, puis ANNULE. C est la consigne de
   supporter l annulation a n importe quelle valeur.
   Echelle et rayon suivent la meme progression, 0.87 et 13 au grand
   apercu, 0.93 et 8 au petit.
   Le retour de boucle est une navigation vers l avant, l ecran revient
   par la droite.
   Gele, on ne saurait pas si le geste va aboutir, ce qui est exactement
   l information que le motif existe pour donner. */
.ex-predictive-back{position:relative;width:170px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--stage)}
.ex-predictive-back .behind{position:absolute;inset:0;padding:12px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:9px;background:var(--s1);opacity:0;
  animation:aExPbkBh 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-predictive-back .bh{display:block;height:10px;width:54%;border-radius:5px;background:var(--ink);
  opacity:.85}
.ex-predictive-back .br{display:block;height:8px;border-radius:4px;background:var(--s4)}
.ex-predictive-back .br.s{width:56%}
.ex-predictive-back .front{position:absolute;inset:0;padding:12px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:10px;background:var(--lime);transform-origin:right center;
  animation:aExPbk 5.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-predictive-back .fh{display:block;height:11px;width:60%;border-radius:5px;
  background:rgba(var(--on-accent-rgb),.75)}
.ex-predictive-back .tg{display:flex;justify-content:flex-end;width:100%}
.ex-predictive-back .tg i{display:block;width:26px;height:14px;border-radius:8px;
  background:rgba(var(--on-accent-rgb),.4)}
.ex-predictive-back .fr{display:block;height:8px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.45)}
.ex-predictive-back .fr.s{width:52%}
.ex-predictive-back .cur{left:9px;top:68px;animation:aExPbkCur 5.6s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExPbk{0%,8%{transform:translateX(0) scale(1);border-radius:0}
  30%,34%{transform:translateX(52px) scale(.87);border-radius:13px}
  46%,54%{transform:translateX(0) scale(1);border-radius:0}
  64%,66%{transform:translateX(28px) scale(.93);border-radius:8px}
  80%,88%{transform:translateX(170px) scale(.85);border-radius:15px}
  100%{transform:translateX(0) scale(1);border-radius:0}}
@keyframes aExPbkBh{0%,8%{opacity:0}30%,34%{opacity:.65}46%,54%{opacity:0}
  64%,66%{opacity:.38}80%,88%{opacity:1}100%{opacity:0}}
@keyframes aExPbkCur{0%{transform:translateX(0);opacity:0}
  8%{transform:translateX(0);opacity:1}
  30%{transform:translateX(52px);opacity:1}
  34%{transform:translateX(52px);opacity:0}
  53%{transform:translateX(0);opacity:0}
  55%{transform:translateX(0);opacity:1}
  64%{transform:translateX(28px);opacity:1}
  67%{transform:translateX(28px);opacity:0}
  99%,100%{transform:translateX(0);opacity:0}}

/* ---------------- morphing-loader ----------------
   Un fil en cours de rafraichissement. Les trois squelettes en dessous
   ne bougent pas, ils donnent l echelle et disent que l indicateur est
   la seule chose vivante, ce qui est le cas d usage reel.
   Huit points par silhouette, tous demarres en haut et enroules dans le
   meme sens, sinon l interpolation n est pas definie et la forme saute.
   Rotation sur 3s, morphing sur 4.4s, sans diviseur commun.
   Il ne porte AUCUNE progression, c est pourquoi il n a rien a faire sur
   une attente de duree connue.
   Interieur de 148 sur 124, moins le titre de 8, le polygone de 36 et
   deux gouttieres de 12, il reste 56 pour les squelettes. Trois rangees
   de 14 avec 6 de gouttiere en font 54, elles tiennent donc.
   Gele, on verrait un polygone au dessus d une liste grise. */
.ex-morphing-loader{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px;
  align-items:center}
.ex-morphing-loader .hd{display:block;height:8px;width:56px;border-radius:4px;background:var(--ink);
  opacity:.85;align-self:flex-start}
.ex-morphing-loader .poly{display:block;width:36px;height:36px;background:var(--lime);flex:0 0 auto;
  animation:aExMldSpin 3s linear infinite,aExMldShape 4.4s ease-in-out infinite}
.ex-morphing-loader .skel{display:flex;flex-direction:column;gap:6px;width:100%}
.ex-morphing-loader .sk{display:flex;align-items:center;gap:9px}
.ex-morphing-loader .sk .av{display:block;width:14px;height:14px;border-radius:50%;background:var(--s2);
  flex:0 0 auto}
.ex-morphing-loader .sk .ln{display:block;flex:1;height:6px;border-radius:3px;background:var(--s2)}
@keyframes aExMldSpin{to{transform:rotate(360deg)}}
@keyframes aExMldShape{
  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%)}}

/* ---------------- connected-animation ----------------
   Le relais passe AU DESSUS de l echange de pages. La grille sort par la
   gauche avec sa page, la legende entre par la droite avec la sienne, et
   la vignette voyage seule par dessus les deux. C est ce qui montre les
   deux temps, enregistree d un cote et relancee de l autre.
   Les limites sont interpolees, gauche haut largeur hauteur, jamais un
   scale, sinon la vignette etirerait son contenu en chemin.
   Les deux cadres pointilles sont les ancrages, ils ne bougent jamais.
   La grille fait trois colonnes de 44 avec 8 de gouttiere, sa deuxieme
   rangee commence donc a 50. L ancrage A est cette case exacte, 12 et 50
   en 44 sur 30, et l ancrage B vaut 12 et 12 en 148 sur 62. La vignette
   qui vole EST une case de la grille, et non un element pose a cote.
   Gele, on verrait une vignette entre deux cadres. */
.ex-connected-animation{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-connected-animation .anc{position:absolute;display:block;border-radius:8px;
  border:1px dashed var(--s5);box-sizing:border-box;z-index:2}
.ex-connected-animation .anc.a{left:12px;top:50px;width:44px;height:30px}
.ex-connected-animation .anc.b{left:12px;top:12px;width:148px;height:62px}
.ex-connected-animation .pg{position:absolute;inset:0;padding:12px;box-sizing:border-box}
.ex-connected-animation .pa{background:var(--s0);animation:aExCnaA 4.8s cubic-bezier(.2,0,0,1) infinite}
.ex-connected-animation .gr{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.ex-connected-animation .gr i{display:block;height:30px;border-radius:8px;background:var(--s3)}
.ex-connected-animation .pb{background:var(--s0);display:flex;flex-direction:column;
  justify-content:flex-end;gap:9px;animation:aExCnaB 4.8s cubic-bezier(.2,0,0,1) infinite}
.ex-connected-animation .cap{display:block;height:8px;border-radius:4px;background:var(--s4)}
.ex-connected-animation .cap.s{width:54%}
.ex-connected-animation .fly{position:absolute;left:12px;top:50px;width:44px;height:30px;
  border-radius:8px;z-index:5;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExCna 4.8s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aExCnaA{0%,14%{transform:translateX(0)}32%,80%{transform:translateX(-100%)}
  96%,100%{transform:translateX(0)}}
@keyframes aExCnaB{0%,14%{transform:translateX(100%)}32%,80%{transform:translateX(0)}
  96%,100%{transform:translateX(100%)}}
@keyframes aExCna{0%,14%{left:12px;top:50px;width:44px;height:30px}
  54%,80%{left:12px;top:12px;width:148px;height:62px}
  96%,100%{left:12px;top:50px;width:44px;height:30px}}

/* ---------------- implicit-animation ----------------
   Une seule ECRITURE, l interrupteur, et quatre reponses. Le fond de la
   carte, sa bordure et son ombre portent une courbe sur leur propriete,
   elles glissent donc. La pastille de droite n en a aucune, elle SAUTE
   au meme instant. Rien n a ete demarre, aucune timeline n existe.
   La cause precede l effet, le bouton bascule a 40 pour cent et tout
   repond a 41.
   Deux ecritures par cycle, aller puis retour, donc rien n est rembobine.
   Gele, on verrait une carte dans un theme, sans savoir que trois de ses
   proprietes ont ete interpolees et une non. */
.ex-implicit-animation{position:relative;width:172px;height:146px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-implicit-animation .sw{position:relative;display:block;width:38px;height:20px;border-radius:11px;
  background:var(--s2);flex:0 0 auto;animation:aExImaSw 3.6s steps(1,end) infinite}
.ex-implicit-animation .knob{position:absolute;left:3px;top:3px;display:block;width:14px;height:14px;
  border-radius:50%;background:var(--ink);animation:aExImaKnob 3.6s cubic-bezier(.3,.9,.3,1) infinite}
/* trois proprietes avec une courbe enregistree, elles glissent */
.ex-implicit-animation .crd{position:relative;flex:1;border-radius:10px;box-sizing:border-box;
  padding:12px;display:flex;flex-direction:column;gap:9px;
  background:var(--s1);border:1px solid var(--s3);
  animation:aExImaCard 3.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-implicit-animation .ttl{display:block;height:10px;width:58%;border-radius:5px;background:var(--ink);
  opacity:.85}
.ex-implicit-animation .ln{display:block;height:7px;border-radius:3px;background:var(--s4)}
.ex-implicit-animation .ln.s{width:56%}
/* aucune courbe sur sa propriete, elle saute */
.ex-implicit-animation .badge{position:absolute;right:12px;bottom:12px;display:block;width:26px;
  height:12px;border-radius:6px;background:var(--s4);
  animation:aExImaBadge 3.6s steps(1,end) infinite}
.ex-implicit-animation .legend{display:flex;gap:10px;flex:0 0 auto}
.ex-implicit-animation .legend em{font:600 7px/1 var(--mono);font-style:normal}
.ex-implicit-animation .ok{color:var(--lime)}
.ex-implicit-animation .ko{color:var(--a4)}
@keyframes aExImaSw{0%,40%{background:var(--s2)}41%,88%{background:var(--lime)}
  89%,100%{background:var(--s2)}}
@keyframes aExImaKnob{0%,40%{transform:translateX(0)}41%,88%{transform:translateX(18px)}
  89%,100%{transform:translateX(0)}}
@keyframes aExImaCard{0%,40%{background:var(--s1);border-color:var(--s3);
    box-shadow:0 0 0 rgba(var(--shadow-rgb),0)}
  62%,88%{background:var(--void);border-color:var(--lime);
    box-shadow:0 8px 16px rgba(var(--shadow-rgb),.5)}
  100%{background:var(--s1);border-color:var(--s3);box-shadow:0 0 0 rgba(var(--shadow-rgb),0)}}
@keyframes aExImaBadge{0%,40%{background:var(--s4)}41%,88%{background:var(--a4)}
  89%,100%{background:var(--s4)}}

/* ---------------- reveal-highlight ----------------
   Une barre de commandes. Le bouton sous le pointeur prend son FOND, ses
   voisins ne prennent que leur BORDURE, et le rayon de la bordure est
   plus large que celui du fond, 32 contre 15, les deux portees sont donc
   distinguables a l oeil.
   La lumiere est posee sur le pointeur, memes instants, elle n a pas de
   timeline a elle. Un balayage independant continuerait a passer avec le
   pointeur immobile.
   Cinq boutons de 26 avec 5 de gouttiere font 150 dans un interieur de
   152. Les centres tombent donc a 23, 54, 85, 116 et 147, pour une course
   de 124, et ils sont atteints a 8, 27, 47, 66 et 86 pour cent.
   Gele, on verrait une barre dont un bouton est allume, sans le
   voisinage, qui est tout le terme. */
.ex-reveal-highlight{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:10px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-reveal-highlight .bar{display:flex;gap:5px;flex:0 0 auto}
.ex-reveal-highlight .btn{display:grid;place-items:center;width:26px;height:26px;border-radius:8px;
  box-sizing:border-box;background:var(--s1);border:1px solid rgba(var(--accent-rgb),.14)}
.ex-reveal-highlight .btn i{display:block;width:11px;height:11px;border-radius:3px;background:var(--s5)}
.ex-reveal-highlight .btn:nth-child(1){animation:aExRvh1 5.2s linear infinite}
.ex-reveal-highlight .btn:nth-child(2){animation:aExRvh2 5.2s linear infinite}
.ex-reveal-highlight .btn:nth-child(3){animation:aExRvh3 5.2s linear infinite}
.ex-reveal-highlight .btn:nth-child(4){animation:aExRvh4 5.2s linear infinite}
.ex-reveal-highlight .btn:nth-child(5){animation:aExRvh5 5.2s linear infinite}
.ex-reveal-highlight .bdy{display:flex;flex-direction:column;gap:10px;margin-top:6px}
.ex-reveal-highlight .bdy i{display:block;height:8px;border-radius:4px;background:var(--s2)}
.ex-reveal-highlight .bdy i.s{width:56%}
.ex-reveal-highlight .light{position:absolute;left:-38px;top:-32px;display:block;width:100px;
  height:100px;border-radius:50%;pointer-events:none;z-index:2;
  background:radial-gradient(circle,rgba(var(--accent-rgb),.34),transparent 62%);
  animation:aExRvhLight 5.2s linear infinite}
.ex-reveal-highlight .cur{left:16px;top:16px;z-index:4;animation:aExRvhLight 5.2s linear infinite}
/* le pointeur va de 23 a 151, la lumiere est posee dessus */
@keyframes aExRvhLight{0%{transform:translateX(0);opacity:0}
  8%{transform:translateX(0);opacity:1}
  86%{transform:translateX(124px);opacity:1}
  94%,100%{transform:translateX(124px);opacity:0}}
@keyframes aExRvh1{0%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  4%,17%{border-color:var(--lime);background:var(--s3)}
  28%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aExRvh2{0%,8%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  18%,37%{border-color:var(--lime);background:var(--s3)}
  48%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aExRvh3{0%,27%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  38%,56%{border-color:var(--lime);background:var(--s3)}
  67%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aExRvh4{0%,46%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  57%,76%{border-color:var(--lime);background:var(--s3)}
  87%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}
@keyframes aExRvh5{0%,66%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}
  77%,95%{border-color:var(--lime);background:var(--s3)}
  99%,100%{border-color:rgba(var(--accent-rgb),.14);background:var(--s1)}}

/* ---------------- view-transition-name ----------------
   DEUX noms differents, jamais le meme deux fois, ce qui est la
   contrainte centrale de la propriete. Le visuel et le titre ont chacun
   le leur et voyagent seuls, chacun vers son ancrage.
   Le corps du texte n est pas etiquete. Il appartient donc a une seule
   image qui se croise en fondu, sans se deplacer et sans etre suivi ligne
   par ligne, et c est ce contraste qui montre ce que le nom change.
   Les limites sont interpolees, jamais un scale.
   Le visuel va de 12 et 12 en 148 sur 54 vers 12 et 12 en 66 sur 42. Le
   titre va de 12 et 74 en 118 sur 11 vers 86 et 20 en 74 sur 11.
   Gele, on verrait une page d article. */
.ex-view-transition-name{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-view-transition-name .bdy{position:absolute;left:12px;right:12px;bottom:12px}
.ex-view-transition-name .ba,.ex-view-transition-name .bb{position:absolute;left:0;right:0;bottom:0;
  display:flex;flex-direction:column;gap:9px}
.ex-view-transition-name .ba i,.ex-view-transition-name .bb i{display:block;height:7px;
  border-radius:3px;background:var(--s3)}
.ex-view-transition-name .ba i:last-child{width:58%}
.ex-view-transition-name .bb i:last-child{width:42%}
.ex-view-transition-name .ba{animation:aExVtnBa 4.6s cubic-bezier(.2,0,0,1) infinite}
.ex-view-transition-name .bb{animation:aExVtnBb 4.6s cubic-bezier(.2,0,0,1) infinite}
.ex-view-transition-name .slot{position:absolute;display:block;border-radius:8px;
  border:1px dashed var(--s5);box-sizing:border-box}
.ex-view-transition-name .sh{left:12px;top:12px;width:66px;height:42px}
.ex-view-transition-name .st{left:86px;top:20px;width:74px;height:11px;border-radius:5px}
.ex-view-transition-name .hero{position:absolute;left:12px;top:12px;width:148px;height:54px;
  border-radius:8px;z-index:4;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExVtnHero 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-view-transition-name .title{position:absolute;left:12px;top:74px;width:118px;height:11px;
  border-radius:5px;background:var(--ink);opacity:.85;z-index:4;
  animation:aExVtnTitle 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-view-transition-name em{position:absolute;left:0;top:-11px;font:600 7px/1 var(--mono);
  color:var(--lime);font-style:normal}
.ex-view-transition-name .title em{color:var(--ink-3)}
@keyframes aExVtnHero{0%,14%{left:12px;top:12px;width:148px;height:54px}
  52%,80%{left:12px;top:12px;width:66px;height:42px}
  96%,100%{left:12px;top:12px;width:148px;height:54px}}
@keyframes aExVtnTitle{0%,14%{left:12px;top:74px;width:118px}
  52%,80%{left:86px;top:20px;width:74px}
  96%,100%{left:12px;top:74px;width:118px}}
/* non etiquete, une seule image qui se croise, sans deplacement */
@keyframes aExVtnBa{0%,18%{opacity:1}52%,78%{opacity:0}96%,100%{opacity:1}}
@keyframes aExVtnBb{0%,18%{opacity:0}52%,78%{opacity:1}96%,100%{opacity:0}}

/* ---------------- vt-pseudo-tree ----------------
   La MEME regle, ecrite deux fois, a deux niveaux. A gauche elle est sur
   le groupe, le seul niveau dont le navigateur interpole la boite, et la
   carte se deplace et se redimensionne. A droite elle est sur l ancien et
   le nouveau, qui sont des images, et il ne se passe rigoureusement
   rien de geometrique, seulement le fondu.
   C est la demonstration par l echec, et c est la seule facon de rendre
   visible une regle qui ne fait silencieusement rien.
   Gele, les deux colonnes seraient identiques, ce qui est precisement le
   piege que ce terme existe pour eviter. */
.ex-vt-pseudo-tree{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:12px}
.ex-vt-pseudo-tree .one{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}
.ex-vt-pseudo-tree em{font:600 7px/1 var(--mono);font-style:normal}
.ex-vt-pseudo-tree .ok em{color:var(--lime)}
.ex-vt-pseudo-tree .ko em{color:var(--a4)}
.ex-vt-pseudo-tree .stg{position:relative;flex:1;border-radius:9px;box-sizing:border-box;
  border:1px dashed var(--s4)}
.ex-vt-pseudo-tree .cd{position:absolute;display:block;border-radius:7px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2))}
/* sur le groupe, la boite est interpolee */
.ex-vt-pseudo-tree .ok .cd{left:8px;top:8px;width:48px;height:26px;
  animation:aExVtpOk 4s cubic-bezier(.2,.9,.25,1) infinite}
/* sur les instantanes, la geometrie ne bouge pas, seul le fondu passe */
.ex-vt-pseudo-tree .ko .cd{left:8px;top:8px;width:48px;height:26px;
  animation:aExVtpKo 4s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aExVtpOk{0%,14%{left:8px;top:8px;width:48px;height:26px}
  52%,82%{left:8px;top:48px;width:48px;height:52px}
  96%,100%{left:8px;top:8px;width:48px;height:26px}}
@keyframes aExVtpKo{0%,14%{opacity:1}52%,82%{opacity:.45}96%,100%{opacity:1}}

/* ---------------- cross-document-vt ----------------
   Une colonne de docs qui SURVIT au saut d un fichier a l autre. Elle
   porte le meme nom dans les deux documents, elle ne recoit donc aucune
   animation et ne bouge pas d un pixel pendant que les corps s echangent.
   C est la seule raison de faire cela sur un site multipage.
   Les deux pastilles du bas ont leur badge allume, parce qu un seul cote
   qui consent donne une coupure nette sans message d erreur. Le document
   sortant s eteint apres l echange, il n existe plus, et tout ce qui
   style la transition doit vivre dans l entrant.
   L entree active de la colonne change avec le fichier, ce qui donne la
   cause avant l effet.
   Gele, on verrait une page de documentation. */
.ex-cross-document-vt{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-cross-document-vt .frame{position:relative;flex:1;border-radius:9px;overflow:hidden;
  background:var(--s0);min-height:0}
/* nomme dans les deux documents, aucune animation, il persiste */
.ex-cross-document-vt .side{position:absolute;left:0;top:0;bottom:0;width:46px;z-index:3;
  background:var(--s1);padding:9px 8px;box-sizing:border-box;display:flex;flex-direction:column;gap:8px}
.ex-cross-document-vt .side i{display:block;height:6px;border-radius:3px;background:var(--s3)}
.ex-cross-document-vt .side i.on{background:var(--lime);animation:aExCdvOn 4.8s steps(1,end) infinite}
.ex-cross-document-vt .pg{position:absolute;left:46px;right:0;top:0;bottom:0;padding:10px;
  box-sizing:border-box;display:flex;flex-direction:column;gap:9px}
.ex-cross-document-vt .pg .h{display:block;height:10px;width:70%;border-radius:5px;background:var(--ink);
  opacity:.85}
.ex-cross-document-vt .pg i{display:block;height:7px;border-radius:3px;background:var(--s4)}
.ex-cross-document-vt .pg i.s{width:56%}
.ex-cross-document-vt .pa{background:var(--s0);animation:aExCdvA 4.8s cubic-bezier(.2,0,0,1) infinite}
.ex-cross-document-vt .pb{background:var(--s0);animation:aExCdvB 4.8s cubic-bezier(.2,0,0,1) infinite}
.ex-cross-document-vt .docs{display:flex;gap:8px;height:20px;flex:0 0 auto}
.ex-cross-document-vt .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)}
.ex-cross-document-vt .doc em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-cross-document-vt .doc b{display:block;width:7px;height:7px;border-radius:2px;background:var(--lime)}
.ex-cross-document-vt .da{animation:aExCdvDa 4.8s steps(1,end) infinite}
.ex-cross-document-vt .db{animation:aExCdvDb 4.8s steps(1,end) infinite}
/* l entree active change AVANT que les corps partent */
@keyframes aExCdvOn{0%,14%{transform:translateY(0)}16%,70%{transform:translateY(14px)}
  72%,100%{transform:translateY(0)}}
@keyframes aExCdvA{0%,18%{transform:translateX(0)}42%,70%{transform:translateX(-140%)}
  94%,100%{transform:translateX(0)}}
@keyframes aExCdvB{0%,18%{transform:translateX(140%)}42%,70%{transform:translateX(0)}
  94%,100%{transform:translateX(140%)}}
@keyframes aExCdvDa{0%,41%{background:var(--s3)}42%,93%{background:var(--s1)}
  94%,100%{background:var(--s3)}}
@keyframes aExCdvDb{0%,41%{background:var(--s1)}42%,93%{background:var(--s3)}
  94%,100%{background:var(--s1)}}

/* ---------------- vt-type ----------------
   Une liste de produits et sa fiche. Le MEME couple d ecrans recoit deux
   animations de nature differente, un glissement quand le type est
   forward et un agrandissement en fondu quand il est back. C est le type
   qui choisit la regle, pas la regle qui choisit un sens.
   L etiquette s allume a 8 pour cent pour une animation qui part a 12,
   la cause precede donc l effet a l ecran.
   Gele, on verrait une fiche produit, sans savoir par quel chemin on y
   est arrive, qui est exactement ce que le type porte. */
.ex-vt-type{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-vt-type .types{display:flex;gap:7px;flex:0 0 auto}
.ex-vt-type .types em{font:600 7px/1 var(--mono);font-style:normal;padding:3px 6px;border-radius:4px;
  background:var(--s1);color:var(--ink-3)}
.ex-vt-type .fwd{animation:aExVttFwd 5.8s steps(1,end) infinite}
.ex-vt-type .bwd{animation:aExVttBwd 5.8s steps(1,end) infinite}
.ex-vt-type .stg{position:relative;flex:1;border-radius:9px;overflow:hidden;min-height:0}
.ex-vt-type .pg{position:absolute;inset:0;padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:8px;border-radius:9px}
.ex-vt-type .list{background:var(--s1);animation:aExVttA 5.8s cubic-bezier(.2,0,0,1) infinite}
.ex-vt-type .list .h{display:block;height:8px;width:52%;border-radius:4px;background:var(--ink);
  opacity:.85}
.ex-vt-type .rw{display:flex;align-items:center;gap:8px}
.ex-vt-type .th{display:block;width:20px;height:16px;border-radius:4px;background:var(--s3);
  flex:0 0 auto}
.ex-vt-type .tx{display:block;flex:1;height:6px;border-radius:3px;background:var(--s4)}
.ex-vt-type .prod{background:var(--lime);animation:aExVttB 5.8s cubic-bezier(.2,0,0,1) infinite}
.ex-vt-type .prod .im{display:block;height:44px;border-radius:7px;
  background:rgba(var(--on-accent-rgb),.28)}
.ex-vt-type .prod .ti{display:block;height:8px;width:64%;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.7)}
.ex-vt-type .prod .pr{display:block;height:6px;width:34%;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.45)}
/* forward, les deux glissent. back, la liste grandit depuis 0.9 et la
   fiche ne fait que se fondre. Deux animations pour un seul couple. */
@keyframes aExVttA{0%,12%{transform:translateX(0) scale(1);opacity:1}
  34%,54%{transform:translateX(-30px) scale(1);opacity:0}
  58%{transform:translateX(0) scale(.9);opacity:0}
  80%,100%{transform:translateX(0) scale(1);opacity:1}}
@keyframes aExVttB{0%,12%{transform:translateX(30px);opacity:0}
  34%,58%{transform:translateX(0);opacity:1}
  76%{transform:translateX(0);opacity:0}
  78%,100%{transform:translateX(30px);opacity:0}}
@keyframes aExVttFwd{0%,7%{background:var(--s1);color:var(--ink-3)}
  8%,52%{background:rgba(var(--accent-rgb),.22);color:var(--lime)}
  53%,100%{background:var(--s1);color:var(--ink-3)}}
@keyframes aExVttBwd{0%,53%{background:var(--s1);color:var(--ink-3)}
  54%,96%{background:rgba(var(--accent-rgb),.22);color:var(--lime)}
  97%,100%{background:var(--s1);color:var(--ink-3)}}

/* ---------------- view-transition-class ----------------
   Six NOMS et UNE classe. Chaque carte garde son etiquette p1 a p6,
   parce que la classe ne leve pas la contrainte d unicite, et le crochet
   du bas montre la seule regle qui les atteint toutes les six.
   La cascade sort de cette regle unique, par nth-child sur le groupe, et
   non de six regles ecrites a la main. C est tout l interet.
   Trois colonnes de 46 avec 7 de gouttiere font 152, la largeur exacte de
   l interieur. Deux rangees de 44 plus la puce de classe et sa gouttiere
   font 115 pour 128 disponibles.
   Gele, on verrait six vignettes numerotees, sans la cascade. */
.ex-view-transition-class{position:relative;width:172px;height:148px;border-radius:11px;
  overflow:hidden;background:var(--s0);padding:10px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:8px}
.ex-view-transition-class .gri{display:grid;grid-template-columns:repeat(3,46px);
  grid-template-rows:repeat(2,44px);gap:7px}
.ex-view-transition-class .cd{position:relative;display:block;border-radius:8px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExVtc 3s cubic-bezier(.2,.9,.25,1) infinite}
.ex-view-transition-class .cd em{position:absolute;left:5px;top:4px;font:600 6px/1 var(--mono);
  font-style:normal;color:var(--on-accent);opacity:.85}
.ex-view-transition-class .cd:nth-child(2){animation-delay:.06s}
.ex-view-transition-class .cd:nth-child(3){animation-delay:.12s}
.ex-view-transition-class .cd:nth-child(4){animation-delay:.18s}
.ex-view-transition-class .cd:nth-child(5){animation-delay:.24s}
.ex-view-transition-class .cd:nth-child(6){animation-delay:.3s}
.ex-view-transition-class .cls{display:flex;align-items:center;gap:7px;height:12px;flex:0 0 auto}
.ex-view-transition-class .brk{display:block;width:112px;height:6px;border-radius:0 0 4px 4px;
  box-sizing:border-box;border:1px solid var(--s4);border-top:none}
.ex-view-transition-class .cls em{font:600 7px/1 var(--mono);font-style:normal;color:var(--ink-3)}
@keyframes aExVtc{0%,10%{transform:translateY(10px);opacity:.2}
  38%,78%{transform:translateY(0);opacity:1}
  96%,100%{transform:translateY(10px);opacity:.2}}

/* ---------------- scroll-progress-timeline ----------------
   Une barre de lecture dans l en-tete. Zero est l article en haut, cent
   pour cent en bas, et rien d autre n est mesure, ni une carte ni un
   titre, seulement la boite qui defile.
   Le document fait 260 dans une fenetre de 110, la course utile vaut
   donc 150, et la barre les parcourt en entier. Une barre qui atteint
   cent pour cent avec du texte encore a lire est exactement le mensonge
   que ce terme sert a eviter.
   Le retour est un vrai defilement vers le haut, la barre redescend donc
   avec lui, ce qu une barre de lecture fait reellement.
   Gele, on verrait une barre a moitie pleine, sans savoir de quoi. */
.ex-scroll-progress-timeline{position:relative;width:170px;height:150px;border-radius:11px;
  overflow:hidden;background:var(--s0)}
.ex-scroll-progress-timeline .hdr{position:absolute;left:0;right:0;top:0;height:26px;z-index:3;
  background:var(--s1);display:flex;align-items:center;gap:9px;padding:0 12px;box-sizing:border-box}
.ex-scroll-progress-timeline .lg{display:block;width:14px;height:14px;border-radius:4px;
  background:var(--lime);flex:0 0 auto}
.ex-scroll-progress-timeline .nv{display:block;width:34px;height:5px;border-radius:3px;
  background:var(--s4)}
.ex-scroll-progress-timeline .bar{position:absolute;left:0;right:0;bottom:0;height:3px;
  background:var(--s2)}
.ex-scroll-progress-timeline .bar i{position:absolute;inset:0;display:block;background:var(--lime);
  transform-origin:left;animation:aExSptBar 5.4s linear infinite}
.ex-scroll-progress-timeline .vp{position:absolute;left:12px;right:12px;top:34px;bottom:6px;
  overflow:hidden}
.ex-scroll-progress-timeline .doc{position:absolute;left:0;right:0;top:0;height:260px;display:flex;
  flex-direction:column;gap:9px;animation:aExSptDoc 5.4s linear infinite}
.ex-scroll-progress-timeline .doc .h{display:block;height:11px;width:72%;border-radius:5px;
  background:var(--ink);opacity:.85;flex:0 0 auto}
.ex-scroll-progress-timeline .doc .p{display:block;height:7px;border-radius:3px;background:var(--s3);
  flex:0 0 auto}
/* course de 150, aller de 8 a 48 pour cent puis retour de 56 a 96 */
@keyframes aExSptBar{0%,8%{transform:scaleX(.02)}48%,56%{transform:scaleX(1)}
  96%,100%{transform:scaleX(.02)}}
@keyframes aExSptDoc{0%,8%{transform:translateY(0)}48%,56%{transform:translateY(-150px)}
  96%,100%{transform:translateY(0)}}

/* ---------------- view-progress-timeline ----------------
   TROIS cartes, trois horloges. Chacune arrive quand ELLE traverse le
   scrollport, donc a 10, 24 puis 38 pour cent du cycle, et aucune n a
   besoin de connaitre les autres. Une timeline de defilement ne saurait
   pas exprimer cela, elle n a qu une valeur pour toute la page.
   Les deux traits 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 370 dans une fenetre de 150, la course utile vaut donc
   220. Les cartes sont a 160, 230 et 300, elles entrent donc quand la
   course atteint 10, 80 et 150.
   Gele, on verrait trois cartes, sans savoir qu elles sont independantes. */
.ex-view-progress-timeline{position:relative;width:172px;height:150px;border-radius:11px;
  overflow:hidden;background:var(--s0)}
.ex-view-progress-timeline .edge{position:absolute;left:0;right:0;display:block;height:1px;z-index:3;
  background:repeating-linear-gradient(90deg,rgba(var(--accent-rgb),.45) 0 3px,transparent 3px 6px)}
.ex-view-progress-timeline .en{bottom:14px}
.ex-view-progress-timeline .ex{top:14px}
.ex-view-progress-timeline .col{position:absolute;left:12px;right:12px;top:0;height:370px;
  animation:aExVptCol 6s linear infinite}
.ex-view-progress-timeline .cd{position:absolute;left:0;right:0;display:flex;align-items:center;gap:9px;
  height:40px;padding:0 10px;box-sizing:border-box;border-radius:9px;background:var(--s2)}
.ex-view-progress-timeline .cd .av{display:block;width:22px;height:22px;border-radius:50%;
  background:var(--lime);flex:0 0 auto}
.ex-view-progress-timeline .cd .ln{display:block;flex:1;height:7px;border-radius:3px;background:var(--s5)}
.ex-view-progress-timeline .c1{top:160px;animation:aExVptC1 6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-view-progress-timeline .c2{top:230px;animation:aExVptC2 6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-view-progress-timeline .c3{top:300px;animation:aExVptC3 6s cubic-bezier(.2,.9,.25,1) infinite}
@keyframes aExVptCol{0%,8%{transform:translateY(0)}52%,60%{transform:translateY(-220px)}
  96%,100%{transform:translateY(0)}}
@keyframes aExVptC1{0%,10%{transform:translateY(14px) scale(.94);opacity:.2}
  22%,84%{transform:translateY(0) scale(1);opacity:1}
  96%,100%{transform:translateY(14px) scale(.94);opacity:.2}}
@keyframes aExVptC2{0%,24%{transform:translateY(14px) scale(.94);opacity:.2}
  36%,80%{transform:translateY(0) scale(1);opacity:1}
  92%,100%{transform:translateY(14px) scale(.94);opacity:.2}}
@keyframes aExVptC3{0%,38%{transform:translateY(14px) scale(.94);opacity:.2}
  50%,76%{transform:translateY(0) scale(1);opacity:1}
  88%,100%{transform:translateY(14px) scale(.94);opacity:.2}}

/* ---------------- animation-range ----------------
   DEUX cartes identiques sur le MEME defilement. Celle de gauche a une
   plage entry, elle finit d arriver a 22 pour cent et RESTE POSEE
   jusqu au retour. Celle de droite n a aucune plage, elle etale donc son
   animation sur tout le passage, elle n est pleinement arrivee qu a 52
   pour cent, au moment ou elle commence a sortir, et elle ne se repose
   jamais.
   C est le Common trap de la fiche, cote a cote, et il ne se voit
   qu ainsi puisque les deux cartes sont rigoureusement les memes.
   La colonne fait 300 dans une fenetre de 112, la course disponible vaut
   donc 188, celle utilisee 184, et la carte posee a 150 entre quand la
   course atteint 38.
   Gele, les deux colonnes seraient identiques. */
.ex-animation-range{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:10px}
.ex-animation-range .one{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.ex-animation-range em{font:600 7px/1 var(--mono);font-style:normal}
.ex-animation-range .ok em{color:var(--lime)}
.ex-animation-range .ko em{color:var(--a4)}
.ex-animation-range .vp{position:relative;flex:1;overflow:hidden;border-radius:8px;background:var(--s1);
  min-height:0}
.ex-animation-range .col{position:absolute;left:8px;right:8px;top:0;height:300px;
  animation:aExArgCol 5.6s linear infinite}
.ex-animation-range .cd{position:absolute;left:0;right:0;top:150px;display:block;height:34px;
  border-radius:8px;background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-animation-range .ok .cd{animation:aExArgOk 5.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-animation-range .ko .cd{animation:aExArgKo 5.6s linear infinite}
@keyframes aExArgCol{0%,8%{transform:translateY(0)}52%,60%{transform:translateY(-184px)}
  96%,100%{transform:translateY(0)}}
/* plage entry, elle finit a 22 pour cent puis retient ses valeurs */
@keyframes aExArgOk{0%,13%{transform:translateY(12px) scale(.92);opacity:.2}
  22%,84%{transform:translateY(0) scale(1);opacity:1}
  96%,100%{transform:translateY(12px) scale(.92);opacity:.2}}
/* aucune plage, elle s etale sur tout le passage et n arrive qu en sortant */
@keyframes aExArgKo{0%,13%{transform:translateY(12px) scale(.92);opacity:.2}
  52%,60%{transform:translateY(0) scale(1);opacity:1}
  96%,100%{transform:translateY(12px) scale(.92);opacity:.2}}

/* ---------------- scroll-state-query ----------------
   DEUX etats interroges dans un vrai tableau. La pastille dit lequel
   repond, scrollable tant qu il reste de la course, stuck quand l en-tete
   est colle en haut. 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 que c est
   justement l etat qu un ecouteur de defilement suit le plus mal.
   Le tableau fait 250 dans une fenetre de 108, la course utile vaut donc
   142, et le palier de 96 reste en dessous.
   Gele, on verrait un en-tete fin avec une ombre, sans savoir de quel
   etat elle est la reponse. */
.ex-scroll-state-query{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-scroll-state-query .th{position:absolute;left:0;right:0;top:0;height:26px;z-index:3;
  background:var(--lime);display:flex;align-items:center;gap:10px;padding:0 12px;box-sizing:border-box;
  animation:aExSstTh 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-state-query .th i{display:block;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.7);flex:1}
.ex-scroll-state-query .th i.s{flex:0 0 34px}
.ex-scroll-state-query .vp{position:absolute;left:0;right:0;top:32px;bottom:10px;overflow:hidden}
.ex-scroll-state-query .tb{position:absolute;left:12px;right:12px;top:0;height:250px;display:flex;
  flex-direction:column;gap:12px;animation:aExSstTb 5.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-state-query .tr{display:flex;align-items:center;gap:10px;flex:0 0 auto}
.ex-scroll-state-query .tr i{display:block;height:7px;border-radius:3px;background:var(--s3);flex:1}
.ex-scroll-state-query .tr i.s{flex:0 0 34px;background:var(--s2)}
.ex-scroll-state-query .badge{position:absolute;right:8px;top:32px;z-index:4;font-style:normal}
.ex-scroll-state-query .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),.5);
  color:var(--ink)}
.ex-scroll-state-query .b1{animation:aExSstB1 5.4s steps(1,end) infinite}
.ex-scroll-state-query .b2{animation:aExSstB2 5.4s steps(1,end) infinite}
.ex-scroll-state-query .hint{position:absolute;left:50%;bottom:7px;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:aExSstHint 5.4s steps(1,end) infinite}
/* course de 142 disponible, palier a 96 */
@keyframes aExSstTb{0%,16%{transform:translateY(0)}46%,84%{transform:translateY(-96px)}
  98%,100%{transform:translateY(0)}}
@keyframes aExSstTh{0%,16%{height:26px;box-shadow:none}
  46%,84%{height:16px;box-shadow:0 5px 12px rgba(var(--shadow-rgb),.6)}
  98%,100%{height:26px;box-shadow:none}}
@keyframes aExSstB1{0%,30%{opacity:1}32%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExSstB2{0%,30%{opacity:0}32%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aExSstHint{0%,30%{opacity:.8}32%,94%{opacity:0}96%,100%{opacity:.8}}

/* ---------------- scroll-snap-event ----------------
   Les deux rangees de batons se COMPTENT. changing en marque quatre,
   change en marque trois, et la difference est le candidat auquel
   personne n est jamais arrive, celui que l utilisateur a laisse en
   revenant sur ses pas.
   C est exactement le Common trap de la fiche. Brancher des statistiques
   sur changing ferait rapporter quatre vues pour trois arrivees, sur un
   seul balayage hesitant.
   La bande fait 3 fois 66 avec 8 de gouttiere, soit 214, dans une fenetre
   de 148, une vue vaut donc 74 de pas et 66 de course visible.
   Le dernier evenement tombe a 86 pour cent, ce qui laisse dix pour cent
   de cycle pour lire le compte avant sa remise a zero.
   Gele, on verrait deux rangees de batons, ce qui donne le compte mais
   pas laquelle des deux a rate son arrivee. */
.ex-scroll-snap-event{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-scroll-snap-event .vp{position:relative;height:54px;overflow:hidden;border-radius:8px;flex:0 0 auto}
.ex-scroll-snap-event .strip{position:absolute;left:0;top:0;display:flex;gap:8px;
  animation:aExSseStrip 6.2s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-snap-event .strip i{display:block;flex:none;width:66px;height:54px;border-radius:8px;
  background:var(--s3)}
.ex-scroll-snap-event .strip i:first-child{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-scroll-snap-event .dots{display:flex;justify-content:center;gap:8px;flex:0 0 auto}
.ex-scroll-snap-event .dots b{display:block;width:8px;height:8px;border-radius:4px;background:var(--s3)}
.ex-scroll-snap-event .d1{animation:aExSseD1 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .d2{animation:aExSseD2 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .rows{display:flex;flex-direction:column;gap:8px;margin-top:auto}
.ex-scroll-snap-event .rw{display:flex;align-items:center;gap:8px}
.ex-scroll-snap-event .rw em{font:600 6px/1 var(--mono);font-style:normal;width:44px;text-align:right}
.ex-scroll-snap-event .ing em,.ex-scroll-snap-event .rw:first-child em{color:var(--a4)}
.ex-scroll-snap-event .rw:last-child em{color:var(--lime)}
.ex-scroll-snap-event .tal{display:flex;gap:6px}
.ex-scroll-snap-event .tal i{display:block;width:3px;height:12px;border-radius:2px;background:var(--s2)}
.ex-scroll-snap-event .ing i:nth-child(1){animation:aExSseI1 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .ing i:nth-child(2){animation:aExSseI2 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .ing i:nth-child(3){animation:aExSseI3 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .ing i:nth-child(4){animation:aExSseI4 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .chg i:nth-child(1){animation:aExSseC1 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .chg i:nth-child(2){animation:aExSseC2 6.2s steps(1,end) infinite}
.ex-scroll-snap-event .chg i:nth-child(3){animation:aExSseC3 6.2s steps(1,end) infinite}
/* part vers la deuxieme a -40, REVIENT a 0, puis y va vraiment a -66 */
@keyframes aExSseStrip{0%,6%{transform:translateX(0)}
  20%{transform:translateX(-40px)}
  32%,38%{transform:translateX(0)}
  48%,66%{transform:translateX(-66px)}
  84%,100%{transform:translateX(0)}}
@keyframes aExSseD1{0%,23%{background:var(--lime)}24%,33%{background:var(--a4)}
  34%,51%{background:var(--lime)}52%,75%{background:var(--s3)}
  76%,85%{background:var(--a4)}86%,100%{background:var(--lime)}}
@keyframes aExSseD2{0%,9%{background:var(--s3)}10%,23%{background:var(--a4)}
  24%,51%{background:var(--s3)}52%,61%{background:var(--a4)}
  62%,75%{background:var(--lime)}76%,100%{background:var(--s3)}}
/* Quatre candidats, a 10, 24, 52 et 76. Trois arrivees, a 34, 62 et 86.
   Le dernier evenement tombe a 86, ce qui laisse de 88 a 98 pour lire le
   compte complet, puis les batons se remettent a zero a 99 et la boucle
   revient donc a son point de depart. */
@keyframes aExSseI1{0%,9%{background:var(--s2)}10%,98%{background:var(--a4)}99%,100%{background:var(--s2)}}
@keyframes aExSseI2{0%,23%{background:var(--s2)}24%,98%{background:var(--a4)}99%,100%{background:var(--s2)}}
@keyframes aExSseI3{0%,51%{background:var(--s2)}52%,98%{background:var(--a4)}99%,100%{background:var(--s2)}}
@keyframes aExSseI4{0%,75%{background:var(--s2)}76%,98%{background:var(--a4)}99%,100%{background:var(--s2)}}
@keyframes aExSseC1{0%,33%{background:var(--s2)}34%,98%{background:var(--lime)}99%,100%{background:var(--s2)}}
@keyframes aExSseC2{0%,61%{background:var(--s2)}62%,98%{background:var(--lime)}99%,100%{background:var(--s2)}}
@keyframes aExSseC3{0%,85%{background:var(--s2)}86%,98%{background:var(--lime)}99%,100%{background:var(--s2)}}

/* ---------------- scroll-marker ----------------
   Une galerie produit. Les deux fleches se GRISENT d elles memes, la
   gauche sur la premiere vue et la droite sur la derniere, parce
   qu elles viennent du conteneur et connaissent sa course. Une rangee de
   points ecrite a la main rate cet etat au premier defilement qu un
   script n a pas provoque.
   Le marqueur courant s elargit au lieu d etre echange, puisque c est la
   seule information de position dont dispose l utilisateur.
   La bande fait 3 fois 148 avec 8 de gouttiere, soit 460, dans une
   fenetre de 148, le pas vaut donc 156 et la course totale 312.
   Gele, on verrait une galerie avec trois marqueurs, sans les deux
   boutons qui se desactivent, qui sont la moitie de ce que le conteneur
   fournit. */
.ex-scroll-marker{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-scroll-marker .vp{position:relative;flex:1;overflow:hidden;border-radius:9px;min-height:0}
.ex-scroll-marker .strip{position:absolute;left:0;top:0;bottom:0;display:flex;gap:8px;
  animation:aExSmkStrip 5.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-marker .strip i{display:block;flex:none;width:148px;border-radius:9px;background:var(--s3)}
.ex-scroll-marker .s1{background:linear-gradient(135deg,var(--lime),var(--accent-2))}
.ex-scroll-marker .s2{background:linear-gradient(135deg,var(--a2),var(--accent-deep))}
.ex-scroll-marker .s3{background:linear-gradient(135deg,var(--a4),var(--void))}
.ex-scroll-marker .ctl{display:flex;align-items:center;justify-content:center;gap:12px;height:12px;
  flex:0 0 auto}
/* les deux boutons generes, ils se desactivent aux extremites */
.ex-scroll-marker .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)}
.ex-scroll-marker .prev{transform:rotate(135deg);animation:aExSmkPrev 5.8s steps(1,end) infinite}
.ex-scroll-marker .next{transform:rotate(-45deg);animation:aExSmkNext 5.8s steps(1,end) infinite}
.ex-scroll-marker .mk{display:flex;gap:7px}
.ex-scroll-marker .mk i{display:block;width:8px;height:8px;border-radius:4px;background:var(--s3)}
.ex-scroll-marker .m1{animation:aExSmkM1 5.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-marker .m2{animation:aExSmkM2 5.8s cubic-bezier(.3,.9,.3,1) infinite}
.ex-scroll-marker .m3{animation:aExSmkM3 5.8s cubic-bezier(.3,.9,.3,1) infinite}
/* pas de 156, trois positions, 0 puis -156 puis -312 */
@keyframes aExSmkStrip{0%,8%{transform:translateX(0)}
  22%,32%{transform:translateX(-156px)}
  44%,56%{transform:translateX(-312px)}
  68%,78%{transform:translateX(-156px)}
  92%,100%{transform:translateX(0)}}
@keyframes aExSmkM1{0%,14%{width:20px;background:var(--lime)}
  22%,86%{width:8px;background:var(--s3)}
  92%,100%{width:20px;background:var(--lime)}}
@keyframes aExSmkM2{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 aExSmkM3{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 aExSmkPrev{0%,14%{border-color:var(--s2)}22%,86%{border-color:var(--ink-3)}
  92%,100%{border-color:var(--s2)}}
/* desactive sur la derniere */
@keyframes aExSmkNext{0%,38%{border-color:var(--ink-3)}44%,62%{border-color:var(--s2)}
  68%,100%{border-color:var(--ink-3)}}

/* ---------------- sibling-index ----------------
   La liste GRANDIT et rien n est ecrit. Cinq barres montent en cascade,
   puis une sixieme apparait a 48 pour cent et prend sa place dans la
   meme cadence, avec le retard que son index lui donne.
   La puce du bas est la seule regle. Un retard ecrit a la main aurait
   demande une sixieme declaration, et personne n aurait su, en la
   lisant, si les cinq premieres etaient calculees ou copiees.
   60ms par rang sur un cycle de 5s, soit 1.2 pour cent, donc les depatrs
   tombent a 6, 7.2, 8.4, 9.6, 10.8 et 12 pour cent.
   Gele, on verrait six barres numerotees, sans la cascade ni le fait que
   la sixieme est arrivee apres. */
.ex-sibling-index{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-sibling-index .ttl{display:block;height:8px;width:52px;border-radius:4px;background:var(--ink);
  opacity:.85;flex:0 0 auto}
.ex-sibling-index .bars{display:flex;align-items:flex-end;gap:8px;flex:1;min-height:0}
.ex-sibling-index .bx{position:relative;display:flex;flex-direction:column;align-items:center;gap:5px;
  flex:1;justify-content:flex-end;transform-origin:bottom;
  animation:aExSixBar 5s cubic-bezier(.2,.9,.25,1) infinite}
.ex-sibling-index .bx i{display:block;width:100%;border-radius:4px 4px 0 0;background:var(--lime)}
.ex-sibling-index .bx em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal}
.ex-sibling-index .b1 i{height:34px}
.ex-sibling-index .b2 i{height:56px}
.ex-sibling-index .b3 i{height:44px}
.ex-sibling-index .b4 i{height:72px}
.ex-sibling-index .b5 i{height:60px}
.ex-sibling-index .b6 i{height:82px}
/* 60ms par rang, soit 1.2 pour cent d un cycle de 5s */
.ex-sibling-index .b2{animation-delay:.06s}
.ex-sibling-index .b3{animation-delay:.12s}
.ex-sibling-index .b4{animation-delay:.18s}
.ex-sibling-index .b5{animation-delay:.24s}
/* la sixieme n existe pas encore, elle arrive a 48 pour cent */
.ex-sibling-index .b6{animation:aExSixNew 5s cubic-bezier(.2,.9,.25,1) infinite}
.ex-sibling-index .rule{position:absolute;left:12px;bottom:2px;font:600 7px/1 var(--mono);
  color:var(--lime);letter-spacing:0;text-transform:none}
@keyframes aExSixBar{0%,6%{transform:scaleY(.04);opacity:0}
  30%,86%{transform:scaleY(1);opacity:1}
  96%,100%{transform:scaleY(.04);opacity:0}}
@keyframes aExSixNew{0%,48%{transform:scaleY(.04);opacity:0}
  62%,86%{transform:scaleY(1);opacity:1}
  96%,100%{transform:scaleY(.04);opacity:0}}

/* ---------------- starting-style ----------------
   Deux toasts arrivent au dessus de la meme application. Celui de gauche
   a un etat de depart, dessine en pointille, decale de 12 et a l echelle
   0.9, et il transitionne DEPUIS lui. Celui de droite n en a aucun, il
   apparait donc directement a sa valeur finale.
   La comparaison porte sur l ENTREE, ce qui est tout ce que cette regle
   sait faire. La sortie appartient a allow-discrete, dont la fiche
   compare l autre bout.
   Gele, les deux toasts seraient identiques, ce qui est exactement
   pourquoi ce defaut passe en revue sans etre vu. */
.ex-starting-style{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-starting-style .app{display:flex;flex-direction:column;gap:10px}
.ex-starting-style .app i{display:block;height:8px;border-radius:4px;background:var(--s2)}
.ex-starting-style .app i:nth-child(2){width:74%}
.ex-starting-style .app i:nth-child(3){width:56%}
.ex-starting-style .one{position:absolute;bottom:12px;display:flex;flex-direction:column;
  align-items:center;gap:7px;width:70px}
.ex-starting-style .ok{left:12px}
.ex-starting-style .ko{right:12px}
.ex-starting-style em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ex-starting-style .ok em{color:var(--lime)}
.ex-starting-style .ko em{color:var(--a4)}
.ex-starting-style .stg{position:relative;display:block;width:70px;height:46px}
/* le cadre pointille EST l etat de depart, decale de 12 et a 0.9 */
.ex-starting-style .ghost{position:absolute;left:0;right:0;top:12px;display:block;height:34px;
  border-radius:8px;box-sizing:border-box;border:1px dashed var(--s5);transform:scale(.9)}
.ex-starting-style .ts{position:absolute;left:0;right:0;top:0;display:flex;flex-direction:column;
  justify-content:center;gap:6px;height:34px;padding:0 9px;box-sizing:border-box;border-radius:8px;
  background:var(--lime)}
.ex-starting-style .ts b{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.6)}
.ex-starting-style .ts b.s{width:56%}
.ex-starting-style .ok .ts{animation:aExStsOk 3.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-starting-style .ko .ts{animation:aExStsKo 3.6s steps(1,end) infinite}
@keyframes aExStsOk{0%,6%{opacity:0;transform:translateY(12px) scale(.9)}
  36%,78%{opacity:1;transform:translateY(0) scale(1)}
  96%,100%{opacity:0;transform:translateY(12px) scale(.9)}}
@keyframes aExStsKo{0%,6%{opacity:0}7%,78%{opacity:1}79%,100%{opacity:0}}

/* ---------------- allow-discrete ----------------
   Deux modales s en vont. Le repere sur la barre de duree dit ou display
   bascule. A gauche il tombe a cent pour cent, la modale reste donc
   peinte pendant toute sa sortie. A droite il tombe a zero, elle
   disparait a la premiere image et l animation ecrite se joue dans le
   vide.
   L entree est identique dans les deux colonnes, et c est pour cela que
   ce bug est livre. On ne le voit qu en fermant.
   Gele, les deux colonnes seraient identiques. */
.ex-allow-discrete{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:12px}
.ex-allow-discrete .one{display:flex;flex-direction:column;align-items:center;gap:9px;flex:1;
  min-width:0}
.ex-allow-discrete em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  white-space:nowrap}
.ex-allow-discrete .ok em{color:var(--lime)}
.ex-allow-discrete .ko em{color:var(--a4)}
.ex-allow-discrete .md{display:flex;flex-direction:column;gap:8px;width:100%;padding:10px;
  box-sizing:border-box;border-radius:9px;background:var(--lime);flex:1}
.ex-allow-discrete .md .h{display:block;height:9px;width:64%;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.75)}
.ex-allow-discrete .md .l{display:block;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.45)}
.ex-allow-discrete .md .l.s{width:52%}
.ex-allow-discrete .ok .md{animation:aExAldOk 3.8s ease infinite}
.ex-allow-discrete .ko .md{animation:aExAldKo 3.8s steps(1,end) infinite}
.ex-allow-discrete .dur{position:relative;display:block;width:100%;height:4px;border-radius:2px;
  background:var(--s2);flex:0 0 auto}
.ex-allow-discrete .flip{position:absolute;top:-4px;display:block;width:2px;height:12px;
  border-radius:1px;background:var(--ink)}
/* display bascule a cent pour cent de la duree */
.ex-allow-discrete .ok .flip{right:0}
/* display bascule a zero pour cent de la duree */
.ex-allow-discrete .ko .flip{left:0}
@keyframes aExAldOk{0%,32%{opacity:1;transform:scale(1)}
  66%,92%{opacity:0;transform:scale(.86) translateY(10px)}
  100%{opacity:1;transform:scale(1)}}
@keyframes aExAldKo{0%,32%{opacity:1}33%,92%{opacity:0}93%,100%{opacity:1}}

/* ---------------- top-layer ----------------
   Une carte qui defile, avec overflow cache, et un menu qui s ouvre
   depuis une de ses lignes. Dans la couche superieure il sort de la carte
   et passe par dessus. Avec un z-index de 9999 il est coupe net au bord,
   parce qu un ancetre qui decoupe ne regarde pas le z-index.
   C est le cas de tous les jours, et la demonstration par l echec est le
   seul moyen de montrer une propriete dont tout le monde croit qu un
   grand nombre suffit.
   Les deux menus font 96. Celui du dedans part a 9 dans une carte de 88,
   il en montre donc 79 et perd les 17 derniers. Celui de la couche
   superieure part a 21 et va jusqu a 117, bien au dela du bord de la
   carte a 100.
   Gele, on verrait deux menus dont un tronque, sans savoir qu ils
   partent de la meme ligne. */
.ex-top-layer{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-top-layer .crd{position:absolute;left:12px;top:34px;width:88px;height:64px;overflow:hidden;
  box-sizing:border-box;border:1px solid var(--s5);border-radius:9px;background:var(--s1);
  padding:9px;display:flex;flex-direction:column;gap:8px}
.ex-top-layer .rw{display:block;height:10px;border-radius:4px;background:var(--s3)}
.ex-top-layer .rw.on{background:var(--s5)}
/* dans la carte, avec un grand z-index, et coupe quand meme */
.ex-top-layer .inmenu{position:absolute;left:9px;top:24px;display:flex;flex-direction:column;gap:6px;
  width:96px;padding:8px;box-sizing:border-box;border-radius:8px;background:var(--a4);z-index:9999;
  animation:aExTplIn 4s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-top-layer .inmenu i{display:block;height:5px;border-radius:3px;
  background:rgba(var(--void-rgb),.4)}
/* dans la couche superieure, il ignore le decoupage de l ancetre */
.ex-top-layer .topmenu{position:absolute;left:21px;top:58px;display:flex;flex-direction:column;gap:6px;
  width:96px;padding:8px;box-sizing:border-box;border-radius:8px;background:var(--lime);z-index:5;
  box-shadow:0 12px 26px rgba(var(--shadow-rgb),.55);
  animation:aExTplTop 4s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-top-layer .topmenu i{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.6)}
.ex-top-layer .tags{position:absolute;right:8px;top:0;bottom:0;display:flex;flex-direction:column;
  justify-content:center;gap:12px;width:40px}
.ex-top-layer .tags em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none}
.ex-top-layer .ok{color:var(--lime)}
.ex-top-layer .ko{color:var(--a4)}
@keyframes aExTplTop{0%,10%{opacity:0;transform:scale(.7)}34%,76%{opacity:1;transform:scale(1)}
  94%,100%{opacity:0;transform:scale(.7)}}
@keyframes aExTplIn{0%,10%{opacity:0;transform:scale(.7)}34%,76%{opacity:1;transform:scale(1)}
  94%,100%{opacity:0;transform:scale(.7)}}

/* ---------------- interpolate-size ----------------
   Deux questions, deux longueurs de reponse, et chacune ouvre a SA
   hauteur. La premiere a deux lignes et ouvre a 21, la seconde en a
   quatre et ouvre a 47.
   Le trait pointille est la hauteur qu il aurait fallu coder en dur. Il
   passe au milieu de la seconde reponse, ce qui montre qu aucun nombre
   unique ne peut servir les deux, et c est precisement le probleme que
   cette fonctionnalite supprime.
   Deux lignes de 8 avec 5 de gouttiere font 21, quatre en font 47.
   Gele, on verrait deux panneaux ouverts, sans savoir qu aucun nombre
   n aurait pu convenir aux deux. */
.ex-interpolate-size{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-interpolate-size .qa{display:flex;flex-direction:column;gap:7px}
.ex-interpolate-size .q{display:block;height:16px;border-radius:6px;background:var(--s3)}
.ex-interpolate-size .a{display:flex;flex-direction:column;gap:5px;overflow:hidden;height:0}
.ex-interpolate-size .a i{display:block;height:8px;border-radius:4px;background:var(--lime);
  flex:0 0 auto}
.ex-interpolate-size .a i:nth-child(2){width:78%}
.ex-interpolate-size .a i:nth-child(4){width:56%}
/* chacune ouvre a la hauteur de son propre contenu */
.ex-interpolate-size .q1 .a{animation:aExIszA 4.2s cubic-bezier(.3,.9,.3,1) infinite}
.ex-interpolate-size .q2 .a{animation:aExIszB 4.2s cubic-bezier(.3,.9,.3,1) infinite}
/* la hauteur qu il aurait fallu coder en dur, elle coupe la seconde */
.ex-interpolate-size .fixed{position:absolute;left:12px;right:48px;bottom:39px;display:block;height:1px;
  background:repeating-linear-gradient(90deg,var(--a4) 0 3px,transparent 3px 6px)}
.ex-interpolate-size .lb{position:absolute;right:12px;bottom:35px;font:600 6px/1 var(--mono);
  color:var(--a4);font-style:normal;letter-spacing:0;text-transform:none}
@keyframes aExIszA{0%,10%{height:0}42%,80%{height:21px}96%,100%{height:0}}
@keyframes aExIszB{0%,16%{height:0}48%,80%{height:47px}96%,100%{height:0}}

/* ---------------- linear-easing ----------------
   Un badge de panier. Le trait vertical est la cible. En haut, une liste
   de sept arrets fait passer le point trois fois de part et d autre,
   comme un ressort qui s amortit. En bas, le meilleur cubic-bezier ne
   depasse qu une fois, et ce n est pas un reglage mais un plafond, il n a
   que deux points de controle.
   Meme course et meme duree des deux cotes, seule la fonction change.
   La piste fait 100 et le point 12, la course vaut donc 88, et la cible
   est posee a 88.
   Gele, on verrait deux points sur leur cible. */
.ex-linear-easing{position:relative;width:172px;height:148px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:14px}
.ex-linear-easing .bar{display:flex;align-items:center;justify-content:space-between;flex:0 0 auto}
.ex-linear-easing .lg{display:block;width:16px;height:16px;border-radius:5px;background:var(--s3)}
.ex-linear-easing .cart{position:relative;display:block;width:22px;height:20px;border-radius:5px;
  box-sizing:border-box;border:2px solid var(--s5)}
.ex-linear-easing .bg{position:absolute;right:-6px;top:-6px;display:block;width:14px;height:14px;
  border-radius:50%;background:var(--lime);
  animation:aExLneBadge 2.8s linear(0,.42,1.08,.94,1.05,.98,1) infinite}
.ex-linear-easing .two{display:flex;flex-direction:column;gap:14px;margin-top:auto}
.ex-linear-easing .one{display:flex;flex-direction:column;gap:7px}
.ex-linear-easing em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ex-linear-easing .ok em{color:var(--lime)}
.ex-linear-easing .ko em{color:var(--ink-3)}
.ex-linear-easing .tk{position:relative;display:block;width:100px;height:8px;border-radius:4px;
  background:var(--s2)}
.ex-linear-easing .tgt{position:absolute;left:88px;top:-4px;display:block;width:1px;height:16px;
  background:var(--ink-3)}
.ex-linear-easing .tk i{position:absolute;left:0;top:-2px;display:block;width:12px;height:12px;
  border-radius:6px;background:var(--lime)}
/* sept arrets, trois croisements */
.ex-linear-easing .ok .tk i{animation:aExLne 2.8s linear(0,.42,1.08,.94,1.05,.98,1) infinite}
/* deux points de controle, un seul depassement possible */
.ex-linear-easing .ko .tk i{animation:aExLne 2.8s cubic-bezier(.2,1.6,.3,1) infinite}
@keyframes aExLne{0%,6%{transform:translateX(0)}70%,92%{transform:translateX(88px)}
  100%{transform:translateX(0)}}
@keyframes aExLneBadge{0%,6%{transform:scale(0)}70%,92%{transform:scale(1)}100%{transform:scale(0)}}

/* ---------------- motion-path ----------------
   Le MEME trace et deux voyageurs. Le coursier est en offset-rotate auto,
   il s incline dans la courbe. L etiquette est a 0deg, elle reste droite
   du depart a l arrivee. C est la difference entre suivre une route et
   etre transporte le long d une route, et la consigne dit qu un texte
   veut toujours la seconde, sinon les mots basculent et le lecteur doit
   pencher la tete.
   Le d du SVG et celui des deux offset-path sont identiques au pixel
   pres, sans quoi ni l un ni l autre ne suivrait le trait dessine.
   Gele, on verrait deux formes sur une route, sans savoir laquelle
   s incline. */
.ex-motion-path{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-motion-path .map{position:absolute;inset:0}
.ex-motion-path .blk{position:absolute;display:block;border-radius:5px;background:var(--s2)}
.ex-motion-path .b1{left:14px;top:14px;width:44px;height:30px}
.ex-motion-path .b2{right:16px;top:20px;width:36px;height:26px}
.ex-motion-path .b3{left:56px;bottom:34px;width:48px;height:24px}
.ex-motion-path svg{position:absolute;left:0;top:20px;display:block;width:172px;height:96px}
.ex-motion-path .rail{fill:none;stroke:var(--s4);stroke-width:2;stroke-dasharray:4 4}
/* offset-rotate auto, il bascule avec la tangente */
.ex-motion-path .ship{position:absolute;left:0;top:20px;width:0;height:0;z-index:3;
  border-left:11px solid var(--lime);border-top:7px solid transparent;
  border-bottom:7px solid transparent;
  offset-path:path("M16 82C48 22 116 18 158 62");offset-rotate:auto;
  animation:aExMpt 4s cubic-bezier(.4,0,.2,1) infinite}
/* offset-rotate 0deg, elle reste droite */
.ex-motion-path .lab{position:absolute;left:0;top:20px;display:block;width:24px;height:8px;
  border-radius:4px;background:var(--a4);z-index:3;
  offset-path:path("M16 82C48 22 116 18 158 62");offset-rotate:0deg;
  animation:aExMpt 4s cubic-bezier(.4,0,.2,1) infinite;animation-delay:-.55s}
.ex-motion-path .tags{position:absolute;left:0;right:0;bottom:8px;display:flex;gap:14px;
  justify-content:center}
.ex-motion-path .tags em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none}
.ex-motion-path .ok{color:var(--lime)}
.ex-motion-path .ko{color:var(--a4)}
@keyframes aExMpt{0%,6%{offset-distance:0%}74%,90%{offset-distance:100%}100%{offset-distance:0%}}

/* ---------------- typed-custom-property ----------------
   Deux jauges, la MEME variable animee. A gauche elle est enregistree
   avec une syntaxe d angle, le navigateur sait donc interpoler et le
   balayage est continu. A droite elle n est pas enregistree, elle reste
   une chaine, et le navigateur bascule d un coup a mi parcours, donc
   deux sauts par cycle.
   Ce qui est anime est la VARIABLE et non le degrade, qui n a jamais ete
   interpolable et ne le sera pas.
   Aller retour de 0 a 180 puis retour, 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 jauges seraient identiques. */
@property --extprA{syntax:"<angle>";inherits:false;initial-value:0deg}
.ex-typed-custom-property{position:relative;width:172px;height:150px;border-radius:11px;
  overflow:hidden;background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:14px;
  align-items:center}
.ex-typed-custom-property .one{display:flex;flex-direction:column;align-items:center;gap:10px;flex:1}
.ex-typed-custom-property em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none}
.ex-typed-custom-property .ok em{color:var(--lime)}
.ex-typed-custom-property .ko em{color:var(--a4)}
.ex-typed-custom-property .gg{position:relative;display:grid;place-items:center;width:64px;height:64px;
  border-radius:50%}
.ex-typed-custom-property .gg i{display:block;width:40px;height:40px;border-radius:50%;
  background:var(--s0)}
/* enregistree, elle interpole */
.ex-typed-custom-property .gr{background:conic-gradient(from var(--extprA),var(--lime),
  var(--s2) 42%,var(--s2) 58%,var(--lime));animation:aExTprReg 3.8s linear infinite}
/* non enregistree, elle bascule a mi parcours */
.ex-typed-custom-property .gp{background:conic-gradient(from var(--extprB,0deg),var(--a4),
  var(--s2) 42%,var(--s2) 58%,var(--a4));animation:aExTprPlain 3.8s linear infinite}
@keyframes aExTprReg{0%,100%{--extprA:0deg}50%{--extprA:180deg}}
@keyframes aExTprPlain{0%,100%{--extprB:0deg}50%{--extprB:180deg}}

/* ---------------- popover ----------------
   Une barre d outils avec deux menus. A 36 pour cent le pointeur touche
   le document vide et le menu se ferme, sans qu aucun ecouteur de clic
   exterieur n existe. A 72 pour cent il touche le second declencheur, le
   premier menu se ferme TOUT SEUL et le second s ouvre, parce que la pile
   appartient au navigateur.
   La note le dit, il n y a pas de gestionnaire. C est la seule chose que
   cette scene doit prouver, puisqu un panneau qui grandit se refait a la
   main en dix lignes.
   Gele, on verrait un menu ouvert sous une barre d outils. */
.ex-popover{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-popover .tb{position:absolute;left:0;right:0;top:0;height:26px;background:var(--s1);display:flex;
  align-items:center;gap:9px;padding:0 12px;box-sizing:border-box;z-index:3}
.ex-popover .lg{display:block;width:14px;height:14px;border-radius:4px;background:var(--lime);
  flex:0 0 auto}
.ex-popover .ta,.ex-popover .tb2{display:block;width:34px;height:12px;border-radius:6px;
  background:var(--s3)}
.ex-popover .doc{position:absolute;left:12px;right:12px;top:38px;display:flex;flex-direction:column;
  gap:10px}
.ex-popover .doc i{display:block;height:7px;border-radius:3px;background:var(--s2)}
.ex-popover .doc i:last-child{width:58%}
.ex-popover .pa,.ex-popover .pb{position:absolute;top:30px;display:flex;flex-direction:column;gap:6px;
  width:70px;padding:9px;box-sizing:border-box;border-radius:9px;background:var(--lime);
  transform-origin:top left;opacity:0;z-index:4}
.ex-popover .pa{left:34px;animation:aExPopA 6.2s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-popover .pb{left:78px;animation:aExPopB 6.2s cubic-bezier(.2,1.1,.3,1) infinite}
.ex-popover .pa i,.ex-popover .pb i{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.6)}
.ex-popover .pa i:last-child,.ex-popover .pb i:last-child{width:54%}
.ex-popover .note{position:absolute;left:12px;bottom:8px;font:600 6px/1 var(--mono);color:var(--ink-3);
  font-style:normal;letter-spacing:0;text-transform:none}
.ex-popover .cur{left:44px;top:6px;z-index:5;animation:aExPopCur 6.2s cubic-bezier(.4,0,.4,1) infinite}
/* declencheur A, document vide, declencheur A, declencheur B */
@keyframes aExPopCur{0%{transform:translate(0,0);opacity:0}
  5%{transform:translate(0,0);opacity:1}
  28%{transform:translate(0,0);opacity:1}
  34%{transform:translate(46px,88px);opacity:1}
  50%{transform:translate(46px,88px);opacity:1}
  54%{transform:translate(0,0);opacity:1}
  68%{transform:translate(0,0);opacity:1}
  72%{transform:translate(44px,0);opacity:1}
  94%{transform:translate(44px,0);opacity:1}
  100%{transform:translate(0,0);opacity:0}}
/* ferme par le clic exterieur a 36, puis ferme TOUT SEUL a 72 */
@keyframes aExPopA{0%,7%{transform:scale(.1);opacity:0}
  17%,34%{transform:scale(1);opacity:1}
  44%,54%{transform:scale(.1);opacity:0}
  62%,70%{transform:scale(1);opacity:1}
  80%,100%{transform:scale(.1);opacity:0}}
@keyframes aExPopB{0%,72%{transform:scale(.1);opacity:0}
  80%,92%{transform:scale(1);opacity:1}
  99%,100%{transform:scale(.1);opacity:0}}

/* ---------------- position-try ----------------
   Une infobulle dans un tableau, pres du bord droit. Trois candidats,
   trois chips, celui qui resout est allume. block-start tant que la ligne
   survolee est assez basse, flip-block quand elle est contre le haut, et
   flip-inline quand elle est dans le coin ou aucun des deux ne tient.
   La LISTE est parcourue dans l ordre, c est le terme. Un seul
   basculement ne montrerait qu une inversion.
   Chaque bascule est instantanee, la consigne dit qu elle n est pas
   animable.
   Gele, on verrait une bulle a cote d une ligne, sans savoir combien de
   places ont ete essayees. */
.ex-position-try{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-position-try .tbl{display:flex;flex-direction:column;gap:12px}
.ex-position-try .tr{display:flex;align-items:center;gap:10px}
.ex-position-try .tr i{display:block;height:7px;border-radius:3px;background:var(--s2);flex:1}
.ex-position-try .tr i.s{flex:0 0 32px;background:var(--s3)}
.ex-position-try .anchor{position:absolute;left:132px;top:88px;display:block;width:12px;height:12px;
  border-radius:6px;background:var(--lime);z-index:3;
  animation:aExPtfA 5.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-position-try .tip{position:absolute;left:88px;top:62px;display:flex;align-items:center;width:60px;
  height:20px;padding:0 8px;box-sizing:border-box;border-radius:7px;background:var(--s5);z-index:4;
  animation:aExPtfT 5.6s steps(1,end) infinite}
.ex-position-try .tip i{display:block;width:100%;height:5px;border-radius:3px;background:var(--s1)}
.ex-position-try .cands{position:absolute;left:12px;bottom:8px;display:flex;flex-direction:column;
  gap:4px;z-index:5}
.ex-position-try .cands em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;color:var(--ink-3)}
.ex-position-try .c1{animation:aExPtfC1 5.6s steps(1,end) infinite}
.ex-position-try .c2{animation:aExPtfC2 5.6s steps(1,end) infinite}
.ex-position-try .c3{animation:aExPtfC3 5.6s steps(1,end) infinite}
/* l ancre monte contre le bord haut, puis va dans le coin */
@keyframes aExPtfA{0%,14%{transform:translate(0,0)}
  34%,50%{transform:translate(0,-62px)}
  68%,84%{transform:translate(24px,-62px)}
  98%,100%{transform:translate(0,0)}}
/* block-start, flip-block, flip-inline, chaque bascule instantanee */
@keyframes aExPtfT{0%,25%{transform:translate(0,0)}
  26%,58%{transform:translate(0,-30px)}
  59%,90%{transform:translate(-64px,-56px)}
  91%,100%{transform:translate(0,0)}}
@keyframes aExPtfC1{0%,25%{color:var(--lime)}26%,90%{color:var(--ink-3)}91%,100%{color:var(--lime)}}
@keyframes aExPtfC2{0%,25%{color:var(--ink-3)}26%,58%{color:var(--lime)}59%,100%{color:var(--ink-3)}}
@keyframes aExPtfC3{0%,58%{color:var(--ink-3)}59%,90%{color:var(--lime)}91%,100%{color:var(--ink-3)}}

/* ---------------- content-visibility ----------------
   Deux fils, cinq blocs chacun, deux a l ecran et trois hors champ. La
   BARRE DE DEFILEMENT est la ou se lit la difference. A gauche les blocs
   hors champ gardent leur boite reservee, le curseur garde donc sa taille
   et sa position. A droite ils s effondrent, le contenu total rapetrit,
   et le curseur grandit brusquement sous le doigt de l utilisateur.
   C est le vrai symptome de l oubli, et la propriete seule est un bug.
   Trois blocs de 18 avec 8 de gouttiere font 78, le curseur de droite
   passe donc de 40 a 62 de haut.
   Gele, les deux colonnes seraient identiques. */
.ex-content-visibility{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:12px}
.ex-content-visibility .one{display:flex;gap:7px;flex:1;min-width:0;position:relative;
  padding-top:14px}
.ex-content-visibility em{position:absolute;left:0;top:0;font:600 6px/1 var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none}
.ex-content-visibility .ok em{color:var(--lime)}
.ex-content-visibility .ko em{color:var(--a4)}
.ex-content-visibility .fd{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}
.ex-content-visibility .fd i{display:block;height:18px;border-radius:6px;flex:0 0 auto}
.ex-content-visibility .on{background:var(--lime)}
.ex-content-visibility .off{background:var(--s1)}
/* a droite, les blocs hors champ s effondrent */
.ex-content-visibility .ko .off{animation:aExCvzDrop 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-content-visibility .sb{position:relative;display:block;width:5px;border-radius:3px;
  background:var(--s2);flex:0 0 auto}
.ex-content-visibility .sb b{position:absolute;left:0;right:0;top:0;display:block;height:40px;
  border-radius:3px;background:var(--s5)}
/* le curseur grandit quand le contenu total rapetrit */
.ex-content-visibility .ko .sb b{animation:aExCvzThumb 4.6s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExCvzDrop{0%,14%{height:18px}46%,84%{height:0}96%,100%{height:18px}}
@keyframes aExCvzThumb{0%,14%{height:40px}46%,84%{height:62px}96%,100%{height:40px}}

/* ---------------- long-animation-frame ----------------
   Un panneau de performance. Le graphique dit qu une image a ete longue,
   ce que n importe quel compteur dit aussi. Ce que l API apporte est en
   dessous, les trois lignes qui arrivent l une apres l autre, la duree
   totale, la part bloquante, et le NOM du gestionnaire.
   L attribution arrive apres les barres, a 32, 42 et 52 pour cent, parce
   que c est l ordre dans lequel on lit un rapport.
   Echelle de 0.8px par milliseconde. Les images normales font 12ms soit
   10px, la longue 78ms soit 62, et le seuil de 50ms tombe a 40.
   La barre longue est decoupee, 45 en bas pour les 56ms bloquants et 17
   en haut pour les 22 restants.
   Gele, on verrait un graphique et trois lignes, ce qui est presque tout,
   mais pas l ordre de lecture. */
.ex-long-animation-frame{position:relative;width:172px;height:150px;border-radius:11px;
  overflow:hidden;background:var(--s0);padding:12px;box-sizing:border-box;display:flex;
  flex-direction:column;gap:10px}
.ex-long-animation-frame .hd{display:block;height:8px;width:56px;border-radius:4px;background:var(--ink);
  opacity:.85;flex:0 0 auto}
.ex-long-animation-frame .chart{position:relative;display:flex;align-items:flex-end;gap:5px;height:62px;
  flex:0 0 auto}
.ex-long-animation-frame .f{display:block;width:10px;height:10px;border-radius:3px;background:var(--lime);
  flex:0 0 auto;transform-origin:bottom;animation:aExLfrBar 4.6s cubic-bezier(.2,.9,.25,1) infinite}
/* 78ms a 0.8px par ms, soit 62 */
.ex-long-animation-frame .long{position:relative;display:flex;flex-direction:column;
  justify-content:flex-end;width:20px;height:62px;flex:0 0 auto;transform-origin:bottom;
  animation:aExLfrBar 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-long-animation-frame .nb{display:block;height:17px;border-radius:3px 3px 0 0;background:var(--a4);
  opacity:.45}
.ex-long-animation-frame .bl{display:block;height:45px;border-radius:0 0 3px 3px;background:var(--danger)}
/* le seuil de 50ms tombe a 40 */
.ex-long-animation-frame .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)}
.ex-long-animation-frame .rows{display:flex;flex-direction:column;gap:7px;margin-top:auto}
.ex-long-animation-frame .rw{display:flex;align-items:center;gap:8px;opacity:0}
.ex-long-animation-frame .rw em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:44px;text-align:right;color:var(--ink-3)}
.ex-long-animation-frame .rw b{display:block;height:6px;border-radius:3px}
.ex-long-animation-frame .d b{width:62px;background:var(--s5)}
.ex-long-animation-frame .b b{width:45px;background:var(--danger)}
.ex-long-animation-frame .s b{width:34px;background:var(--a4)}
.ex-long-animation-frame .s em{color:var(--a4)}
/* les barres, PUIS l attribution, une ligne apres l autre */
.ex-long-animation-frame .d{animation:aExLfrD 4.6s steps(1,end) infinite}
.ex-long-animation-frame .b{animation:aExLfrB 4.6s steps(1,end) infinite}
.ex-long-animation-frame .s{animation:aExLfrS 4.6s steps(1,end) infinite}
@keyframes aExLfrBar{0%,6%{transform:scaleY(0)}18%,86%{transform:scaleY(1)}96%,100%{transform:scaleY(0)}}
@keyframes aExLfrD{0%,31%{opacity:0}32%,86%{opacity:1}88%,100%{opacity:0}}
@keyframes aExLfrB{0%,41%{opacity:0}42%,86%{opacity:1}88%,100%{opacity:0}}
@keyframes aExLfrS{0%,51%{opacity:0}52%,86%{opacity:1}88%,100%{opacity:0}}

/* ---------------- scrub ----------------
   La tete S ARRETE quand le doigt s arrete, et c est la seule chose
   qu une animation sur horloge ne sait pas faire. Le glissement du haut
   comporte deux PAUSES et un retour en arriere, et l illustration comme
   les pastilles les recopient exactement.
   Aucune duree n est declaree, la note le dit. Une animation avec une
   duree et un scrub a deux maitres.
   La piste fait 104 et le curseur 10, la course vaut donc 94, et les
   quatre pastilles se remplissent aux quarts de cette course.
   Gele, on verrait une etape d onboarding, sans savoir qui la commande. */
.ex-scrub{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:14px}
.ex-scrub .lane{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.ex-scrub .lane em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;width:26px;
  text-align:right;letter-spacing:0;text-transform:none}
.ex-scrub .tk{position:relative;display:block;width:104px;height:10px;border-radius:5px;
  background:var(--s2)}
.ex-scrub .th{position:absolute;left:0;top:0;display:block;width:10px;height:10px;border-radius:5px;
  background:var(--ink);animation:aExScb 4.8s linear infinite}
.ex-scrub .stz{position:relative;flex:1;display:flex;flex-direction:column;gap:12px;
  justify-content:center;min-height:0}
.ex-scrub .ill{display:block;height:44px;border-radius:9px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));transform-origin:center;
  animation:aExScbIll 4.8s linear infinite}
.ex-scrub .pips{display:flex;justify-content:center;gap:8px}
.ex-scrub .pips b{display:block;width:9px;height:9px;border-radius:50%;background:var(--s3)}
.ex-scrub .pips b:nth-child(1){animation:aExScbP1 4.8s steps(1,end) infinite}
.ex-scrub .pips b:nth-child(2){animation:aExScbP2 4.8s steps(1,end) infinite}
.ex-scrub .pips b:nth-child(3){animation:aExScbP3 4.8s steps(1,end) infinite}
.ex-scrub .pips b:nth-child(4){animation:aExScbP4 4.8s steps(1,end) infinite}
.ex-scrub .note{position:absolute;right:12px;bottom:8px;font:600 6px/1 var(--mono);color:var(--ink-3);
  font-style:normal;letter-spacing:0;text-transform:none}
.ex-scrub .cur{left:11px;top:12px;animation:aExScb 4.8s linear infinite}
/* deux pauses et un retour en arriere, course de 94 */
@keyframes aExScb{0%,6%{transform:translateX(0)}
  22%,34%{transform:translateX(34px)}
  48%,58%{transform:translateX(94px)}
  70%,78%{transform:translateX(52px)}
  94%,100%{transform:translateX(0)}}
/* la meme forme, pauses comprises, sur une autre propriete */
@keyframes aExScbIll{0%,6%{transform:scale(.86) rotate(-4deg)}
  22%,34%{transform:scale(.94) rotate(-1deg)}
  48%,58%{transform:scale(1) rotate(0)}
  70%,78%{transform:scale(.96) rotate(-2deg)}
  94%,100%{transform:scale(.86) rotate(-4deg)}}
@keyframes aExScbP1{0%,5%{background:var(--s3)}6%,93%{background:var(--lime)}
  94%,100%{background:var(--s3)}}
@keyframes aExScbP2{0%,21%{background:var(--s3)}22%,73%{background:var(--lime)}
  74%,100%{background:var(--s3)}}
@keyframes aExScbP3{0%,35%{background:var(--s3)}36%,63%{background:var(--lime)}
  64%,100%{background:var(--s3)}}
@keyframes aExScbP4{0%,47%{background:var(--s3)}48%,57%{background:var(--lime)}
  58%,100%{background:var(--s3)}}

/* ---------------- pin ----------------
   Le RESERVOIR, montre par ce qui arrive sans lui. Les deux colonnes
   recoivent le meme defilement de 78. Celle de gauche fait 176 avec son
   bloc pointille, le pied de page arrive donc a sa place. Celle de droite
   ne fait que 142, il lui manque exactement les 34 du reservoir, et son
   pied de page arrive 34 trop tot.
   C est le detail propre a ce terme. La fiche scroll-pinning montre
   l autre moitie, ce que le defilement retenu paie.
   Gele, les deux colonnes seraient presque identiques, et c est pourquoi
   cet oubli survit aux relectures. */
.ex-pin{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:12px}
.ex-pin .one{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0}
.ex-pin em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ex-pin .ok em{color:var(--lime)}
.ex-pin .ko em{color:var(--a4)}
.ex-pin .vp{position:relative;display:block;flex:1;overflow:hidden;border-radius:8px;
  background:var(--s1);min-height:0}
.ex-pin .col{position:absolute;left:7px;right:7px;top:0;display:flex;flex-direction:column;gap:8px;
  animation:aExPinCol 4.8s linear infinite}
.ex-pin .ln{display:block;height:9px;border-radius:4px;background:var(--s3);flex:0 0 auto}
.ex-pin .ft{display:block;height:14px;border-radius:5px;background:var(--s5);flex:0 0 auto}
/* le reservoir, il rend au document la distance maintenue */
.ex-pin .sp{display:block;height:34px;border-radius:6px;box-sizing:border-box;
  border:1px dashed rgba(var(--accent-rgb),.5);flex:0 0 auto}
.ex-pin .crd{position:absolute;left:7px;right:7px;top:30px;display:flex;flex-direction:column;gap:6px;
  padding:8px;box-sizing:border-box;border-radius:7px;background:var(--lime);z-index:2;
  animation:aExPinCard 4.8s linear infinite}
.ex-pin .crd b{display:block;height:5px;border-radius:3px;background:rgba(var(--on-accent-rgb),.6)}
.ex-pin .crd b.s{width:56%}
@keyframes aExPinCol{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 au bout du defilement,
   se recale au retour, puis redescend */
@keyframes aExPinCard{0%,10%{transform:translateY(48px)}
  26%,40%{transform:translateY(0)}
  50%,58%{transform:translateY(-48px)}
  68%,82%{transform:translateY(0)}
  98%,100%{transform:translateY(48px)}}

/* ---------------- position-parameter ----------------
   Une chorégraphie d envoi, et les trois offsets sont ecrits sur les
   barres. Le pli part a zero. L envol part a moins 0.3, il mord donc sur
   la fin du pli, et l on voit les deux en meme temps sur la scene du bas.
   La coche est ancree sur le label sent, apres un vide voulu.
   Un recouvrement, un vide, un label. Une cascade uniforme n a ni l un ni
   l autre, et c est ce qui separe ce terme de stagger.
   La regle fait 132. Le pli va de 0 a 52, l envol de 34 a 86, le label est
   a 102 et la coche de 102 a 132.
   Gele, on verrait trois barres decalees, sans le recouvrement ni le
   vide. */
.ex-position-parameter{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px}
.ex-position-parameter .ruler{position:relative;display:block;width:132px;height:5px;border-radius:3px;
  background:var(--s2);margin-left:14px;flex:0 0 auto}
.ex-position-parameter .lbmk{position:absolute;left:102px;top:-4px;display:block;width:1px;height:13px;
  background:var(--a2)}
.ex-position-parameter .hdd{position:absolute;left:0;top:-5px;display:block;width:2px;height:15px;
  background:var(--ink);animation:aExPpmHead 4.6s linear infinite}
.ex-position-parameter .steps{position:relative;height:64px;flex:0 0 auto}
.ex-position-parameter .st{position:absolute;display:flex;align-items:center;height:17px;
  border-radius:6px;padding:0 6px;box-sizing:border-box;background:var(--lime)}
.ex-position-parameter .st em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;color:var(--on-accent);opacity:.85}
.ex-position-parameter .a{left:14px;top:0;width:52px;animation:aExPpmA 4.6s steps(1,end) infinite}
.ex-position-parameter .b{left:48px;top:23px;width:52px;background:var(--a2);
  animation:aExPpmB 4.6s steps(1,end) infinite}
.ex-position-parameter .c{left:116px;top:46px;width:32px;background:var(--a4);
  animation:aExPpmC 4.6s steps(1,end) infinite}
.ex-position-parameter .stz{position:relative;display:flex;align-items:center;justify-content:center;
  gap:14px;flex:1;min-height:0}
.ex-position-parameter .fold{display:block;width:26px;height:20px;border-radius:4px;background:var(--s5);
  animation:aExPpmFold 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-position-parameter .fly{display:block;width:20px;height:14px;border-radius:3px;background:var(--a2);
  animation:aExPpmFly 4.6s cubic-bezier(.3,.9,.3,1) infinite}
.ex-position-parameter .tick{display:block;width:14px;height:14px;border-radius:50%;background:var(--a4);
  animation:aExPpmTick 4.6s cubic-bezier(.2,1.4,.3,1) infinite}
/* elle s eteint aux deux bouts, le rembobinage ne se voit pas */
@keyframes aExPpmHead{0%{transform:translateX(0);opacity:0}
  6%{opacity:1}94%{opacity:1}
  100%{transform:translateX(130px);opacity:0}}
@keyframes aExPpmA{0%,5%{opacity:.35}6%,41%{opacity:1}42%,100%{opacity:.35}}
@keyframes aExPpmB{0%,28%{opacity:.35}29%,64%{opacity:1}65%,100%{opacity:.35}}
@keyframes aExPpmC{0%,74%{opacity:.35}75%,94%{opacity:1}95%,100%{opacity:.35}}
/* le pli de 6 a 41, l envol de 29 a 64, ils se recouvrent de 29 a 41 */
@keyframes aExPpmFold{0%,6%{transform:scaleY(1)}41%,94%{transform:scaleY(.35)}
  100%{transform:scaleY(1)}}
@keyframes aExPpmFly{0%,29%{transform:translateX(0);opacity:.25}
  64%,94%{transform:translateX(22px);opacity:1}100%{transform:translateX(0);opacity:.25}}
@keyframes aExPpmTick{0%,74%{transform:scale(0)}88%,94%{transform:scale(1)}100%{transform:scale(0)}}

/* ---------------- normalized-progress ----------------
   UNE valeur, quatre proprietes derivees. L image se decale, le titre
   s allonge, la sous ligne s eclaircit et la pastille grossit, toutes
   depuis la meme progression et sur le meme cycle.
   La piste du bas garde son PROPRE etat, sur 5.4s contre 4.2, et elle se
   decale un peu plus a chaque passage. C est elle qui prouve le terme, et
   c est exactement la desynchronisation que la consigne decrit, invisible
   au debut et certaine ensuite.
   La valeur fait un aller retour, ce qui est honnete puisqu une
   progression liee a une entree se rembobine.
   La piste fait 104 et la tete 2, la course vaut donc 102.
   Gele, on verrait un hero, sans savoir que quatre proprietes lisent le
   meme nombre. */
.ex-normalized-progress{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-normalized-progress .src,.ex-normalized-progress .drift{display:flex;align-items:center;gap:8px;
  flex:0 0 auto}
.ex-normalized-progress 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}
.ex-normalized-progress .tk{position:relative;display:block;width:100px;height:6px;border-radius:3px;
  background:var(--s2)}
.ex-normalized-progress .hdd{position:absolute;left:0;top:-4px;display:block;width:2px;height:14px;
  background:var(--ink);animation:aExNpgP 4.2s linear infinite}
.ex-normalized-progress .dh{position:absolute;left:0;top:-4px;display:block;width:2px;height:14px;
  background:var(--a4);animation:aExNpgP 5.4s linear infinite}
.ex-normalized-progress .hero{position:relative;flex:1;border-radius:9px;overflow:hidden;
  background:var(--s1);min-height:0}
/* les quatre sorties, toutes derivees de la meme valeur et du meme cycle */
.ex-normalized-progress .im{position:absolute;left:0;right:0;top:0;height:44px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExNpgIm 4.2s linear infinite}
.ex-normalized-progress .ti{position:absolute;left:10px;top:54px;display:block;width:0;height:9px;
  border-radius:4px;background:var(--ink);opacity:.85;animation:aExNpgTi 4.2s linear infinite}
.ex-normalized-progress .sb{position:absolute;left:10px;top:70px;display:block;width:52px;height:6px;
  border-radius:3px;background:var(--s3);animation:aExNpgSb 4.2s linear infinite}
.ex-normalized-progress .badge{position:absolute;right:10px;top:54px;display:block;width:14px;
  height:14px;border-radius:50%;background:var(--a2);animation:aExNpgBd 4.2s linear infinite}
@keyframes aExNpgP{0%,6%{transform:translateX(0)}50%{transform:translateX(98px)}
  94%,100%{transform:translateX(0)}}
@keyframes aExNpgIm{0%,6%{transform:translateY(0)}50%{transform:translateY(-14px)}
  94%,100%{transform:translateY(0)}}
@keyframes aExNpgTi{0%,6%{width:0}50%{width:74px}94%,100%{width:0}}
@keyframes aExNpgSb{0%,6%{background:var(--s3)}50%{background:var(--lime)}
  94%,100%{background:var(--s3)}}
@keyframes aExNpgBd{0%,6%{transform:scale(.5)}50%{transform:scale(1)}94%,100%{transform:scale(.5)}}

/* ---------------- time-scale ----------------
   Un apercu de montage. La pellicule et la piste du bas suivent le MEME
   multiplicateur, qui passe de 1 a 0.25 puis revient a 1 et devient
   NEGATIF, ce qui rejoue tout a l envers. La jauge signee le dit.
   La piste du haut, a 1x constant, est la reference inerte. Le contraste
   entre les deux sur le meme cycle est la demonstration, et il ne
   s ecrirait pas avec deux durees.
   La bande fait 6 fois 26 avec 5 de gouttiere, soit 181, dans une fenetre
   de 148, la course vaut donc 33. La piste fait 92 pour un point de 9,
   soit 83 de course.
   Gele, on verrait une pellicule, sans la cadence ni son signe. */
.ex-time-scale{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:11px}
.ex-time-scale .vp{position:relative;height:48px;overflow:hidden;border-radius:8px;flex:0 0 auto}
.ex-time-scale .strip{position:absolute;left:0;top:0;display:flex;gap:5px;
  animation:aExTscStrip 4.8s linear infinite}
.ex-time-scale .strip i{display:block;width:26px;height:48px;border-radius:5px;background:var(--s3);
  flex:0 0 auto}
.ex-time-scale .strip i:nth-child(odd){background:var(--s2)}
.ex-time-scale .lane{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.ex-time-scale 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}
.ex-time-scale .tk{position:relative;display:block;width:92px;height:7px;border-radius:4px;
  background:var(--s2)}
.ex-time-scale .tk b{position:absolute;left:0;top:-1px;display:block;width:9px;height:9px;
  border-radius:5px;background:var(--lime)}
/* reference a cadence constante */
.ex-time-scale .n1{animation:aExTscRef 4.8s linear infinite}
.ex-time-scale .n2{animation:aExTscScaled 4.8s linear infinite}
.ex-time-scale .gauge{position:relative;display:block;height:5px;border-radius:3px;background:var(--s2);
  margin-top:auto;flex:0 0 auto}
.ex-time-scale .gauge b{position:absolute;top:0;bottom:0;width:50%;border-radius:3px}
.ex-time-scale .pos{left:50%;transform-origin:left;transform:scaleX(0);background:var(--a2);
  animation:aExTscPos 4.8s linear infinite}
.ex-time-scale .neg{left:0;transform-origin:right;transform:scaleX(0);background:var(--a4);
  animation:aExTscNeg 4.8s linear infinite}
.ex-time-scale .zero{position:absolute;left:50%;top:-3px;display:block;width:1px;height:11px;
  background:var(--ink-3)}
@keyframes aExTscRef{0%,6%{transform:translateX(0)}50%{transform:translateX(83px)}
  94%,100%{transform:translateX(0)}}
/* 1x, puis 0.25x, puis 1x, puis negatif */
@keyframes aExTscScaled{0%,8%{transform:translateX(0)}
  24%{transform:translateX(37px)}52%{transform:translateX(47px)}
  64%{transform:translateX(83px)}80%{transform:translateX(25px)}
  96%,100%{transform:translateX(0)}}
/* la pellicule suit exactement le meme multiplicateur, course de 33 */
@keyframes aExTscStrip{0%,8%{transform:translateX(0)}
  24%{transform:translateX(-15px)}52%{transform:translateX(-19px)}
  64%{transform:translateX(-33px)}80%{transform:translateX(-10px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExTscPos{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 aExTscNeg{0%,64%{transform:scaleX(0)}66%,95%{transform:scaleX(.6)}
  97%,100%{transform:scaleX(0)}}

/* ---------------- tick-loop ----------------
   Un tableau de bord. La barre du haut est le tick, et les quatre courbes
   lisent le MEME horodatage, leurs pics tombent donc tous sur la meme
   image, sans un seul delai declare.
   La note dit ce que cela coute, un rappel par image pour quatre
   courbes, et non quatre.
   La version abstraite montre la derive de l alternative. Ici l unisson
   suffit, parce que le cas reel est celui ou il faut que quarante courbes
   restent d accord.
   Gele, on verrait quatre courbes identiques. */
.ex-tick-loop{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:11px}
.ex-tick-loop .tick{display:block;height:5px;border-radius:3px;background:var(--s2);overflow:hidden;
  flex:0 0 auto}
.ex-tick-loop .tick b{display:block;width:100%;height:5px;border-radius:3px;background:var(--ink);
  transform-origin:left;animation:aExTklTick 1.2s steps(1,end) infinite}
.ex-tick-loop .gri{display:grid;grid-template-columns:1fr 1fr;gap:10px;flex:1;min-height:0}
.ex-tick-loop .cd{position:relative;display:block;border-radius:8px;background:var(--s1);
  overflow:hidden}
.ex-tick-loop .pk{position:absolute;left:0;right:0;bottom:0;display:block;height:100%;
  background:linear-gradient(180deg,transparent,rgba(var(--accent-rgb),.28));transform-origin:bottom;
  animation:aExTklPk 1.2s ease-in-out infinite}
.ex-tick-loop .ln{position:absolute;left:6px;right:6px;bottom:8px;display:block;height:2px;
  border-radius:2px;background:var(--lime);transform-origin:left;
  animation:aExTklLn 1.2s ease-in-out infinite}
.ex-tick-loop .note{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;letter-spacing:0;
  text-transform:none;flex:0 0 auto}
@keyframes aExTklTick{0%,44%{transform:scaleX(1)}48%,96%{transform:scaleX(.04)}
  100%{transform:scaleX(1)}}
@keyframes aExTklPk{0%,100%{transform:scaleY(.4)}50%{transform:scaleY(1)}}
@keyframes aExTklLn{0%,100%{transform:scaleX(.5)}50%{transform:scaleX(1)}}

/* ---------------- motion-value ----------------
   Une poignee de redimensionnement. Les deux barres du bas sont le
   compteur de rendus. Celle du haut reste a ZERO pendant tout le
   glissement, celle du bas se remplit image apres image, et c est la
   seule difference entre les deux facons d ecrire la meme chose.
   Le panneau bouge pareil dans les deux cas, ce qui est precisement
   pourquoi ce choix ne se voit jamais a l ecran et se paie en profilage.
   La barre se vide a 94 pour cent, sinon la boucle sauterait.
   La poignee va de 62 a 108, la course vaut donc 46.
   Gele, on verrait un panneau redimensionne, et rien du cout. */
.ex-motion-value{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-motion-value .panel{position:relative;display:flex;gap:0;height:74px;border-radius:8px;
  overflow:hidden;background:var(--s1);flex:0 0 auto}
.ex-motion-value .side{display:flex;flex-direction:column;gap:8px;width:62px;padding:10px;
  box-sizing:border-box;background:var(--s2);animation:aExMvlSide 4s cubic-bezier(.4,0,.4,1) infinite}
.ex-motion-value .side i{display:block;height:6px;border-radius:3px;background:var(--s4)}
.ex-motion-value .main{display:flex;flex-direction:column;gap:8px;flex:1;padding:10px;
  box-sizing:border-box}
.ex-motion-value .main i{display:block;height:7px;border-radius:3px;background:var(--s3)}
.ex-motion-value .main i:last-child{width:58%}
.ex-motion-value .grip{position:absolute;left:62px;top:0;bottom:0;display:block;width:3px;
  background:var(--lime);animation:aExMvlGrip 4s cubic-bezier(.4,0,.4,1) infinite}
.ex-motion-value .rows{display:flex;flex-direction:column;gap:9px;margin-top:auto}
.ex-motion-value .rw{display:flex;align-items:center;gap:8px}
.ex-motion-value em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  width:54px;text-align:right}
.ex-motion-value .ok em{color:var(--lime)}
.ex-motion-value .ko em{color:var(--a4)}
.ex-motion-value .rn{position:relative;display:block;flex:1;height:8px;border-radius:4px;
  background:var(--s2);overflow:hidden}
.ex-motion-value .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, elle se remplit puis se vide a 94 */
.ex-motion-value .fill{animation:aExMvlRn 4s linear infinite}
.ex-motion-value .cur{left:57px;top:34px;animation:aExMvlGrip 4s cubic-bezier(.4,0,.4,1) infinite}
/* la poignee va de 62 a 108, course de 46 */
@keyframes aExMvlGrip{0%,8%{transform:translateX(0)}52%{transform:translateX(46px)}
  94%,100%{transform:translateX(0)}}
@keyframes aExMvlSide{0%,8%{width:62px}52%{width:108px}94%,100%{width:62px}}
@keyframes aExMvlRn{0%,8%{width:0}88%{width:100%}94%,100%{width:0}}

/* ---------------- range-mapping ----------------
   Un en-tete qui reste OPAQUE sur le premier tiers du scroll puis se
   fond. Les trois traits sur la piste du bas sont les arrets, a 0, 30 et
   72 de la course.
   Entre le premier et le deuxieme l en-tete ne change pas du tout, c est
   le plateau, et c est le seul usage des arrets intermediaires. Passe le
   troisieme, le scroll continue et l en-tete reste a son minimum, c est
   le bornage.
   Le document fait 250 dans une fenetre de 72, la course vaut donc 178.
   La piste fait 96 pour un point de 9, soit 87, et les arrets tombent a
   0, 26 et 63.
   Gele, on verrait un en-tete a demi transparent, sans savoir pourquoi. */
.ex-range-mapping{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-range-mapping .hdr{position:absolute;left:0;right:0;top:0;height:26px;z-index:3;
  background:var(--lime);display:flex;align-items:center;gap:9px;padding:0 12px;box-sizing:border-box;
  animation:aExRmpHdr 4.6s linear infinite}
.ex-range-mapping .lg{display:block;width:14px;height:14px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.75);flex:0 0 auto}
.ex-range-mapping .nv{display:block;width:36px;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.55)}
.ex-range-mapping .vp{position:absolute;left:12px;right:12px;top:34px;height:72px;overflow:hidden}
.ex-range-mapping .doc{position:absolute;left:0;right:0;top:0;height:250px;display:flex;
  flex-direction:column;gap:9px;animation:aExRmpDoc 4.6s linear infinite}
.ex-range-mapping .doc i{display:block;height:7px;border-radius:3px;background:var(--s3);flex:0 0 auto}
.ex-range-mapping .lane{position:absolute;left:12px;right:12px;bottom:12px;display:flex;
  align-items:center;gap:8px}
.ex-range-mapping em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;width:34px;
  text-align:right;letter-spacing:0;text-transform:none}
.ex-range-mapping .tk{position:relative;display:block;width:96px;height:7px;border-radius:4px;
  background:var(--s2)}
.ex-range-mapping .tk u{position:absolute;top:-3px;display:block;width:1px;height:13px;
  background:rgba(var(--accent-rgb),.5)}
.ex-range-mapping .s1{left:0}
.ex-range-mapping .s2{left:26px}
.ex-range-mapping .s3{left:63px}
.ex-range-mapping .tk b{position:absolute;left:0;top:-1px;display:block;width:9px;height:9px;
  border-radius:5px;background:var(--lime);animation:aExRmpIn 4.6s linear infinite}
/* trois arrets, 0 puis 26 puis 63, et la course va jusqu a 87 */
@keyframes aExRmpIn{0%,6%{transform:translateX(0)}
  32%{transform:translateX(26px)}
  60%{transform:translateX(63px)}
  76%,84%{transform:translateX(87px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExRmpDoc{0%,6%{transform:translateY(0)}
  32%{transform:translateY(-53px)}
  60%{transform:translateY(-129px)}
  76%,84%{transform:translateY(-178px)}
  96%,100%{transform:translateY(0)}}
/* plateau de 0 a 32, fondu de 32 a 60, borne ensuite */
@keyframes aExRmpHdr{0%,32%{opacity:1}60%,84%{opacity:.25}96%,100%{opacity:1}}

/* ---------------- variant-propagation ----------------
   Un seul nom d etat, et quatre enfants qui le reprennent CHACUN A SA
   FACON. Le visuel se fond, le titre glisse, la sous ligne s allonge, le
   bouton grandit avec un depassement. Aucun n a ete adresse
   individuellement.
   La carte finit de s ouvrir a 28 pour cent, AVANT que le premier enfant
   commence a 32, ce qui est l ordonnancement beforeChildren de la
   consigne.
   Gele, on verrait une carte ouverte, sans savoir que le nom a voyage ni
   que chacun l a interprete. */
.ex-variant-propagation{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;padding-top:26px}
.ex-variant-propagation .nm{position:absolute;left:12px;top:8px;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)}
.ex-variant-propagation .crd{display:flex;flex-direction:column;gap:9px;height:100%;padding:11px;
  box-sizing:border-box;border-radius:10px;background:var(--lime);transform-origin:center;
  animation:aExVprCard 4.6s cubic-bezier(.2,1,.3,1) infinite}
.ex-variant-propagation .ch{display:block}
.ex-variant-propagation .im{display:block;height:40px;border-radius:7px;
  background:rgba(var(--on-accent-rgb),.28)}
.ex-variant-propagation .tx{display:block;height:8px;border-radius:4px;
  background:rgba(var(--on-accent-rgb),.6)}
.ex-variant-propagation .tx.s{width:58%;background:rgba(var(--on-accent-rgb),.4)}
.ex-variant-propagation .bt{display:block;width:52px;height:14px;border-radius:7px;
  background:rgba(var(--on-accent-rgb),.75)}
/* chacun interprete le meme nom autrement */
.ex-variant-propagation .c1 .im{animation:aExVprFade 4.6s ease infinite}
.ex-variant-propagation .c2 .tx{animation:aExVprSlide 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-variant-propagation .c3 .tx{transform-origin:left;
  animation:aExVprGrow 4.6s cubic-bezier(.2,.9,.25,1) infinite}
.ex-variant-propagation .c4 .bt{animation:aExVprPop 4.6s cubic-bezier(.2,1.6,.3,1) infinite}
/* la carte finit a 28, les enfants commencent a 32 */
@keyframes aExVprCard{0%,8%{transform:scale(.6);opacity:0}
  28%,80%{transform:scale(1);opacity:1}96%,100%{transform:scale(.6);opacity:0}}
@keyframes aExVprFade{0%,32%{opacity:0}48%,80%{opacity:1}92%,100%{opacity:0}}
@keyframes aExVprSlide{0%,38%{transform:translateX(-22px);opacity:0}
  54%,80%{transform:translateX(0);opacity:1}92%,100%{transform:translateX(-22px);opacity:0}}
@keyframes aExVprGrow{0%,44%{transform:scaleX(.15);opacity:0}
  60%,80%{transform:scaleX(1);opacity:1}92%,100%{transform:scaleX(.15);opacity:0}}
@keyframes aExVprPop{0%,50%{transform:scale(0)}68%,80%{transform:scale(1)}
  92%,100%{transform:scale(0)}}

/* ---------------- pop-layout ----------------
   Une tache cochee. A gauche elle est sortie du flux des qu elle part, la
   tache du bas remonte donc PENDANT sa sortie. A droite elle reste dans
   le flux, il faut attendre qu elle ait disparu, et l on voit le trou.
   C est ce trou qui est le sujet. Sur une longue liste il tient un
   demi seconde et l utilisateur regarde un emplacement vide.
   Les lignes font 26 avec 8 de gouttiere, la remontee vaut donc 34.
   Gele, les deux colonnes montreraient deux taches, sans savoir laquelle
   a attendu. */
.ex-pop-layout{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;gap:12px}
.ex-pop-layout .one{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}
.ex-pop-layout em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none}
.ex-pop-layout .ok em{color:var(--lime)}
.ex-pop-layout .ko em{color:var(--a4)}
.ex-pop-layout .col{position:relative;display:block;flex:1;min-height:0}
.ex-pop-layout .it{position:absolute;left:0;right:0;display:flex;align-items:center;gap:8px;height:26px;
  padding:0 8px;box-sizing:border-box;border-radius:8px;background:var(--s2)}
.ex-pop-layout .it b{display:block;width:11px;height:11px;border-radius:3px;box-sizing:border-box;
  border:1.5px solid var(--s5);flex:0 0 auto}
.ex-pop-layout .it b.on{background:var(--lime);border-color:var(--lime)}
.ex-pop-layout .it i{display:block;flex:1;height:6px;border-radius:3px;background:var(--s4)}
.ex-pop-layout .it:nth-of-type(1){top:0}
.ex-pop-layout .go{top:34px}
.ex-pop-layout .r3{top:68px}
.ex-pop-layout .go{animation:aExPlqGo 4s cubic-bezier(.3,.9,.3,1) infinite}
/* a gauche la voisine remonte PENDANT la sortie */
.ex-pop-layout .ok .r3{animation:aExPlqNow 4s cubic-bezier(.3,.9,.3,1) infinite}
/* a droite elle attend la fin */
.ex-pop-layout .ko .r3{animation:aExPlqAfter 4s cubic-bezier(.3,.9,.3,1) infinite}
/* la liste des fonctions de transform doit etre ecrite IDENTIQUEMENT aux
   deux bouts, scale(1) et scale(1) translateX(0) sont equivalents pour le
   navigateur et differents pour le detecteur */
@keyframes aExPlqGo{0%,16%{transform:scale(1) translateX(0);opacity:1}
  52%,84%{transform:scale(.88) translateX(14px);opacity:0}
  96%,100%{transform:scale(1) translateX(0);opacity:1}}
@keyframes aExPlqNow{0%,16%{transform:translateY(0)}52%,84%{transform:translateY(-34px)}
  96%,100%{transform:translateY(0)}}
@keyframes aExPlqAfter{0%,52%{transform:translateY(0)}68%,84%{transform:translateY(-34px)}
  96%,100%{transform:translateY(0)}}

/* ---------------- exit-before-enter ----------------
   Deux panneaux d onglet de hauteurs INEGALES. A gauche ils ne sont
   jamais montes ensemble, le conteneur passe donc de 34 a 70 entre les
   deux et chacun est mesure seul. A droite ils se recouvrent, le
   conteneur doit tenir 70 en permanence, et le panneau court laisse un
   vide sous lui.
   C est la vraie raison de preferer ce mode a un empilement en absolu, et
   c est ce qui le separe de fade-through, qui est une transition Material
   precise avec son trou de 90ms et son echelle de 0.92.
   Deux lignes de 7 avec 6 de gouttiere et 8 de padding font 34, quatre en
   font 70.
   Gele, on verrait deux panneaux, sans savoir lequel attend. */
.ex-exit-before-enter{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:11px}
.ex-exit-before-enter .tbs{display:flex;gap:8px;flex:0 0 auto}
.ex-exit-before-enter .tbs i{display:block;width:30px;height:11px;border-radius:6px;background:var(--s3)}
.ex-exit-before-enter .t1{animation:aExEbeT1 4.4s steps(1,end) infinite}
.ex-exit-before-enter .t2{animation:aExEbeT2 4.4s steps(1,end) infinite}
.ex-exit-before-enter .one{display:flex;gap:9px;align-items:flex-start}
.ex-exit-before-enter em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:52px;text-align:right;padding-top:4px}
.ex-exit-before-enter .ok em{color:var(--lime)}
.ex-exit-before-enter .ko em{color:var(--a4)}
.ex-exit-before-enter .bx{position:relative;display:block;flex:1;border-radius:8px;background:var(--s1);
  overflow:hidden;padding:8px;box-sizing:border-box}
/* le conteneur suit la vue montee */
.ex-exit-before-enter .ok .bx{height:34px;animation:aExEbeBox 4.4s cubic-bezier(.3,.9,.3,1) infinite}
/* il doit tenir la plus haute en permanence */
.ex-exit-before-enter .ko .bx{height:70px}
.ex-exit-before-enter .v{position:absolute;left:8px;right:8px;top:8px;display:flex;
  flex-direction:column;gap:6px}
.ex-exit-before-enter .v i{display:block;height:7px;border-radius:3px;flex:0 0 auto}
.ex-exit-before-enter .a i{background:var(--s5)}
.ex-exit-before-enter .b i{background:var(--lime)}
.ex-exit-before-enter .ok .a{animation:aExEbeWaitA 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-exit-before-enter .ok .b{animation:aExEbeWaitB 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-exit-before-enter .ko .a{animation:aExEbeConA 4.4s cubic-bezier(.3,.9,.3,1) infinite}
.ex-exit-before-enter .ko .b{animation:aExEbeConB 4.4s cubic-bezier(.3,.9,.3,1) infinite}
/* deux lignes font 34, quatre en font 70 */
@keyframes aExEbeBox{0%,18%{height:34px}44%,80%{height:70px}96%,100%{height:34px}}
@keyframes aExEbeWaitA{0%,18%{opacity:1}32%,88%{opacity:0}96%,100%{opacity:1}}
@keyframes aExEbeWaitB{0%,36%{opacity:0}50%,80%{opacity:1}92%,100%{opacity:0}}
@keyframes aExEbeConA{0%,18%{opacity:1}50%,88%{opacity:0}96%,100%{opacity:1}}
@keyframes aExEbeConB{0%,18%{opacity:0}50%,80%{opacity:1}92%,100%{opacity:0}}
@keyframes aExEbeT1{0%,20%{background:var(--lime)}22%,90%{background:var(--s3)}
  92%,100%{background:var(--lime)}}
@keyframes aExEbeT2{0%,20%{background:var(--s3)}22%,90%{background:var(--lime)}
  92%,100%{background:var(--s3)}}

/* ---------------- flip ----------------
   Une carte kanban qui change de colonne. Les quatre temps sont nommes.
   First, la boite est mesuree et le fantome pointille la garde. Last, la
   carte SAUTE instantanement dans la colonne de droite, sa vraie place
   dans la mise en page. Invert, une transformation la ramene exactement
   sur le fantome, elle a donc l air de ne pas avoir bouge. Play, cette
   transformation est animee vers rien.
   Le saut de Last est instantane et c est tout le sujet. La mise en page
   est VRAIE des cet instant, seule l apparence est rattrapee ensuite, et
   c est pourquoi rien n a a etre tenu synchrone.
   Les colonnes sont a 0 et a 74, la course vaut donc 74.
   Gele, on verrait une carte dans une colonne. */
.ex-flip{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box}
.ex-flip .cols{display:flex;gap:10px}
.ex-flip .cl{display:block;width:69px;height:104px;border-radius:8px;box-sizing:border-box;
  border:1px dashed var(--s5);background:var(--s1);padding:8px}
.ex-flip .hd{display:block;height:7px;width:60%;border-radius:3px;background:var(--s4)}
/* le fantome, la boite mesuree a First */
.ex-flip .ghost{position:absolute;left:20px;top:38px;display:block;width:53px;height:30px;
  border-radius:7px;box-sizing:border-box;border:1px dashed rgba(var(--accent-rgb),.6);
  animation:aExFlpGhost 5.2s steps(1,end) infinite}
.ex-flip .cd{position:absolute;left:20px;top:38px;display:flex;flex-direction:column;
  justify-content:center;gap:6px;width:53px;height:30px;padding:0 8px;box-sizing:border-box;
  border-radius:7px;background:var(--lime);animation:aExFlpCd 5.2s cubic-bezier(.3,.9,.3,1) infinite}
.ex-flip .cd b{display:block;height:5px;border-radius:3px;background:rgba(var(--on-accent-rgb),.6)}
.ex-flip .cd b.s{width:54%}
.ex-flip .beats{position:absolute;left:12px;right:12px;bottom:6px;display:flex;
  justify-content:space-between}
.ex-flip .beats em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  color:var(--ink-3)}
.ex-flip .b1{animation:aExFlpB1 5.2s steps(1,end) infinite}
.ex-flip .b2{animation:aExFlpB2 5.2s steps(1,end) infinite}
.ex-flip .b3{animation:aExFlpB3 5.2s steps(1,end) infinite}
.ex-flip .b4{animation:aExFlpB4 5.2s steps(1,end) infinite}
/* Last saute a 26, Invert ramene a 32, Play anime de 40 a 74 */
@keyframes aExFlpCd{0%,25%{transform:translateX(0)}
  26%,31%{transform:translateX(74px)}
  32%,39%{transform:translateX(0)}
  74%,94%{transform:translateX(74px)}
  95%,100%{transform:translateX(0)}}
@keyframes aExFlpGhost{0%,25%{opacity:.9}26%,31%{opacity:0}32%,74%{opacity:.9}
  75%,96%{opacity:0}100%{opacity:.9}}
@keyframes aExFlpB1{0%,25%{color:var(--lime)}26%,94%{color:var(--ink-3)}95%,100%{color:var(--lime)}}
@keyframes aExFlpB2{0%,25%{color:var(--ink-3)}26%,31%{color:var(--lime)}32%,100%{color:var(--ink-3)}}
@keyframes aExFlpB3{0%,31%{color:var(--ink-3)}32%,39%{color:var(--lime)}40%,100%{color:var(--ink-3)}}
@keyframes aExFlpB4{0%,39%{color:var(--ink-3)}40%,74%{color:var(--lime)}75%,100%{color:var(--ink-3)}}

/* ---------------- scale-correction ----------------
   Une carte qui s elargit. Les deux sont mises a l echelle 2, celle du
   haut divise son avatar et son texte par 2, donc 0.5, ET son rayon
   horizontal par 2, donc 11 devient 5.5. Celle du bas ne corrige rien.
   Le disque de l avatar devient un ovale et les lignes de texte
   s allongent, ce qui sont les deux tells de la definition, et le rayon
   des coins avec eux.
   Gele, les deux cartes seraient etirees pareil, et la correction serait
   invisible, ce qui est exactement pourquoi on l oublie. */
.ex-scale-correction{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:14px;
  justify-content:center}
.ex-scale-correction .one{display:flex;flex-direction:column;gap:7px}
.ex-scale-correction em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none}
.ex-scale-correction .ok em{color:var(--lime)}
.ex-scale-correction .ko em{color:var(--a4)}
.ex-scale-correction .cd{display:flex;align-items:center;gap:9px;width:72px;height:38px;padding:0 9px;
  box-sizing:border-box;border-radius:11px;background:var(--lime);transform-origin:left}
.ex-scale-correction .av{display:block;width:20px;height:20px;border-radius:50%;
  background:rgba(var(--on-accent-rgb),.7);flex:0 0 auto}
.ex-scale-correction .tx{display:flex;flex-direction:column;gap:5px;flex:1;transform-origin:left}
.ex-scale-correction .tx b{display:block;height:5px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.5)}
.ex-scale-correction .tx b.s{width:58%}
/* 11 divise par 2 vaut 5.5, et 1 divise par 2 vaut 0.5 */
.ex-scale-correction .ok .cd{animation:aExScrBox 3.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-scale-correction .ok .av{animation:aExScrChild 3.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-scale-correction .ok .tx{animation:aExScrChild 3.6s cubic-bezier(.4,0,.4,1) infinite}
.ex-scale-correction .ko .cd{animation:aExScrRaw 3.6s cubic-bezier(.4,0,.4,1) infinite}
@keyframes aExScrBox{0%,10%{transform:scaleX(1);border-radius:11px}
  50%,80%{transform:scaleX(2);border-radius:5.5px/11px}
  96%,100%{transform:scaleX(1);border-radius:11px}}
@keyframes aExScrChild{0%,10%{transform:scaleX(1)}50%,80%{transform:scaleX(.5)}
  96%,100%{transform:scaleX(1)}}
@keyframes aExScrRaw{0%,10%{transform:scaleX(1)}50%,80%{transform:scaleX(2)}
  96%,100%{transform:scaleX(1)}}

/* ---------------- drag-constraints ----------------
   Une feuille tiree vers le haut. Le trait rouge est sa borne, posee a 26
   du haut du cadre. Le doigt la depasse jusqu a 14, contre une resistance
   croissante, et au relachement elle revient se poser a 26.
   C est le retour elastique qui dit que le bord est reel sans que le
   geste semble casse. Avec une elasticite de zero elle s arreterait net,
   ce que la version abstraite montre a cote.
   La feuille repose a 96, sa borne est a 26, la course vaut donc 70, et le
   depassement va a 82.
   Gele, on verrait une feuille haute, sans savoir si elle a le droit
   d etre la. */
.ex-drag-constraints{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-drag-constraints .scene{position:absolute;inset:0}
.ex-drag-constraints .bg{position:absolute;left:12px;right:12px;top:12px;display:flex;
  flex-direction:column;gap:9px}
.ex-drag-constraints .bg i{display:block;height:7px;border-radius:3px;background:var(--s2)}
.ex-drag-constraints .bg i:last-child{width:56%}
/* la borne, a 26 du haut */
.ex-drag-constraints .limit{position:absolute;left:0;right:0;top:26px;display:block;height:2px;
  background:rgba(var(--danger-rgb),.7);z-index:3}
.ex-drag-constraints .sheet{position:absolute;left:0;right:0;top:96px;height:120px;
  border-radius:12px 12px 0 0;background:var(--lime);padding:13px 12px 0;box-sizing:border-box;
  display:flex;flex-direction:column;gap:8px;z-index:2;
  animation:aExDcs 4.4s cubic-bezier(.3,.8,.3,1) infinite}
.ex-drag-constraints .grab{position:absolute;top:6px;left:50%;margin-left:-13px;display:block;
  width:26px;height:3px;border-radius:2px;background:rgba(var(--on-accent-rgb),.45)}
.ex-drag-constraints .ln{display:block;height:7px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.5)}
.ex-drag-constraints .ln.s{width:54%}
.ex-drag-constraints .note{position:absolute;left:12px;top:6px;z-index:4;font:600 6px/1 var(--mono);
  color:var(--ink-3);font-style:normal;letter-spacing:0;text-transform:none}
.ex-drag-constraints .cur{left:79px;top:106px;z-index:4;
  animation:aExDcsCur 4.4s cubic-bezier(.3,.8,.3,1) infinite}
/* borne a 70 de course, depassement a 82, retour elastique a 70 */
@keyframes aExDcs{0%,10%{transform:translateY(0)}
  46%{transform:translateY(-70px)}
  58%{transform:translateY(-82px)}
  72%,88%{transform:translateY(-70px)}
  100%{transform:translateY(0)}}
@keyframes aExDcsCur{0%{transform:translateY(0);opacity:0}
  8%{transform:translateY(0);opacity:1}
  46%{transform:translateY(-70px);opacity:1}
  58%{transform:translateY(-82px);opacity:1}
  62%{transform:translateY(-82px);opacity:0}
  99%,100%{transform:translateY(0);opacity:0}}

/* ---------------- grid-stagger ----------------
   Une grille de photos, et l origine est la case TOUCHEE. Le pointeur se
   pose sur la septieme, elle part la premiere, et le retard des autres
   suit leur distance a elle.
   C est le cas ou la consigne dit un index specifique, et c est aussi
   celui qu aucune cascade lineaire ne sait faire, puisque l origine
   change a chaque interaction.
   Quatre colonnes de 34 avec 6 de gouttiere font 154, la largeur exacte
   de l interieur, et quatre rangees de 28 en font 130 pour 132
   disponibles. Les retards valent 70ms par pas de distance.
   Gele, on verrait une grille pleine, sans savoir d ou elle est partie. */
.ex-grid-stagger{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:9px;box-sizing:border-box}
.ex-grid-stagger .gd{display:grid;grid-template-columns:repeat(4,34px);grid-auto-rows:28px;gap:6px}
.ex-grid-stagger .gd i{display:block;border-radius:6px;
  background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExGst 3.4s ease-out infinite}
/* origine sur la septieme case, retard de 70ms par pas de distance */
.ex-grid-stagger .gd i:nth-child(7){animation-delay:0s}
.ex-grid-stagger .gd i:nth-child(3),.ex-grid-stagger .gd i:nth-child(6),
.ex-grid-stagger .gd i:nth-child(8),.ex-grid-stagger .gd i:nth-child(11){animation-delay:.07s}
.ex-grid-stagger .gd i:nth-child(2),.ex-grid-stagger .gd i:nth-child(4),
.ex-grid-stagger .gd i:nth-child(5),.ex-grid-stagger .gd i:nth-child(10),
.ex-grid-stagger .gd i:nth-child(12),.ex-grid-stagger .gd i:nth-child(15){animation-delay:.14s}
.ex-grid-stagger .gd i:nth-child(1),.ex-grid-stagger .gd i:nth-child(9),
.ex-grid-stagger .gd i:nth-child(14),.ex-grid-stagger .gd i:nth-child(16){animation-delay:.21s}
.ex-grid-stagger .gd i:nth-child(13){animation-delay:.28s}
.ex-grid-stagger .cur{left:99px;top:50px;z-index:3}
.ex-grid-stagger .note{position:absolute;left:9px;bottom:3px;font:600 6px/1 var(--mono);
  color:var(--ink-3);font-style:normal;letter-spacing:0;text-transform:none}
@keyframes aExGst{0%,8%{transform:scale(.4);opacity:.15}
  32%,72%{transform:scale(1);opacity:1}
  94%,100%{transform:scale(.4);opacity:.15}}

/* ---------------- overwrite ----------------
   Un item de nav survole quatre fois en une seconde. En haut, auto, chaque
   nouveau survol tue le precedent sur la propriete en conflit et le
   soulignement repart proprement de la ou il etait.
   En bas, aucune, les quatre animations restent vivantes et ecrivent la
   meme largeur, le soulignement BAT et sa longueur finale depend de celle
   qui a tourne en dernier.
   C est le cas de tous les jours et il ne se voit qu en repetant vite.
   Le soulignement va de 0 a 62.
   Gele, les deux soulignements seraient a une longueur quelconque. */
.ex-overwrite{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:20px;
  justify-content:center}
.ex-overwrite .one{display:flex;align-items:center;gap:10px}
.ex-overwrite em{font:600 7px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  width:28px;text-align:right}
.ex-overwrite .ok em{color:var(--lime)}
.ex-overwrite .ko em{color:var(--a4)}
.ex-overwrite .nv{position:relative;display:block;width:62px;height:20px;border-radius:6px;
  background:var(--s2)}
.ex-overwrite .und{position:absolute;left:0;bottom:-5px;display:block;width:0;height:3px;
  border-radius:2px;background:var(--lime)}
/* quatre survols, chacun tue le precedent, la largeur monte proprement */
.ex-overwrite .ok .und{animation:aExOvwAuto 3.8s cubic-bezier(.3,.9,.3,1) infinite}
/* quatre animations vivantes, la largeur bat */
.ex-overwrite .ko .und{animation:aExOvwNone 3.8s linear infinite}
.ex-overwrite .cur{left:44px;top:64px;animation:aExOvwCur 3.8s steps(1,end) infinite}
@keyframes aExOvwCur{0%,8%{transform:translateY(0);opacity:0}
  10%,74%{transform:translateY(0);opacity:1}
  84%,100%{transform:translateY(0);opacity:0}}
@keyframes aExOvwAuto{0%,10%{width:0}
  24%{width:22px}38%{width:36px}52%{width:50px}66%,82%{width:62px}
  96%,100%{width:0}}
@keyframes aExOvwNone{0%,10%{width:0}
  20%{width:22px}26%{width:8px}32%{width:36px}38%{width:14px}
  44%{width:50px}50%{width:20px}56%{width:62px}62%{width:26px}
  68%,82%{width:44px}96%,100%{width:0}}

/* ---------------- damped-lerp ----------------
   Un curseur doux qui suit le pointeur. Le trait pointille est le seuil,
   pose un demi pixel avant la cible, et l anneau CALE ce dernier bout
   d un coup a 76 pour cent, parce que la distance restante n atteint
   jamais zero.
   Il ne depasse jamais, ce qui est la propriete qui le separe d un
   ressort. La demi vie de 90ms est le nombre que l on regle, pas une
   constante de lissage.
   Le pointeur va de 24 et 106 a 130 et 40, l anneau le suit avec retard.
   Gele, on verrait un anneau pres d un pointeur, sans la trainee ni le
   calage. */
.ex-damped-lerp{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0)}
.ex-damped-lerp .page{position:absolute;left:12px;right:12px;top:12px;display:flex;
  flex-direction:column;gap:9px}
.ex-damped-lerp .page i{display:block;height:7px;border-radius:3px;background:var(--s2)}
.ex-damped-lerp .page i:last-child{width:52%}
.ex-damped-lerp .ring{position:absolute;left:14px;top:96px;display:block;width:26px;height:26px;
  border-radius:50%;box-sizing:border-box;border:2px solid var(--lime);z-index:2;
  animation:aExDlpRing 4.4s linear infinite}
/* le seuil, un demi pixel avant la cible */
.ex-damped-lerp .thr{position:absolute;left:126px;top:34px;display:block;width:1px;height:26px;
  background:rgba(var(--accent-rgb),.45);z-index:3}
.ex-damped-lerp .cur{left:20px;top:102px;z-index:4;animation:aExDlpCur 4.4s cubic-bezier(.4,0,.4,1) infinite}
.ex-damped-lerp .note{position:absolute;left:12px;bottom:8px;font:600 6px/1 var(--mono);
  color:var(--ink-3);font-style:normal;letter-spacing:0;text-transform:none}
@keyframes aExDlpCur{0%,10%{transform:translate(0,0)}
  40%,84%{transform:translate(110px,-62px)}
  98%,100%{transform:translate(0,0)}}
/* approche exponentielle, la trainee se tasse puis cale a 76 */
@keyframes aExDlpRing{0%,12%{transform:translate(0,0)}
  34%{transform:translate(62px,-35px)}
  50%{transform:translate(90px,-51px)}
  62%{transform:translate(102px,-57px)}
  74%{transform:translate(108px,-60px)}
  76%,84%{transform:translate(110px,-62px)}
  98%,100%{transform:translate(0,0)}}

/* ---------------- spring-conversion ----------------
   UN mouvement, DEUX panneaux. La carte se deplace une seule fois, et les
   deux panneaux la decrivent en meme temps, l un en duree et rebond,
   l autre en raideur, amortissement et masse. Les quatre jauges bougent
   ensemble parce qu elles lisent le meme systeme.
   C est ce que la version precedente ratait, elle donnait deux courbes
   differentes a deux pistes, ce qui montre deux ressorts et non deux
   vocabulaires.
   La carte va de 12 a 96, la course vaut donc 84.
   Gele, on verrait une carte et quatre jauges, ce qui est presque tout,
   mais pas qu elles decrivent la meme chose. */
.ex-spring-conversion{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-spring-conversion .stz{position:relative;display:block;height:44px;border-radius:8px;
  background:var(--s1);flex:0 0 auto}
.ex-spring-conversion .stz .cd{position:absolute;left:6px;top:6px;display:block;width:44px;
  height:32px;border-radius:7px;background:linear-gradient(135deg,var(--lime),var(--accent-2));
  animation:aExSpcCard 3.8s cubic-bezier(.15,1.6,.35,1) infinite}
.ex-spring-conversion .panels{display:flex;gap:10px;flex:1}
.ex-spring-conversion .pn{display:flex;flex-direction:column;gap:5px;flex:1;padding:8px;
  box-sizing:border-box;border-radius:8px;background:var(--s1)}
.ex-spring-conversion em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;color:var(--ink-3)}
.ex-spring-conversion .pn b{display:block;height:5px;border-radius:3px;background:var(--s3);
  transform-origin:left}
/* les quatre jauges lisent le meme systeme et bougent donc ensemble */
.ex-spring-conversion .v1,.ex-spring-conversion .v2,
.ex-spring-conversion .v3,.ex-spring-conversion .v4{background:var(--lime);
  animation:aExSpcGauge 3.8s cubic-bezier(.15,1.6,.35,1) infinite}
.ex-spring-conversion .note{position:absolute;right:12px;top:4px;font:600 6px/1 var(--mono);
  color:var(--lime);font-style:normal;letter-spacing:0;text-transform:none}
/* la carte va de 6 a 90, course de 84 */
@keyframes aExSpcCard{0%,12%{transform:translateX(0)}62%,88%{transform:translateX(84px)}
  100%{transform:translateX(0)}}
@keyframes aExSpcGauge{0%,12%{transform:scaleX(.15)}62%,88%{transform:scaleX(1)}
  100%{transform:scaleX(.15)}}

/* ---------------- state-machine ----------------
   Un bouton d envoi, et le graphe a cote. Le jeton passe de idle a busy
   puis a done, l arete active s allume, et le bouton change en meme temps
   parce qu il EST l etat courant.
   Ce que le code fait, c est rapporter que l envoi a commence. Ce qui
   decide que busy mene a done, c est le graphe, et cela peut etre
   redessine sans toucher au code.
   Gele, on verrait un bouton et trois noeuds, sans les regles ni le lien
   entre les deux. */
.ex-state-machine{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:14px}
.ex-state-machine .btn{position:relative;display:grid;place-items:center;height:32px;border-radius:9px;
  background:var(--lime);flex:0 0 auto;animation:aExStmBtn 4.6s steps(1,end) infinite}
.ex-state-machine .btn b{position:absolute;display:block}
.ex-state-machine .lb{width:40px;height:6px;border-radius:3px;
  background:rgba(var(--on-accent-rgb),.75);animation:aExStmLb 4.6s steps(1,end) infinite}
.ex-state-machine .sp{width:14px;height:14px;border-radius:50%;box-sizing:border-box;
  border:2px solid rgba(var(--on-accent-rgb),.35);border-top-color:var(--on-accent);opacity:0;
  animation:aExStmSp 4.6s linear infinite}
.ex-state-machine .ck{width:12px;height:7px;box-sizing:border-box;
  border-left:2px solid var(--on-accent);border-bottom:2px solid var(--on-accent);
  transform:rotate(-45deg);opacity:0;animation:aExStmCk 4.6s ease infinite}
.ex-state-machine .graph{position:relative;flex:1;min-height:0}
.ex-state-machine .node{position:absolute;display:grid;place-items:center;width:40px;height:28px;
  border-radius:8px;box-sizing:border-box;border:1.5px solid var(--s5);background:var(--s1)}
.ex-state-machine .node em{font:600 6px/1 var(--mono);color:var(--ink-3);font-style:normal;
  letter-spacing:0;text-transform:none}
.ex-state-machine .n1{left:0;top:32px}
.ex-state-machine .n2{left:54px;top:0}
.ex-state-machine .n3{right:0;top:40px}
.ex-state-machine .ed{position:absolute;display:block;height:1.5px;border-radius:2px;
  background:var(--s3)}
.ex-state-machine .e1{left:38px;top:28px;width:24px;transform:rotate(-26deg);transform-origin:left;
  animation:aExStmE1 4.6s steps(1,end) infinite}
.ex-state-machine .e2{left:92px;top:22px;width:30px;transform:rotate(32deg);transform-origin:left;
  animation:aExStmE2 4.6s steps(1,end) infinite}
.ex-state-machine .e3{left:38px;top:50px;width:70px;transform:rotate(3deg);transform-origin:left;
  animation:aExStmE3 4.6s steps(1,end) infinite}
.ex-state-machine .tok{position:absolute;left:12px;top:38px;display:block;width:14px;height:14px;
  border-radius:4px;background:var(--lime);animation:aExStmTok 4.6s cubic-bezier(.3,.9,.3,1) infinite}
@keyframes aExStmTok{0%,12%{transform:translate(0,0)}
  34%,48%{transform:translate(54px,-32px)}
  70%,84%{transform:translate(112px,8px)}
  98%,100%{transform:translate(0,0)}}
@keyframes aExStmBtn{0%,33%{background:var(--lime)}34%,69%{background:var(--a2)}
  70%,97%{background:var(--lime)}98%,100%{background:var(--lime)}}
@keyframes aExStmLb{0%,33%{opacity:1}34%,97%{opacity:0}98%,100%{opacity:1}}
@keyframes aExStmSp{0%,33%{opacity:0;transform:rotate(0)}
  34%{opacity:1;transform:rotate(0)}
  68%{opacity:1;transform:rotate(720deg)}
  69%,100%{opacity:0;transform:rotate(0)}}
@keyframes aExStmCk{0%,69%{opacity:0}74%,97%{opacity:1}98%,100%{opacity:0}}
@keyframes aExStmE1{0%,13%{background:var(--s3)}14%,33%{background:var(--lime)}
  34%,100%{background:var(--s3)}}
@keyframes aExStmE2{0%,48%{background:var(--s3)}49%,69%{background:var(--lime)}
  70%,100%{background:var(--s3)}}
@keyframes aExStmE3{0%,84%{background:var(--s3)}85%,97%{background:var(--lime)}
  98%,100%{background:var(--s3)}}

/* ---------------- state-machine-input ----------------
   Trois lignes de reglages, une seule icone, et TROIS comportements. Le
   booleen tient, l arc reste allume tant que l interrupteur est mis. Le
   nombre porte un niveau, l arc du milieu suit le curseur en continu. Le
   declencheur se produit une fois, l arc du bas pulse et retombe alors
   que le bouton est encore enfonce.
   Cette derniere ligne est la plus importante, un declencheur est
   consomme. La definition nomme trois types, la scene en montre trois.
   Gele, on verrait une icone et trois reglages, sans savoir lequel tient
   et lequel s oublie. */
/* interieur de 126, l icone fait 40, les trois lignes 74 avec leurs deux
   gouttieres de 11, et la gouttiere du milieu 12, soit 126 pile */
.ex-state-machine-input{position:relative;width:172px;height:150px;border-radius:11px;overflow:hidden;
  background:var(--s0);padding:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:12px}
.ex-state-machine-input .ico{position:relative;display:block;height:40px;border-radius:9px;
  background:var(--s1);flex:0 0 auto}
.ex-state-machine-input .ico i{position:absolute;left:50%;display:block;border-radius:2px;
  background:var(--s3)}
.ex-state-machine-input .a1{top:8px;width:52px;height:5px;margin-left:-26px;
  animation:aExSmiA1 4.4s steps(1,end) infinite}
.ex-state-machine-input .a2{top:18px;width:36px;height:5px;margin-left:-18px;transform-origin:center;
  animation:aExSmiA2 4.4s cubic-bezier(.4,0,.4,1) infinite}
.ex-state-machine-input .a3{top:28px;width:20px;height:5px;margin-left:-10px;
  animation:aExSmiA3 4.4s cubic-bezier(.2,.9,.3,1) infinite}
.ex-state-machine-input .rows{display:flex;flex-direction:column;gap:11px}
.ex-state-machine-input .rw{display:flex;align-items:center;gap:9px}
.ex-state-machine-input 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}
.ex-state-machine-input .sw{position:relative;display:block;width:34px;height:18px;border-radius:10px;
  background:var(--s2);flex:0 0 auto;animation:aExSmiSwBg 4.4s steps(1,end) infinite}
.ex-state-machine-input .sw i{position:absolute;left:3px;top:3px;display:block;width:12px;height:12px;
  border-radius:6px;background:var(--ink);animation:aExSmiSw 4.4s cubic-bezier(.2,1.2,.3,1) infinite}
.ex-state-machine-input .sl{position:relative;display:block;width:44px;height:5px;border-radius:3px;
  background:var(--s2);flex:0 0 auto}
.ex-state-machine-input .sl i{position:absolute;left:0;top:-4px;display:block;width:12px;height:12px;
  border-radius:6px;background:var(--ink);animation:aExSmiSl 4.4s cubic-bezier(.4,0,.4,1) infinite}
.ex-state-machine-input .bt{position:relative;display:block;width:34px;height:18px;border-radius:5px;
  background:var(--s2);flex:0 0 auto;animation:aExSmiBtBg 4.4s steps(1,end) infinite}
.ex-state-machine-input .bt i{position:absolute;left:11px;top:6px;display:block;width:12px;height:6px;
  border-radius:3px;background:var(--ink)}
/* il tient tant que l interrupteur est mis */
@keyframes aExSmiSw{0%,14%{transform:translateX(0)}24%,70%{transform:translateX(16px)}
  80%,100%{transform:translateX(0)}}
@keyframes aExSmiSwBg{0%,18%{background:var(--s2)}19%,75%{background:var(--lime)}
  76%,100%{background:var(--s2)}}
@keyframes aExSmiA1{0%,18%{background:var(--s3)}19%,75%{background:var(--lime)}
  76%,100%{background:var(--s3)}}
/* il suit le niveau en continu, course de 32 */
@keyframes aExSmiSl{0%,10%{transform:translateX(0)}50%{transform:translateX(32px)}
  90%,100%{transform:translateX(0)}}
@keyframes aExSmiA2{0%,10%{transform:scaleX(.35);background:var(--s3)}
  50%{transform:scaleX(1);background:var(--lime)}
  90%,100%{transform:scaleX(.35);background:var(--s3)}}
/* il est consomme, il pulse une fois alors que le bouton reste enfonce */
@keyframes aExSmiBtBg{0%,38%{background:var(--s2)}39%,72%{background:var(--s4)}
  73%,100%{background:var(--s2)}}
@keyframes aExSmiA3{0%,39%{background:var(--s3);transform:scaleY(1)}
  43%{background:var(--a2);transform:scaleY(1.8)}
  56%,100%{background:var(--s3);transform:scaleY(1)}}

/* blend-state, l avatar. UN seul nombre pilote TROIS choses a la fois, la
   bouche, le sourcil gauche et le sourcil droit. C est ce qu un melange
   achete par rapport a des etats separes, il n y a rien a synchroniser.
   Les deux poses extremes restent tracees en pointille, la bouche pleine se
   tient toujours entre elles, a la position que dit la jauge.
   Les deux traces ont la meme structure de commandes, M puis C, sinon d
   n interpolerait pas du tout.
   La piste fait 70 et le point 9, la course vaut donc 61.
   Gele, on verrait un visage et un nombre sans lien entre eux. */
.ex-blend-state{display:flex;flex-direction:column;align-items:center;gap:10px}
.ex-blend-state .face{position:relative;display:block;width:130px;height:56px}
.ex-blend-state svg{display:block;width:130px;height:56px}
.ex-blend-state .gh{fill:none;stroke:var(--s4);stroke-width:2;stroke-dasharray:3 4}
.ex-blend-state .mouth{fill:none;stroke:var(--lime);stroke-width:4;
  animation:aExBlendM 4s ease-in-out infinite}
.ex-blend-state .eye{position:absolute;top:17px;display:block;width:9px;height:9px;border-radius:5px;
  background:var(--lime)}
.ex-blend-state .eye.l{left:40px}
.ex-blend-state .eye.r{right:40px}
.ex-blend-state .brow{position:absolute;top:7px;display:block;width:15px;height:2.5px;border-radius:2px;
  background:var(--lime)}
.ex-blend-state .brow.bl{left:36px;transform-origin:100% 50%;animation:aExBlendBl 4s ease-in-out infinite}
.ex-blend-state .brow.br{right:36px;transform-origin:0 50%;animation:aExBlendBr 4s ease-in-out infinite}
.ex-blend-state .score{display:flex;align-items:center;gap:7px}
.ex-blend-state .score em{font:600 7px/1 var(--mono);color:var(--ink-3);font-style:normal;
  letter-spacing:0;text-transform:none}
.ex-blend-state .tk{position:relative;display:block;width:70px;height:6px;border-radius:3px;
  background:var(--s2)}
.ex-blend-state .tk b{position:absolute;left:0;top:-1.5px;display:block;width:9px;height:9px;
  border-radius:5px;background:var(--a2);animation:aExBlendW 4s ease-in-out infinite}
/* la premiere pose, bouche tombante, et la seconde, bouche remontee */
@keyframes aExBlendM{0%,100%{d:path("M26 44C46 24 84 24 104 44")}
  50%{d:path("M26 26C46 46 84 46 104 26")}}
@keyframes aExBlendBl{0%,100%{transform:rotate(-14deg)}50%{transform:rotate(6deg)}}
@keyframes aExBlendBr{0%,100%{transform:rotate(14deg)}50%{transform:rotate(-6deg)}}
@keyframes aExBlendW{0%,100%{transform:translateX(0)}50%{transform:translateX(61px)}}

/* marker, le clip de succes. Le RE-EXPORT tombe a 52 pour cent, il insere
   deux images au debut. En haut l appel se fait par NOM, la selection suit
   le decalage, elle reste sur les deux cases claires, et la pastille se
   remplit jusqu en haut. En bas l appel est code en numeros, la selection
   ne bouge pas, elle designe maintenant deux cases grises, et la pastille
   s arrete a 8 sur 20. Le succes n est jamais atteint.
   Les cases font 11 avec 3 de marge, le pas vaut donc 14. Deux images
   inserees valent 28 de decalage, et la selection couvre deux cases plus
   leur marge, soit 25. Le contenu clair est a 28 avant, a 56 apres.
   La piste fait 7 fois 14 moins 3, soit 95, et 123 apres insertion, ce qui
   laisse 123 plus 8 plus 20, soit 151 de large au total.
   Gele, les deux rangees seraient identiques. */
.ex-marker{display:flex;flex-direction:column;gap:14px;justify-content:center}
/* le libelle passe AU DESSUS de la piste, sans quoi la rangee ferait 225 de
   large. La pastille couvre les deux rangees. */
.ex-marker .one{display:grid;grid-template-columns:1fr auto;gap:4px 8px;align-items:center}
.ex-marker em{font:600 6px/1 var(--mono);font-style:normal;letter-spacing:0;text-transform:none;
  grid-column:1;grid-row:1}
.ex-marker .tl{grid-column:1;grid-row:2}
.ex-marker .bg{grid-column:2;grid-row:1/-1}
.ex-marker .ok em{color:var(--lime)}
.ex-marker .ko em{color:var(--a4)}
.ex-marker .tl{position:relative;display:flex}
.ex-marker .f{display:block;width:11px;height:22px;margin-right:3px;border-radius:3px;box-sizing:border-box;
  border:1px solid var(--line);flex:0 0 auto}
.ex-marker .g{background:var(--s1)}
.ex-marker .l{background:var(--lime);border-color:transparent}
/* les deux images inserees, largeur ET marge partent de zero */
.ex-marker .n2{background:var(--s2);width:0;margin-right:0;overflow:hidden;
  animation:aExMkrNew 5s steps(1,end) infinite}
.ex-marker .sel{position:absolute;left:0;top:-3px;display:block;width:25px;height:28px;border-radius:5px;
  box-sizing:border-box;border:1.5px solid var(--a2)}
.ex-marker .ok .sel{animation:aExMkrName 5s steps(1,end) infinite}
/* codee en dur, elle ne bouge jamais, aucune animation */
.ex-marker .ko .sel{transform:translateX(28px)}
.ex-marker .bg{position:relative;display:block;width:20px;height:20px;border-radius:10px;
  background:var(--s2);overflow:hidden;flex:0 0 auto}
.ex-marker .ck{position:absolute;left:0;right:0;bottom:0;display:block;height:0;background:var(--lime)}
.ex-marker .ok .ck{animation:aExMkrFull 5s ease-out infinite}
.ex-marker .ko .ck{animation:aExMkrPart 5s ease-out infinite}
@keyframes aExMkrNew{0%,51%{width:0;margin-right:0}52%,94%{width:11px;margin-right:3px}
  96%,100%{width:0;margin-right:0}}
@keyframes aExMkrName{0%,51%{transform:translateX(28px)}52%,94%{transform:translateX(56px)}
  96%,100%{transform:translateX(28px)}}
/* il lit les bonnes images, le remplissage va jusqu en haut */
@keyframes aExMkrFull{0%,55%{height:0}80%,94%{height:20px}96%,100%{height:0}}
/* il lit deux images grises, le remplissage cale a 8 sur 20 */
@keyframes aExMkrPart{0%,55%{height:0}68%,94%{height:8px}96%,100%{height:0}}

/* token-streaming, la bulle de chat. Les traits du bas marquent les
   instants d arrivee reels, 8 14 30 34 38 56 68 et 80 pour cent. Ils ne
   sont PAS regulierement espaces, et c est tout le sujet, la cadence
   appartient au reseau et non a une courbe d animation.
   Le bloc de code, lui, n apparait pas mot a mot. Il attend sa cloture et
   sort d un seul coup a 80. C est ce que fait un rendu en flux qui doit
   accumuler avant de savoir comment afficher.
   La piste fait 126 utiles, les traits sont donc a 10 18 38 43 48 71 86
   et 101, soit le pourcentage fois 1,26.
   Gele, la reponse serait juste ecrite, sans son rythme ni son attente. */
.ex-token-streaming{display:grid;grid-template-columns:auto auto;gap:7px 8px;align-items:start;
  justify-content:center}
.ex-token-streaming .av{grid-column:1;grid-row:1}
.ex-token-streaming .bub{grid-column:2;grid-row:1}
.ex-token-streaming .trace{grid-column:2;grid-row:2}
.ex-token-streaming .av{display:block;width:16px;height:16px;border-radius:8px;background:var(--s3);
  flex:0 0 auto}
.ex-token-streaming .bub{display:flex;flex-direction:column;align-items:flex-start;gap:5px;
  padding:8px;border-radius:8px;background:var(--s1);width:130px;box-sizing:border-box}
.ex-token-streaming .bub b{display:block;height:6px;border-radius:3px;background:var(--ink-3);width:0}
.ex-token-streaming .l1{animation:aExTksL1 4.4s steps(1,end) infinite}
.ex-token-streaming .l2{animation:aExTksL2 4.4s steps(1,end) infinite}
/* le bloc de code, hauteur nulle avant sa cloture */
.ex-token-streaming .fence{display:block;width:100%;height:0;overflow:hidden;border-radius:5px;
  background:var(--s3);opacity:0;box-sizing:border-box;
  animation:aExTksF 4.4s steps(1,end) infinite}
.ex-token-streaming .fence i{display:block;height:5px;border-radius:3px;background:var(--lime);
  margin:5px 0 0 5px}
.ex-token-streaming .fence i:first-child{width:62px}
.ex-token-streaming .fence i:last-child{width:38px}
.ex-token-streaming .trace{position:relative;display:block;width:126px;height:8px;
  border-top:1px solid var(--line)}
.ex-token-streaming .trace i{position:absolute;top:2px;display:block;width:2px;height:6px;
  border-radius:1px;background:var(--a2);opacity:0}
.ex-token-streaming .k1{left:10px;animation:aExTksK1 4.4s steps(1,end) infinite}
.ex-token-streaming .k2{left:18px;animation:aExTksK2 4.4s steps(1,end) infinite}
.ex-token-streaming .k3{left:38px;animation:aExTksK3 4.4s steps(1,end) infinite}
.ex-token-streaming .k4{left:43px;animation:aExTksK4 4.4s steps(1,end) infinite}
.ex-token-streaming .k5{left:48px;animation:aExTksK5 4.4s steps(1,end) infinite}
.ex-token-streaming .k6{left:71px;animation:aExTksK6 4.4s steps(1,end) infinite}
.ex-token-streaming .k7{left:86px;animation:aExTksK7 4.4s steps(1,end) infinite}
.ex-token-streaming .k8{left:101px;animation:aExTksK8 4.4s steps(1,end) infinite}
@keyframes aExTksL1{0%,7%{width:0}8%,13%{width:12px}14%,29%{width:22px}30%,33%{width:58px}
  34%,37%{width:66px}38%,95%{width:74px}97%,100%{width:0}}
@keyframes aExTksL2{0%,55%{width:0}56%,67%{width:30px}68%,95%{width:44px}97%,100%{width:0}}
@keyframes aExTksF{0%,79%{height:0;opacity:0}80%,95%{height:22px;opacity:1}
  97%,100%{height:0;opacity:0}}
@keyframes aExTksK1{0%,7%{opacity:0}8%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK2{0%,13%{opacity:0}14%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK3{0%,29%{opacity:0}30%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK4{0%,33%{opacity:0}34%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK5{0%,37%{opacity:0}38%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK6{0%,55%{opacity:0}56%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK7{0%,67%{opacity:0}68%,95%{opacity:1}97%,100%{opacity:0}}
@keyframes aExTksK8{0%,79%{opacity:0}80%,95%{opacity:1}97%,100%{opacity:0}}

/* smooth-streaming, le meme reglage active puis coupe. En haut sans tampon,
   la reponse avance par sauts, aux instants ou le reseau livre. En bas avec
   tampon, elle avance a vitesse constante.
   La preuve est en bas a droite. La version brute a fini a 80 pour cent, la
   version lissee a 92. Le lissage COUTE 12 points de latence, il n est pas
   gratuit, et c est exactement ce que la jauge de tampon mesure.
   Le contenu vaut 74 plus 44, soit 118. La ligne lissee ecrit en continu de
   8 a 92, donc 84 points pour 118, et la premiere ligne finit a 8 plus 84
   fois 74 sur 118, soit 61.
   Le retard vaut brut moins lisse, il monte a 37 sur 118 vers 56 pour cent,
   soit 0,31 de la piste de 100, puis il se vide.
   Gele, les deux bulles auraient la meme longueur et il n y aurait rien. */
.ex-smooth-streaming{display:flex;flex-direction:column;gap:8px;justify-content:center}
.ex-smooth-streaming .one{display:flex;align-items:center;gap:8px}
.ex-smooth-streaming em{font:600 6px/1 var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;width:26px;flex:0 0 auto}
.ex-smooth-streaming .ko em{color:var(--a4)}
.ex-smooth-streaming .ok em{color:var(--lime)}
.ex-smooth-streaming .lane em{color:var(--ink-3)}
.ex-smooth-streaming .bub{display:flex;flex-direction:column;gap:4px;padding:6px;border-radius:7px;
  background:var(--s1);width:90px;box-sizing:border-box}
.ex-smooth-streaming .bub b{display:block;height:6px;border-radius:3px;background:var(--ink-3);width:0}
/* elle avance par sauts, aux instants du reseau */
.ex-smooth-streaming .r1{animation:aExSmsR1 4.4s steps(1,end) infinite}
.ex-smooth-streaming .r2{animation:aExSmsR2 4.4s steps(1,end) infinite}
/* elle avance a vitesse constante, et elle finit plus tard */
.ex-smooth-streaming .s1{animation:aExSmsS1 4.4s linear infinite}
.ex-smooth-streaming .s2{animation:aExSmsS2 4.4s linear infinite}
.ex-smooth-streaming .lane{display:flex;align-items:center;gap:8px}
.ex-smooth-streaming .tk{display:block;width:100px;height:5px;border-radius:3px;background:var(--s2);
  overflow:hidden}
.ex-smooth-streaming .buf{display:block;width:100%;height:5px;border-radius:3px;background:var(--a2);
  transform:scaleX(0);transform-origin:0 50%;animation:aExSmsBuf 4.4s linear infinite}
@keyframes aExSmsR1{0%,7%{width:0}8%,13%{width:12px}14%,29%{width:22px}30%,33%{width:58px}
  34%,37%{width:66px}38%,95%{width:74px}97%,100%{width:0}}
@keyframes aExSmsR2{0%,55%{width:0}56%,67%{width:30px}68%,95%{width:44px}97%,100%{width:0}}
@keyframes aExSmsS1{0%,8%{width:0}61%,95%{width:74px}97%,100%{width:0}}
@keyframes aExSmsS2{0%,61%{width:0}92%,95%{width:44px}97%,100%{width:0}}
/* le retard accumule, il monte a 0,31 puis il se vide a 92 */
@keyframes aExSmsBuf{0%{transform:scaleX(0)}8%{transform:scaleX(.10)}14%{transform:scaleX(.12)}
  30%{transform:scaleX(.23)}38%{transform:scaleX(.27)}56%{transform:scaleX(.31)}
  68%{transform:scaleX(.29)}80%{transform:scaleX(.14)}92%,100%{transform:scaleX(0)}}

/* generation-caret, le curseur d une reponse en cours. Il n a AUCUNE
   animation de position. Il est place apres la barre de texte dans la meme
   rangee flex, c est donc la longueur du texte qui le pousse, et quand la
   phrase change de ligne il change de ligne avec elle. C est le seul
   montage qui garantit qu il reste collé au dernier caractere.
   A 80 pour cent la generation se termine et le curseur est RETIRE, pas
   fondu. visibility passe a hidden en un pas, sans transition, parce qu un
   curseur qui s efface doucement se lit comme un texte encore en train
   d arriver.
   Les lignes montent a 70, 62 et 34, la bulle en fait 86 utiles.
   Gele, on ne saurait pas si la reponse est finie. */
.ex-generation-caret{display:flex;align-items:flex-start;gap:8px}
.ex-generation-caret .av{display:block;width:16px;height:16px;border-radius:8px;background:var(--s3);
  flex:0 0 auto}
.ex-generation-caret .bub{display:flex;flex-direction:column;gap:6px;padding:9px;border-radius:8px;
  background:var(--s1);width:104px;box-sizing:border-box}
.ex-generation-caret .ln{display:flex;align-items:center;gap:2px;height:11px}
.ex-generation-caret .tx{display:block;height:6px;border-radius:3px;background:var(--ink-3);width:0}
.ex-generation-caret .l1 .tx{animation:aExGcrT1 5s steps(1,end) infinite}
.ex-generation-caret .l2 .tx{animation:aExGcrT2 5s steps(1,end) infinite}
.ex-generation-caret .l3 .tx{animation:aExGcrT3 5s steps(1,end) infinite}
.ex-generation-caret .car{display:block;width:2px;height:11px;border-radius:1px;background:var(--lime);
  flex:0 0 auto;visibility:hidden}
.ex-generation-caret .c1{animation:aExGcrBlink .9s steps(1,end) infinite,
  aExGcrC1 5s steps(1,end) infinite}
.ex-generation-caret .c2{animation:aExGcrBlink .9s steps(1,end) infinite,
  aExGcrC2 5s steps(1,end) infinite}
.ex-generation-caret .c3{animation:aExGcrBlink .9s steps(1,end) infinite,
  aExGcrC3 5s steps(1,end) infinite}
@keyframes aExGcrBlink{0%,49%{opacity:1}50%,100%{opacity:0}}
@keyframes aExGcrT1{0%,3%{width:0}8%{width:18px}14%{width:38px}22%{width:56px}
  30%,95%{width:70px}97%,100%{width:0}}
@keyframes aExGcrT2{0%,30%{width:0}38%{width:22px}48%{width:44px}58%,95%{width:62px}
  97%,100%{width:0}}
@keyframes aExGcrT3{0%,58%{width:0}66%{width:16px}74%,95%{width:34px}97%,100%{width:0}}
@keyframes aExGcrC1{0%,30%{visibility:visible}31%,96%{visibility:hidden}
  97%,100%{visibility:visible}}
@keyframes aExGcrC2{0%,30%{visibility:hidden}31%,58%{visibility:visible}
  59%,100%{visibility:hidden}}
/* retire net a 80, la reponse est finie */
@keyframes aExGcrC3{0%,58%{visibility:hidden}59%,79%{visibility:visible}
  80%,100%{visibility:hidden}}

/* thinking-shimmer, l attente d une reponse. Le gabarit a la FORME de ce
   qui va arriver, deux lignes aux largeurs exactes de la reponse, 82 et 54.
   A 62 pour cent le vrai contenu prend leur place, et il tombe pile dessus,
   rien ne bouge. C est ce qu une roue ne peut pas faire, elle occupe un
   espace qui n a aucun rapport avec la reponse et tout saute a l arrivee.
   Une troisieme ligne s ajoute ensuite, plus courte, parce qu une attente
   honnete promet une forme et non un nombre de mots exact.
   Le reflet fait un aller retour, 1,9s par passage. Sur trois rangees de 7
   et deux ecarts de 6, la boite fait 33 de haut.
   Gele, on ne saurait pas si le systeme travaille ou s il est bloque. */
.ex-thinking-shimmer{display:flex;align-items:flex-start;gap:8px}
.ex-thinking-shimmer .av{display:block;width:16px;height:16px;border-radius:8px;background:var(--s3);
  flex:0 0 auto}
.ex-thinking-shimmer .bub{position:relative;display:block;width:100px;height:33px;padding:9px;
  border-radius:8px;background:var(--s1);box-sizing:border-box}
.ex-thinking-shimmer .ph{position:absolute;left:9px;top:9px;display:flex;flex-direction:column;gap:6px;
  overflow:hidden;animation:aExTshPh 4.6s steps(1,end) infinite}
.ex-thinking-shimmer .ph .w{display:block;height:7px;border-radius:4px;background:var(--s3)}
.ex-thinking-shimmer .ph .w:first-child{width:82px}
.ex-thinking-shimmer .ph .w.s{width:54px}
.ex-thinking-shimmer .sheen{position:absolute;top:0;bottom:0;left:0;display:block;width:34px;
  background:linear-gradient(90deg,transparent,rgba(var(--ink-rgb),.20),transparent);
  animation:aExTshSw 3.8s linear infinite}
.ex-thinking-shimmer .real{position:absolute;left:9px;top:9px;display:flex;flex-direction:column;gap:6px;
  visibility:hidden;animation:aExTshRe 4.6s steps(1,end) infinite}
.ex-thinking-shimmer .real b{display:block;height:7px;border-radius:4px;background:var(--ink-3);
  width:82px}
.ex-thinking-shimmer .real b.s{width:54px}
.ex-thinking-shimmer .real b.t{width:40px;background:var(--lime)}
@keyframes aExTshPh{0%,61%{visibility:visible}62%,96%{visibility:hidden}
  97%,100%{visibility:visible}}
@keyframes aExTshRe{0%,61%{visibility:hidden}62%,96%{visibility:visible}
  97%,100%{visibility:hidden}}
/* aller retour, la boucle revient a son point de depart */
@keyframes aExTshSw{0%,100%{transform:translateX(-40px)}50%{transform:translateX(88px)}}

/* stop-generating, la barre de saisie. Le bouton stop prend la PLACE du
   bouton envoyer, il ne s ajoute pas a cote. C est ce qui evite qu on
   cherche ou couper pendant que ca defile.
   La pression a 44 pour cent est accusee tout de suite, l anneau part avant
   que le flux ne s arrete, et le texte deja produit RESTE. Il ne s efface
   pas, et un curseur d edition apparait au bout, parce qu une reponse
   coupee est un brouillon, pas un echec.
   La premiere ligne monte a 96, la seconde s arrete a 52 alors qu elle
   allait vers 96, c est le tiers manquant qui se voit.
   Gele, on ne saurait pas si le bouton coupe ou annule. */
.ex-stop-generating{display:flex;flex-direction:column;gap:12px;justify-content:center}
/* grille et non flex, parce que flex-basis 100 pour cent aurait ecrase la
   largeur animee de la premiere ligne */
.ex-stop-generating .msg{display:grid;grid-template-columns:auto 1fr;gap:5px;width:138px;
  align-items:center}
.ex-stop-generating .msg b{display:block;height:6px;border-radius:3px;background:var(--ink-3);width:0;
  justify-self:start}
.ex-stop-generating .t1{grid-column:1/-1;animation:aExStgT1 5s linear infinite}
.ex-stop-generating .t2{animation:aExStgT2 5s linear infinite}
/* le curseur d edition, il dit que le brouillon est repris en main */
.ex-stop-generating .edit{display:block;width:2px;height:11px;border-radius:1px;background:var(--a2);
  opacity:0;animation:aExStgEd 5s steps(1,end) infinite}
.ex-stop-generating .bar{display:flex;align-items:center;gap:8px;width:138px;padding:6px;
  border-radius:9px;background:var(--s1);box-sizing:border-box}
.ex-stop-generating .fld{display:block;height:6px;border-radius:3px;background:var(--s3);flex:1 1 auto}
/* une seule case, les deux boutons s y relaient */
.ex-stop-generating .ctl{position:relative;display:block;width:20px;height:20px;flex:0 0 auto}
.ex-stop-generating .send{position:absolute;left:1px;top:1px;display:block;width:18px;height:18px;
  background:var(--lime);clip-path:polygon(3px 2px,16px 9px,3px 16px);
  animation:aExStgSend 5s steps(1,end) infinite}
.ex-stop-generating .stop{position:absolute;left:5px;top:5px;display:block;width:10px;height:10px;
  border-radius:2px;background:var(--a4);opacity:0;animation:aExStgStop 5s steps(1,end) infinite}
.ex-stop-generating .wait{position:absolute;left:0;top:0;display:block;width:20px;height:20px;
  border-radius:10px;box-sizing:border-box;border:2px solid var(--a4);opacity:0;
  animation:aExStgAck 5s ease-out infinite}
@keyframes aExStgT1{0%,8%{width:0}30%,95%{width:96px}97%,100%{width:0}}
/* elle allait vers 96 et elle s arrete net a 52 */
@keyframes aExStgT2{0%,30%{width:0}44%,95%{width:52px}97%,100%{width:0}}
@keyframes aExStgEd{0%,49%{opacity:0}50%,95%{opacity:1}97%,100%{opacity:0}}
/* envoyer est la, puis il cede la case, puis il revient */
@keyframes aExStgSend{0%,7%{opacity:1}8%,45%{opacity:0}46%,100%{opacity:1}}
@keyframes aExStgStop{0%,7%{opacity:0}8%,44%{opacity:1}45%,100%{opacity:0}}
/* l accuse de reception part au moment de la pression, pas apres */
@keyframes aExStgAck{0%,43%{opacity:0;transform:scale(1)}
  44%{opacity:1;transform:scale(1)}
  56%{opacity:0;transform:scale(1.9)}
  57%,100%{opacity:0;transform:scale(1)}}

/* stick-to-bottom, un lecteur qui remonte pendant la reponse. La colonne
   est ancree sur le BAS de la fenetre, elle n a donc rien a recalculer
   quand une bulle s ajoute, le dernier message reste au meme endroit. Les
   bulles 4 et 5 arrivent a 14 et 44 pour cent.
   A 30 le doigt descend, l accrochage est RELACHE. A 44 la cinquieme bulle
   arrive quand meme, et elle tombe 30 en dessous du bord, invisible. C est
   la preuve, le contenu continue d arriver et la vue ne bouge plus, ce qui
   est exactement ce qu il faut, sinon on arracherait la page des yeux du
   lecteur. Le bouton de retour apparait pour cette raison.
   A 74 le doigt le touche, la colonne revient a zero et se raccroche.
   Une bulle fait 6 plus 4 plus 6, soit 16, avec 10 de marge le pas vaut 26.
   Cinq bulles font 120 pour une fenetre de 76.
   Gele, on ne verrait pas la difference entre accroche et relache. */
.ex-stick-to-bottom{position:relative;display:block;width:140px;height:76px}
.ex-stick-to-bottom .vp{position:absolute;left:0;top:0;right:0;bottom:0;display:block;
  overflow:hidden;border-radius:8px;background:var(--s1)}
.ex-stick-to-bottom .col{position:absolute;left:8px;right:8px;bottom:8px;display:block;
  animation:aExS2bCol 5.4s cubic-bezier(.3,0,.2,1) infinite}
.ex-stick-to-bottom .bb{display:block;margin-bottom:10px}
.ex-stick-to-bottom .bb:last-child{margin-bottom:0}
.ex-stick-to-bottom .bb i{display:block;height:6px;border-radius:3px;background:var(--ink-3);width:100%}
.ex-stick-to-bottom .bb i.s{width:58%;margin-top:4px;background:var(--s4)}
/* les deux dernieres bulles arrivent en cours de route */
.ex-stick-to-bottom .bb:nth-child(4){height:0;margin-bottom:0;overflow:hidden;
  animation:aExS2bB4 5.4s steps(1,end) infinite}
.ex-stick-to-bottom .bb:nth-child(5){height:0;margin-bottom:0;overflow:hidden;
  animation:aExS2bB5 5.4s steps(1,end) infinite}
.ex-stick-to-bottom .jump{position:absolute;left:46px;bottom:6px;display:block;width:44px;height:14px;
  border-radius:7px;background:var(--a2);opacity:0;animation:aExS2bJ 5.4s ease-out infinite}
.ex-stick-to-bottom .cur{left:112px;top:24px;animation:aExS2bCur 5.4s cubic-bezier(.3,0,.2,1) infinite}
/* zero veut dire accroche au bas. 30 veut dire remonte de 30. */
@keyframes aExS2bCol{0%,30%{transform:translateY(0)}
  36%,73%{transform:translateY(30px)}
  74%,100%{transform:translateY(0)}}
@keyframes aExS2bB4{0%,13%{height:0;margin-bottom:0}14%,94%{height:16px;margin-bottom:10px}
  96%,100%{height:0;margin-bottom:0}}
@keyframes aExS2bB5{0%,43%{height:0;margin-bottom:0}44%,94%{height:16px;margin-bottom:10px}
  96%,100%{height:0;margin-bottom:0}}
@keyframes aExS2bJ{0%,33%{opacity:0}34%,73%{opacity:1}74%,100%{opacity:0}}
/* le doigt descend pour remonter le fil, puis il va toucher le bouton */
@keyframes aExS2bCur{0%,24%{transform:translate(0,0)}
  36%,66%{transform:translate(4px,36px)}
  74%,88%{transform:translate(-51px,32px)}
  96%,100%{transform:translate(0,0)}}

/* ghost-text, l editeur. L exemple montre ce que la scene abstraite ne
   montrait pas, l acceptation MOT PAR MOT que la consigne autorise.
   La preuve des metriques identiques est une somme constante. Des
   l apparition la ligne vaut 30 plus 4 plus 19 plus 25 plus 18, soit 96. Au
   premier Tab le code passe a 49 et le premier mot tombe a zero avec sa
   marge, 49 plus 4 plus 0 plus 25 plus 18 font encore 96. Au second, 74
   plus 4 plus 0 plus 0 plus 18 font toujours 96. Rien ne se recompose,
   parce que le fantome occupait exactement la place du texte accepte.
   Le troisieme mot reste fantome, l utilisateur peut s arreter au milieu
   d une suggestion, ce que la consigne demande aussi.
   Gele, on ne verrait pas lequel des trois mots est deja dans le fichier. */
.ex-ghost-text{display:flex;flex-direction:column;gap:10px}
.ex-ghost-text .ln{display:flex;align-items:center;gap:0}
.ex-ghost-text .ln em{font:600 6px/11px var(--mono);color:var(--s4);font-style:normal;
  letter-spacing:0;text-transform:none;width:12px;margin-right:5px;flex:0 0 auto}
.ex-ghost-text .code{display:block;height:11px;border-radius:3px;background:var(--ink-3);width:30px;
  animation:aExGtxC 5.5s steps(1,end) infinite}
.ex-ghost-text .car{display:block;width:2px;height:13px;margin:0 1px;border-radius:1px;
  background:var(--a2);flex:0 0 auto}
.ex-ghost-text .ln b{display:block;height:11px;border-radius:3px;background:var(--ink-3)}
/* les trois mots de la suggestion, largeur ET marge partent de zero */
.ex-ghost-text .g1{width:0;margin-right:0;opacity:0;animation:aExGtxG1 5.5s steps(1,end) infinite}
.ex-ghost-text .g2{width:0;margin-right:0;opacity:0;animation:aExGtxG2 5.5s steps(1,end) infinite}
.ex-ghost-text .g3{width:0;opacity:0;animation:aExGtxG3 5.5s steps(1,end) infinite}
.ex-ghost-text .keys{display:flex;gap:6px;padding-left:17px}
.ex-ghost-text .keys em{display:block;padding:2px 5px;border-radius:4px;background:var(--s2);
  font:700 6px/8px var(--mono);color:var(--ink-2);font-style:normal;letter-spacing:0;
  text-transform:none;opacity:.25}
.ex-ghost-text .t1{animation:aExGtxT1 5.5s steps(1,end) infinite}
.ex-ghost-text .t2{animation:aExGtxT2 5.5s steps(1,end) infinite}
/* 30 puis 49 puis 74, soit 19 et 25, exactement les deux mots acceptes */
@keyframes aExGtxC{0%,11%{width:30px}34%,57%{width:49px}58%,95%{width:74px}97%,100%{width:30px}}
@keyframes aExGtxG1{0%,11%{width:0;margin-right:0;opacity:0}
  12%,33%{width:16px;margin-right:3px;opacity:.45}34%,100%{width:0;margin-right:0;opacity:0}}
@keyframes aExGtxG2{0%,11%{width:0;margin-right:0;opacity:0}
  12%,57%{width:22px;margin-right:3px;opacity:.45}58%,100%{width:0;margin-right:0;opacity:0}}
@keyframes aExGtxG3{0%,11%{width:0;opacity:0}12%,95%{width:18px;opacity:.45}97%,100%{width:0;opacity:0}}
@keyframes aExGtxT1{0%,33%{opacity:.25}34%,44%{opacity:1}45%,100%{opacity:.25}}
@keyframes aExGtxT2{0%,57%{opacity:.25}58%,68%{opacity:1}69%,100%{opacity:.25}}

/* reasoning-trace, la reponse terminee. La scene abstraite montrait le
   repli automatique, celle ci montre l etat d APRES, le resume d une ligne
   et sa reouverture a la demande.
   La reponse est presente du debut a la fin, elle ne bouge pas, c est le
   voisin inerte qui dit que le run est fini. Le chevron tourne de 90 et le
   panneau s ouvre a 34, la hauteur mesuree, jamais auto.
   Le pointeur atteint l en tete a 26 et l ouverture commence a 30, la cause
   precede l effet.
   Trois lignes de 6 et deux ecarts de 4 font 26, plus 8 de padding, 34.
   Le pointeur fait 14, l en tete a son centre en 20 et 4,5 avec le padding
   du haut, le coin doit donc atteindre 13 et moins 2,5, arrondi a 13 et 4. */
.ex-reasoning-trace{position:relative;display:flex;flex-direction:column;gap:6px;width:116px;
  padding-top:8px}
.ex-reasoning-trace .hdr{display:flex;align-items:center;gap:6px;height:7px}
.ex-reasoning-trace .chev{display:block;width:6px;height:7px;background:var(--lime);
  clip-path:polygon(0 0,6px 3.5px,0 7px);flex:0 0 auto;
  animation:aExRtrC 4.6s cubic-bezier(.3,0,.2,1) infinite}
.ex-reasoning-trace .sum{display:block;width:74px;height:6px;border-radius:3px;background:var(--s4)}
.ex-reasoning-trace .pan{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  padding:0 4px;border-radius:5px;background:var(--s1);box-sizing:border-box;
  animation:aExRtrP 4.6s cubic-bezier(.3,0,.2,1) infinite}
.ex-reasoning-trace .pan i{display:block;height:6px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-reasoning-trace .pan i:nth-child(1){width:98px}
.ex-reasoning-trace .pan i:nth-child(2){width:82px}
.ex-reasoning-trace .pan i:nth-child(3){width:90px}
/* la reponse est deja la et elle ne bouge pas */
.ex-reasoning-trace .ans{display:block;width:108px;height:7px;border-radius:4px;background:var(--ink-3)}
.ex-reasoning-trace .cur{left:100px;top:56px;animation:aExRtrCur 4.6s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExRtrC{0%,25%{transform:rotate(0)}30%,77%{transform:rotate(90deg)}
  82%,100%{transform:rotate(0)}}
@keyframes aExRtrP{0%,25%{height:0;padding-top:0;padding-bottom:0}
  30%,77%{height:34px;padding-top:4px;padding-bottom:4px}
  82%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExRtrCur{0%,18%{transform:translate(0,0)}26%,74%{transform:translate(-87px,-52px)}
  84%,100%{transform:translate(0,0)}}

/* status-narration, la phase longue. La scene abstraite opposait l etat et
   la minuterie, celle ci montre le cas ou la regle coute le plus cher, une
   phase qui dure trois fois plus longtemps que la suivante.
   Le verbe Searching TIENT de 6 a 69, il ne se met pas a tourner pour
   occuper le temps. Ce qui avance pendant ce temps est le DETAIL, 3 puis 9
   puis 14 fichiers. L etat et la progression sont deux informations
   distinctes et elles vivent sur deux lignes distinctes.
   La frise donne les durees reelles, 64 points puis 22, d ou 63 et 21 sur
   une piste de 86 avec 2 d ecart.
   Gele, on ne saurait pas si le systeme avance ou s il est bloque, et c est
   precisement ce que le compte de fichiers repond. */
.ex-status-narration{display:flex;flex-direction:column;gap:5px;width:92px}
.ex-status-narration .row{position:relative;display:block;height:12px}
.ex-status-narration .row em{position:absolute;left:0;top:0;font:700 9px/12px var(--mono);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;white-space:nowrap}
.ex-status-narration .w1{color:var(--lime);animation:aExSnrW1 5s ease infinite}
.ex-status-narration .w2{color:var(--lime);animation:aExSnrW2 5s ease infinite}
.ex-status-narration .sub{position:relative;display:block;height:9px}
.ex-status-narration .sub em{position:absolute;left:0;top:0;font:600 7px/9px var(--mono);
  color:var(--ink-3);font-style:normal;letter-spacing:0;text-transform:none;opacity:0;
  white-space:nowrap}
.ex-status-narration .c1{animation:aExSnrC1 5s ease infinite}
.ex-status-narration .c2{animation:aExSnrC2 5s ease infinite}
.ex-status-narration .c3{animation:aExSnrC3 5s ease infinite}
.ex-status-narration .ph{display:flex;gap:2px;margin-top:3px}
.ex-status-narration .ph i{display:block;height:6px;border-radius:3px;background:var(--lime);
  opacity:.2}
/* la largeur EST la duree, 64 points contre 22 */
.ex-status-narration .p1{width:63px;animation:aExSnrP1 5s steps(1,end) infinite}
.ex-status-narration .p2{width:21px;animation:aExSnrP2 5s steps(1,end) infinite}
/* le verbe tient pendant toute la phase, de 6 a 69 */
@keyframes aExSnrW1{0%,5%{opacity:0}9%,67%{opacity:1}71%,100%{opacity:0}}
@keyframes aExSnrW2{0%,68%{opacity:0}72%,90%{opacity:1}94%,100%{opacity:0}}
/* le detail avance pendant que le verbe tient */
@keyframes aExSnrC1{0%,11%{opacity:0}15%,29%{opacity:1}33%,100%{opacity:0}}
@keyframes aExSnrC2{0%,31%{opacity:0}35%,49%{opacity:1}53%,100%{opacity:0}}
@keyframes aExSnrC3{0%,51%{opacity:0}55%,67%{opacity:1}71%,100%{opacity:0}}
@keyframes aExSnrP1{0%,5%{opacity:.2}6%,69%{opacity:1}70%,100%{opacity:.2}}
@keyframes aExSnrP2{0%,69%{opacity:.2}70%,91%{opacity:1}92%,100%{opacity:.2}}

/* tool-call-chip, la puce ouverte. La scene abstraite montrait la marque de
   sortie et la persistance, celle ci montre la premiere consigne, le RESUME
   d une ligne quand elle est repliee, et le depliage sur la charge utile.
   La barre grise a droite du nom est ce resume, elle prend la place qui
   reste. Le depliage se fait a la demande, le pointeur touche la puce a 52
   et la charge s ouvre a 56.
   Quatre lignes de 6 et trois ecarts de 4 font 30, plus 8 de padding, 38.
   La puce fait 17 de haut, son centre est a 16,5 avec le padding du haut,
   le coin du pointeur doit donc atteindre 113 et 9,5.
   Gele, on ne saurait pas que la puce cache autre chose. */
.ex-tool-call-chip{position:relative;display:flex;flex-direction:column;gap:6px;width:132px;
  padding-top:8px}
.ex-tool-call-chip .chp{position:relative;display:flex;align-items:center;gap:5px;padding:4px 5px;
  border-radius:6px;background:var(--s1);box-sizing:border-box;width:132px}
.ex-tool-call-chip .ic{display:block;width:9px;height:9px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-tool-call-chip em{font:600 6px/9px var(--mono);color:var(--ink-2);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto}
/* le resume d une ligne, il occupe la place qui reste */
.ex-tool-call-chip .arg{display:block;height:5px;border-radius:3px;background:var(--s3);
  flex:1 1 auto}
.ex-tool-call-chip .mk{display:block;width:9px;height:9px;border-radius:3px;background:var(--lime);
  flex:0 0 auto;opacity:0;animation:aExTccMk 5s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-tool-call-chip .pay{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  padding:0 5px;border-radius:6px;background:var(--s1);box-sizing:border-box;
  animation:aExTccP 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-tool-call-chip .pay i{display:block;height:6px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-tool-call-chip .pay i:nth-child(1){width:104px}
.ex-tool-call-chip .pay i:nth-child(2){width:88px}
.ex-tool-call-chip .pay i:nth-child(3){width:96px}
.ex-tool-call-chip .pay i:nth-child(4){width:62px}
.ex-tool-call-chip .cur{left:100px;top:60px;animation:aExTccCur 5s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExTccMk{0%,29%{opacity:0;transform:scale(.4)}36%,94%{opacity:1;transform:scale(1)}
  96%,100%{opacity:0;transform:scale(.4)}}
/* 38 mesures, quatre lignes de 6, trois ecarts de 4 et 8 de padding */
@keyframes aExTccP{0%,55%{height:0;padding-top:0;padding-bottom:0}
  62%,94%{height:38px;padding-top:4px;padding-bottom:4px}
  96%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExTccCur{0%,42%{transform:translate(0,0)}52%,90%{transform:translate(13px,-50px)}
  96%,100%{transform:translate(0,0)}}

/* agent-timeline, l audit d une execution terminee. La scene abstraite
   montrait l ordre d ajout et l etape en cours, celle ci montre la
   troisieme consigne, une etape terminee que le lecteur DEPLIE apres coup.
   Les quatre pastilles sont grises et fixes, aucune n est en cours, aucune
   n a d animation, parce que le run est fini. C est le voisin inerte.
   Le depliage est un vrai element de flux, sa hauteur passe de 0 a 24 et il
   pousse les deux lignes du bas de 24 plus 4 d ecart, soit 28. Rien n est
   deplace par transform, c est la mise en page qui travaille, comme dans un
   vrai journal d execution.
   Deux lignes de 6 et un ecart de 4 font 16, plus 8 de padding, 24.
   La ligne 2 est a 15 de pas, son centre est a 20,5 plus 8 de padding, le
   coin du pointeur doit atteindre 53 et 21,5.
   Gele, on ne verrait pas quelle etape a ete ouverte. */
.ex-agent-timeline{position:relative;display:block;width:124px;padding-top:8px}
.ex-agent-timeline .lst{display:flex;flex-direction:column;gap:4px}
.ex-agent-timeline .rw{display:flex;align-items:center;gap:5px;height:11px}
/* terminees et immobiles, le run est fini */
.ex-agent-timeline .d{display:block;width:7px;height:7px;border-radius:4px;background:var(--s4);
  flex:0 0 auto}
.ex-agent-timeline .t{display:block;height:6px;border-radius:3px;background:var(--s4)}
.ex-agent-timeline .rw:nth-child(1) .t{width:88px}
.ex-agent-timeline .rw:nth-child(2) .t{width:74px}
.ex-agent-timeline .rw:nth-child(4) .t{width:82px}
.ex-agent-timeline .rw:nth-child(5) .t{width:66px}
.ex-agent-timeline .det{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  margin-left:12px;padding:0 5px;border-radius:5px;background:var(--s1);box-sizing:border-box;
  animation:aExAtsD 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-agent-timeline .det i{display:block;height:6px;border-radius:3px;background:var(--a2);
  flex:0 0 auto}
.ex-agent-timeline .det i:nth-child(1){width:82px}
.ex-agent-timeline .det i:nth-child(2){width:58px}
.ex-agent-timeline .cur{left:104px;top:64px;animation:aExAtsCur 5s cubic-bezier(.3,0,.2,1) infinite}
/* 24 mesures, deux lignes de 6, un ecart de 4 et 8 de padding */
@keyframes aExAtsD{0%,38%{height:0;padding-top:0;padding-bottom:0}
  46%,92%{height:24px;padding-top:4px;padding-bottom:4px}
  96%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExAtsCur{0%,26%{transform:translate(0,0)}36%,88%{transform:translate(-51px,-42px)}
  96%,100%{transform:translate(0,0)}}

/* citation-chip, la fiche de source. La scene abstraite mesurait le retard
   d une puce qui attend ses metadonnees, celle ci montre ce que la puce
   REVELE, et la sortie au clavier que la consigne exige.
   La definition nomme trois choses, le titre, le domaine et un extrait. La
   fiche en montre trois, un titre, un domaine en couleur, et deux lignes
   d extrait.
   Le pointeur atteint la puce a 24 et la fiche s ouvre a 28. A 70 la touche
   Echap s allume et la fiche se ferme a 74, ce qui montre que la revelation
   n est pas une impasse.
   Quatre lignes de 5 et trois ecarts de 4 font 32, plus 10 de padding, 42.
   La puce est a 62 plus 4, son centre est a 71,5 et 24,5 avec le padding du
   haut, le coin du pointeur doit atteindre 64,5 et 17,5.
   Gele, la fiche serait ouverte ou fermee sans qu on sache ce qui la
   commande. */
.ex-citation-chip{position:relative;display:flex;flex-direction:column;gap:6px;width:124px;
  padding-top:8px}
.ex-citation-chip .para{display:flex;flex-wrap:wrap;align-items:center;gap:4px;width:124px}
.ex-citation-chip .para b{display:block;height:7px;border-radius:4px;background:var(--ink-3)}
.ex-citation-chip .l1{flex:0 0 100%}
.ex-citation-chip .l2{width:62px}
.ex-citation-chip .num{display:block;width:11px;height:11px;border-radius:3px;background:var(--a2);
  font:700 6px/11px var(--mono);color:var(--s0);text-align:center;flex:0 0 auto;font-style:normal;
  letter-spacing:0;text-transform:none}
.ex-citation-chip .crd{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  padding:0 5px;border-radius:6px;background:var(--s1);box-sizing:border-box;
  animation:aExIccCd 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-citation-chip .crd i{display:block;height:5px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-citation-chip .ti{width:88px}
.ex-citation-chip .dm{width:44px;background:var(--a2)}
.ex-citation-chip .xc{width:106px}
.ex-citation-chip .xc.s{width:70px}
.ex-citation-chip .esc{display:block;align-self:flex-start;padding:2px 5px;border-radius:4px;
  background:var(--s2);font:700 6px/8px var(--mono);color:var(--ink-2);font-style:normal;
  letter-spacing:0;text-transform:none;opacity:.25;animation:aExIccE 5s steps(1,end) infinite}
.ex-citation-chip .cur{left:110px;top:70px;animation:aExIccCur 5s cubic-bezier(.3,0,.2,1) infinite}
/* 42 mesures, quatre lignes de 5, trois ecarts de 4 et 10 de padding */
@keyframes aExIccCd{0%,27%{height:0;padding-top:0;padding-bottom:0}
  34%,73%{height:42px;padding-top:5px;padding-bottom:5px}
  80%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExIccE{0%,69%{opacity:.25}70%,79%{opacity:1}80%,100%{opacity:.25}}
/* il reste sur la puce jusqu a 78, sans quoi la fiche se fermerait d elle
   meme et Echap ne demontrerait rien */
@keyframes aExIccCur{0%,16%{transform:translate(0,0)}24%,78%{transform:translate(-46px,-52px)}
  86%,100%{transform:translate(0,0)}}

/* confidence-band, l action de la bande basse. La scene abstraite montrait
   les trois bandes et le pourcentage qu elles remplacent, celle ci montre
   la troisieme consigne, une bande basse APPARIEE a un moyen de verifier.
   La rangee haute est inerte, aucune animation, et elle n a aucune action.
   C est ce qui prouve que l action est attachee a la bande BASSE et non a
   toutes les affirmations. Une bande haute qui proposerait de verifier ne
   voudrait plus rien dire.
   Le pointeur touche l action a 40 et la source s ouvre a 44. Une bande
   basse qui ne menerait nulle part serait un aveu sans issue.
   Deux lignes de 5 et un ecart de 4 font 14, plus 10 de padding, 24.
   La bande low fait 11 de texte plus 8 de padding, soit 19. L action est
   donc a 46 plus 5 plus 19 plus 5, soit 75, et sa largeur de 30 donne un
   centre a 90. La rangee 2 est a 18, son centre a 24 plus 8 de padding, le
   coin du pointeur doit atteindre 83 et 25.
   Gele, on garderait les deux bandes, ce qui est juste, mais on perdrait le
   fait que la basse mene quelque part. */
.ex-confidence-band{position:relative;display:flex;flex-direction:column;gap:6px;width:124px;
  padding-top:8px}
.ex-confidence-band .rw{display:flex;align-items:center;gap:5px;height:12px}
.ex-confidence-band .cl{display:block;height:7px;border-radius:4px;background:var(--ink-3)}
.ex-confidence-band .hi .cl{width:52px}
.ex-confidence-band .lo .cl{width:46px}
.ex-confidence-band .bd{display:block;padding:2px 4px;border-radius:4px;font:700 6px/8px var(--mono);
  color:var(--s0);font-style:normal;letter-spacing:0;text-transform:none;flex:0 0 auto}
.ex-confidence-band .hi .bd{background:var(--lime)}
.ex-confidence-band .lo .bd{background:var(--a4)}
/* seule la bande basse fait pousser une action */
.ex-confidence-band .vf{display:block;padding:2px 5px;border-radius:4px;box-sizing:border-box;
  border:1px solid var(--a4);font:700 6px/8px var(--mono);color:var(--a4);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto;animation:aExCfbV 5s steps(1,end) infinite}
.ex-confidence-band .src{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  margin-left:12px;padding:0 5px;border-radius:5px;background:var(--s1);box-sizing:border-box;
  animation:aExCfbS 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-confidence-band .src i{display:block;height:5px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-confidence-band .ti{width:82px}
.ex-confidence-band .dm{width:46px;background:var(--a2)}
.ex-confidence-band .cur{left:112px;top:72px;animation:aExCfbCur 5s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExCfbV{0%,39%{background:none}40%,90%{background:var(--s2)}91%,100%{background:none}}
/* 24 mesures, deux lignes de 5, un ecart de 4 et 10 de padding */
@keyframes aExCfbS{0%,43%{height:0;padding-top:0;padding-bottom:0}
  51%,92%{height:24px;padding-top:5px;padding-bottom:5px}
  96%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExCfbCur{0%,28%{transform:translate(0,0)}40%,88%{transform:translate(-30px,-47px)}
  96%,100%{transform:translate(0,0)}}

/* running-meter, les deux unites. La scene abstraite comparait les cadences,
   celle ci montre la deuxieme consigne, la monnaie A COTE du compte de
   jetons, et le seuil declare.
   Le cycle fait 2 secondes et il porte QUATRE relevés, a 12 37 62 et 87
   pour cent, soit un ecart de 25 points qui vaut une demi seconde. C est
   litteralement deux mises a jour par seconde, la consigne est respectee au
   sens propre et non a l echelle pres.
   Les deux chiffres changent au MEME instant, parce qu ils decrivent la
   meme depense. Des unites qui avanceraient a des cadences differentes
   laisseraient croire a deux mesures independantes.
   Le budget vaut 16k et le seuil 70 pour cent, soit 11,2k. Sur une piste de
   92 le seuil est a 64,4, arrondi a 64. Les quatre valeurs 3,1k 6,8k 9,9k
   et 12,4k donnent 17 40 57 et 72. Le franchissement a lieu au quatrieme
   releve, 9,9k etant sous 11,2k et 12,4k au dessus. La couleur bascule la
   et nulle part ailleurs.
   Gele, on ne verrait pas la cadence, qui est tout le sujet du terme. */
.ex-running-meter{display:flex;flex-direction:column;gap:9px;width:100px}
.ex-running-meter .figs{display:flex;gap:12px}
.ex-running-meter .tk,.ex-running-meter .cu{position:relative;display:block;height:14px;width:44px}
.ex-running-meter .figs em{position:absolute;left:0;top:0;font:800 11px/14px var(--mono);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;white-space:nowrap}
.ex-running-meter .tk em{color:var(--lime)}
.ex-running-meter .cu em{color:var(--ink-2)}
/* les deux unites changent au meme instant, quatre fois en 2 secondes */
.ex-running-meter .a1,.ex-running-meter .b1{animation:aExRmt1 2s steps(1,end) infinite}
.ex-running-meter .a2,.ex-running-meter .b2{animation:aExRmt2 2s steps(1,end) infinite}
.ex-running-meter .a3,.ex-running-meter .b3{animation:aExRmt3 2s steps(1,end) infinite}
.ex-running-meter .a4,.ex-running-meter .b4{animation:aExRmt4 2s steps(1,end) infinite}
.ex-running-meter .trk{position:relative;display:block;width:92px;height:8px;border-radius:4px;
  background:var(--s2)}
.ex-running-meter .fill{display:block;height:8px;border-radius:4px;background:var(--lime);width:0;
  animation:aExRmtF 2s steps(1,end) infinite}
/* le seuil est declare, il ne bouge pas */
.ex-running-meter .thr{position:absolute;left:64px;top:-2px;display:block;width:1.5px;height:12px;
  background:var(--s5)}
.ex-running-meter .lb{display:block;font:600 6px/8px var(--mono);color:var(--s4);font-style:normal;
  letter-spacing:0;text-transform:none}
@keyframes aExRmt1{0%,11%{opacity:0}12%,36%{opacity:1}37%,100%{opacity:0}}
@keyframes aExRmt2{0%,36%{opacity:0}37%,61%{opacity:1}62%,100%{opacity:0}}
@keyframes aExRmt3{0%,61%{opacity:0}62%,86%{opacity:1}87%,100%{opacity:0}}
@keyframes aExRmt4{0%,86%{opacity:0}87%,95%{opacity:1}97%,100%{opacity:0}}
/* 17 40 57 72, la couleur bascule au quatrieme releve et pas avant */
@keyframes aExRmtF{0%,11%{width:0;background:var(--lime)}
  12%{width:17px}37%{width:40px}62%{width:57px}
  87%,95%{width:72px;background:var(--a4)}
  97%,100%{width:0;background:var(--lime)}}

/* approval-card, la portee nommee exactement. La scene abstraite montrait le
   poids egal et le delai qui n accorde rien, celle ci prend au serieux la
   premiere consigne, nommer le verbe ET l objet.
   En haut l objet est resolu, quatorze fichiers dans un dossier nomme, et
   la carte peut donc DENOMBRER, trois lignes s ouvrent en dessous pour
   montrer ce qui sera touche. En bas la carte demande de proceder. Elle ne
   peut rien deplier parce qu elle n a rien a enumerer, et elle reste donc
   inerte du premier au dernier pour cent. C est la meilleure preuve, une
   formulation vague n a aucun detail a offrir.
   Le placeholder fait 58 et le texte final aussi, l apparition ne pousse
   donc rien. La carte fait 128, moins 12 de padding et 2 de bordure, il
   reste 114, et 3 boutons de 34 avec 2 ecarts de 6 font exactement 114.
   Gele, on ne verrait pas que la carte du haut sait de quoi elle parle. */
.ex-approval-card{display:flex;flex-direction:column;gap:8px;width:128px}
.ex-approval-card .cd{display:flex;flex-direction:column;gap:6px;width:128px;padding:6px;
  border-radius:9px;background:var(--s1);border:1px solid var(--line-2);box-sizing:border-box}
.ex-approval-card .hd{position:relative;display:flex;align-items:center;gap:4px;height:9px}
.ex-approval-card .v{display:block;width:22px;height:8px;border-radius:3px;background:var(--ink-3);
  flex:0 0 auto}
/* meme largeur que le texte final, l arrivee ne pousse rien */
.ex-approval-card .ph{display:block;width:58px;height:8px;border-radius:3px;background:var(--s3);
  flex:0 0 auto;animation:aExApcPh 5s steps(1,end) infinite}
.ex-approval-card .o{position:absolute;left:26px;top:0;font:700 6px/9px var(--mono);
  color:var(--lime);font-style:normal;letter-spacing:0;text-transform:none;white-space:nowrap}
.ex-approval-card .ok .o{opacity:0;animation:aExApcO 5s steps(1,end) infinite}
/* la carte vague ne peut rien denombrer, elle est inerte */
.ex-approval-card .ko .o{color:var(--a4)}
.ex-approval-card .items{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  animation:aExApcIt 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-approval-card .items i{display:block;height:5px;border-radius:3px;background:var(--a2);
  flex:0 0 auto}
.ex-approval-card .items i:nth-child(1){width:96px}
.ex-approval-card .items i:nth-child(2){width:78px}
.ex-approval-card .items i:nth-child(3){width:88px}
.ex-approval-card .bs{display:flex;gap:6px}
/* poids egal, 34 chacun, 3 fois 34 plus 2 fois 6 font 114 */
.ex-approval-card .bs i{display:block;width:34px;height:14px;border-radius:5px;background:var(--s3);
  flex:0 0 auto}
@keyframes aExApcPh{0%,39%{opacity:1}40%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExApcO{0%,39%{opacity:0}40%,94%{opacity:1}96%,100%{opacity:0}}
/* trois lignes de 5 et deux ecarts de 4 font 23 */
@keyframes aExApcIt{0%,53%{height:0}60%,94%{height:23px}96%,100%{height:0}}

/* staged-diff, les trois niveaux de commande. La scene abstraite montrait la
   stabilite du defilement, celle ci montre la deuxieme consigne, tout
   accepter, tout refuser et par bloc, et surtout leur ORDRE d usage.
   A 30 le pointeur resout le bloc du BAS seul, par sa propre commande. Le
   bloc du haut ne bouge pas, ce qui prouve que la commande est bien par
   bloc et non globale. A 66 il presse tout accepter et le bloc du haut se
   resout a son tour.
   Tout refuser reste intact du debut a la fin. C est le voisin inerte, il
   dit que les trois niveaux coexistent et que rien ne se declenche seul.
   Le bloc du bas occupe 74 a 91, son centre est donc a 82,5. Sa commande
   est a 78 plus 4, soit 82, et son centre a 86. Le coin du pointeur doit
   donc atteindre 79 et 75,5. La puce tout accepter a son centre en 23 et
   14, le coin doit atteindre 16 et 7.
   Gele, on ne saurait pas laquelle des trois commandes a agi. */
.ex-staged-diff{position:relative;display:flex;flex-direction:column;gap:8px;width:110px;
  padding-top:8px}
.ex-staged-diff .ctl{display:flex;gap:5px}
.ex-staged-diff .ctl em{display:block;padding:2px 5px;border-radius:4px;font:700 6px/8px var(--mono);
  font-style:normal;letter-spacing:0;text-transform:none;box-sizing:border-box}
.ex-staged-diff .all{background:var(--s2);color:var(--lime);animation:aExSdfAll 5s steps(1,end) infinite}
/* jamais presse, il est la pour dire que les trois niveaux coexistent */
.ex-staged-diff .none{background:var(--s2);color:var(--a4)}
.ex-staged-diff .col{display:flex;flex-direction:column;gap:5px;width:102px}
.ex-staged-diff .ln{display:block;width:96px;height:7px;border-radius:3px;background:var(--s3)}
.ex-staged-diff .hk{display:flex;align-items:center;gap:4px}
.ex-staged-diff .pair{display:flex;flex-direction:column;gap:3px;width:78px}
.ex-staged-diff .del{display:block;height:7px;width:58px;border-radius:3px;background:var(--a4)}
.ex-staged-diff .add{display:block;height:7px;width:70px;border-radius:3px;background:var(--lime)}
/* la ligne supprimee tombe a hauteur nulle, sa marge negative mange l ecart */
.ex-staged-diff .h1 .del{animation:aExSdfD1 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-staged-diff .h2 .del{animation:aExSdfD2 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-staged-diff .y,.ex-staged-diff .n{display:block;width:8px;height:8px;border-radius:3px;
  flex:0 0 auto}
.ex-staged-diff .y{background:var(--lime)}
.ex-staged-diff .n{background:var(--a4)}
.ex-staged-diff .h1 .y,.ex-staged-diff .h1 .n{animation:aExSdfC1 5s steps(1,end) infinite}
.ex-staged-diff .h2 .y,.ex-staged-diff .h2 .n{animation:aExSdfC2 5s steps(1,end) infinite}
.ex-staged-diff .cur{left:96px;top:92px;animation:aExSdfCur 5s cubic-bezier(.3,0,.2,1) infinite}
/* resolu par tout accepter, a 66 */
@keyframes aExSdfD1{0%,65%{height:7px;margin-bottom:0}72%,94%{height:0;margin-bottom:-3px}
  96%,100%{height:7px;margin-bottom:0}}
/* resolu par sa propre commande, a 30, et seul */
@keyframes aExSdfD2{0%,29%{height:7px;margin-bottom:0}36%,94%{height:0;margin-bottom:-3px}
  96%,100%{height:7px;margin-bottom:0}}
@keyframes aExSdfC1{0%,65%{opacity:1}66%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExSdfC2{0%,29%{opacity:1}30%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExSdfAll{0%,65%{background:var(--s2)}66%,80%{background:var(--lime)}
  81%,100%{background:var(--s2)}}
@keyframes aExSdfCur{0%,20%{transform:translate(0,0)}30%,56%{transform:translate(-17px,-16px)}
  66%,88%{transform:translate(-81px,-85px)}96%,100%{transform:translate(0,0)}}

/* tool-permission, la revocation utilisee. La scene abstraite annoncait que
   toujours autoriser laisse une trace revocable, celle ci s en sert.
   Trois capacites sont accordees en permanence. A 46 le pointeur retire la
   deuxieme. Sa rangee tombe a hauteur nulle et la troisieme remonte de 17,
   soit une rangee de 12 plus son ecart de 5. La liste se recompose, ce qui
   est la preuve qu une permission permanente n est pas un aller simple.
   La rangee 2 est a 14 plus 17, son centre a 37 et 45 avec le padding du
   haut. La commande de retrait est a 90, son centre a 95. Le coin du
   pointeur doit donc atteindre 88 et 38.
   Gele, la liste ressemblerait a un simple etat des lieux. */
.ex-tool-permission{position:relative;display:flex;flex-direction:column;gap:6px;width:100px;
  padding-top:8px}
.ex-tool-permission .hd{display:block;font:700 6px/8px var(--mono);color:var(--ink-2);
  font-style:normal;letter-spacing:0;text-transform:none}
.ex-tool-permission .rows{display:flex;flex-direction:column;gap:5px}
.ex-tool-permission .rw{display:flex;align-items:center;gap:6px;height:12px;overflow:hidden}
.ex-tool-permission .t{display:block;width:84px;height:7px;border-radius:3px;background:var(--s3)}
.ex-tool-permission .rv{display:block;width:10px;height:10px;border-radius:3px;background:var(--a4);
  flex:0 0 auto}
/* retiree a 46, la rangee cesse d occuper sa place */
.ex-tool-permission .w2{animation:aExTppRw 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-tool-permission .cur{left:96px;top:78px;animation:aExTppCur 5s cubic-bezier(.3,0,.2,1) infinite}
/* 17 de moins, une rangee de 12 plus son ecart de 5 */
@keyframes aExTppRw{0%,45%{height:12px;margin-bottom:0;opacity:1}
  54%,94%{height:0;margin-bottom:-5px;opacity:0}
  96%,100%{height:12px;margin-bottom:0;opacity:1}}
@keyframes aExTppCur{0%,34%{transform:translate(0,0)}46%,70%{transform:translate(-8px,-40px)}
  96%,100%{transform:translate(0,0)}}

/* autonomy-level, ce que les niveaux VEULENT dire. La scene abstraite
   nommait les trois niveaux et montrait la retrogradation, celle ci montre
   la seule chose qui compte pour l utilisateur, jusqu ou la MEME action va
   selon le niveau.
   Les trois rangees lancent la meme action a 10. En haut elle s arrete a
   proposee. Au milieu elle atteint la porte et ATTEND, la porte reste en
   creux et ne se remplit jamais. En bas elle passe et se termine.
   Le nombre de blocs allumes EST le niveau, et c est ce qui rend les trois
   noms comparables. Un curseur continu ne pourrait pas produire cette
   lecture, parce qu il n a pas de palier.
   Les blocs font 24 avec 4 d ecart, trois font donc 80, et la rangee 30
   plus 6 plus 80, soit 116.
   Gele, les trois rangees se liraient comme trois etats sans lien, alors
   qu il s agit d une seule action a trois portees. */
.ex-autonomy-level{display:flex;flex-direction:column;gap:8px;width:116px}
.ex-autonomy-level .rw{display:flex;align-items:center;gap:6px;height:12px}
.ex-autonomy-level em{display:block;width:30px;font:700 6px/12px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto}
.ex-autonomy-level .r1 em::before{content:"suggest";color:var(--s4)}
.ex-autonomy-level .r2 em::before{content:"confirm";color:var(--a2)}
.ex-autonomy-level .r3 em::before{content:"execute";color:var(--lime)}
.ex-autonomy-level .out{display:flex;gap:4px}
.ex-autonomy-level .out i{display:block;width:24px;height:12px;border-radius:4px;box-sizing:border-box;
  background:var(--s2);flex:0 0 auto}
/* les trois rangees lancent la meme action au meme instant */
.ex-autonomy-level .s1{animation:aExAlyS1 5.5s steps(1,end) infinite}
.ex-autonomy-level .r3 .s2{animation:aExAlyS2 5.5s steps(1,end) infinite}
.ex-autonomy-level .r3 .s3{animation:aExAlyS3 5.5s steps(1,end) infinite}
/* elle atteint la porte et n en passe jamais, le bloc reste en creux */
.ex-autonomy-level .r2 .s2{animation:aExAlyGate 5.5s steps(1,end) infinite}
@keyframes aExAlyS1{0%,19%{background:var(--s2)}20%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExAlyS2{0%,33%{background:var(--s2)}34%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExAlyS3{0%,49%{background:var(--s2)}50%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
/* en creux et en attente, jamais rempli */
@keyframes aExAlyGate{0%,33%{background:var(--s2);border:0 solid var(--a2)}
  34%,94%{background:none;border:1.5px solid var(--a2)}
  96%,100%{background:var(--s2);border:0 solid var(--a2)}}

/* kill-switch, la reprise en deux gestes. La scene abstraite montrait la
   file annulee et les identifiants revoques, celle ci montre la troisieme
   consigne, qu il faut une action DELIBEREE ET DISTINCTE pour repartir.
   La file est vide, le systeme est arrete. A 24 le pointeur presse reprendre
   et RIEN NE DEMARRE. Seule une confirmation apparait. Le premier geste
   arme, il ne lance pas. A 52 le pointeur presse la confirmation, et alors
   seulement la file se remplit, a 58.
   Le geste unique est le piege, parce qu un coupe circuit presse par
   accident se relacherait aussi par accident.
   Le bouton reprendre est a 28, son centre a 38 et 40. Le coin du pointeur
   doit atteindre 31 et 33. La confirmation occupe 58 a 70, son centre est
   donc a 17 et 64, et le coin doit atteindre 10 et 57.
   Gele, on ne verrait pas qu il faut deux gestes. */
.ex-kill-switch{position:relative;display:flex;flex-direction:column;width:88px;padding-top:8px}
.ex-kill-switch .q{display:flex;gap:4px;height:12px;margin-bottom:10px}
.ex-kill-switch .q i{display:block;width:16px;height:12px;border-radius:4px;background:var(--s2);
  flex:0 0 auto}
/* elle ne se remplit qu apres la confirmation */
.ex-kill-switch .q1{animation:aExKswQ1 5.5s steps(1,end) infinite}
.ex-kill-switch .q2{animation:aExKswQ2 5.5s steps(1,end) infinite}
.ex-kill-switch .q3{animation:aExKswQ3 5.5s steps(1,end) infinite}
.ex-kill-switch .q4{animation:aExKswQ4 5.5s steps(1,end) infinite}
.ex-kill-switch .ctl{display:flex;gap:8px;height:20px;margin-bottom:8px}
.ex-kill-switch .stop{display:block;width:20px;height:20px;border-radius:10px;background:var(--a4)}
.ex-kill-switch .res{display:block;width:20px;height:20px;border-radius:10px;box-sizing:border-box;
  border:1.5px solid var(--lime);animation:aExKswRes 5.5s steps(1,end) infinite}
/* le premier geste ne fait qu armer, il fait apparaitre ceci */
.ex-kill-switch .hint{display:block;align-self:flex-start;padding:2px 5px;border-radius:4px;
  background:var(--s2);font:700 6px/8px var(--mono);color:var(--lime);font-style:normal;
  letter-spacing:0;text-transform:none;opacity:0;animation:aExKswH 5.5s ease infinite}
.ex-kill-switch .cur{left:88px;top:76px;animation:aExKswCur 5.5s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExKswQ1{0%,57%{background:var(--s2)}58%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExKswQ2{0%,65%{background:var(--s2)}66%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExKswQ3{0%,73%{background:var(--s2)}74%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExKswQ4{0%,81%{background:var(--s2)}82%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
/* arme a 24, consomme a 52 */
@keyframes aExKswRes{0%,23%{background:none}24%,51%{background:var(--s2)}
  52%,100%{background:none}}
@keyframes aExKswH{0%,23%{opacity:0}28%,51%{opacity:1}56%,100%{opacity:0}}
@keyframes aExKswCur{0%,14%{transform:translate(0,0)}24%,40%{transform:translate(-57px,-43px)}
  52%,72%{transform:translate(-78px,-20px)}96%,100%{transform:translate(0,0)}}

/* checkpoint-rewind, l etiquette. La scene abstraite montrait l ordre et
   l independance des voies, celle ci prend la seconde moitie de la premiere
   consigne, etiqueter le point avec la consigne qui l a cause.
   Les trois points naissent a 10, 34 et 58, et leur etiquette arrive AU
   MEME instant, pas apres. Elles font 52, 38 et 46, trois largeurs
   differentes, donc trois points reconnaissables.
   En bas les trois points naissent aux memes instants et sont
   rigoureusement identiques. Rien ne les distingue, et revenir a l un
   plutot qu a l autre devient un pari. C est le meme mecanisme prive de son
   etiquette, la comparaison ne porte donc que sur elle.
   Le pas vaut 8 plus 6, soit 14, et chaque rail fait 3 fois 8 plus 2 fois 6,
   soit 36.
   Gele, on garderait les etiquettes, ce qui est juste, elles sont un etat.
   Ce qu on perdrait est le fait qu elles arrivent AVEC le point. */
.ex-checkpoint-rewind{display:flex;flex-direction:column;gap:10px;width:105px}
.ex-checkpoint-rewind .one{display:flex;align-items:flex-start;gap:6px}
.ex-checkpoint-rewind em{font:600 6px/8px var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:34px;flex:0 0 auto}
.ex-checkpoint-rewind .ok em{color:var(--lime)}
.ex-checkpoint-rewind .ko em{color:var(--a4)}
.ex-checkpoint-rewind .rail{display:flex;flex-direction:column;gap:6px}
.ex-checkpoint-rewind .k{display:flex;align-items:center;gap:5px;height:8px;opacity:0}
.ex-checkpoint-rewind .k i{display:block;width:8px;height:8px;border-radius:3px;background:var(--a2);
  flex:0 0 auto}
/* trois largeurs differentes, donc trois points reconnaissables */
.ex-checkpoint-rewind .lb{display:block;height:6px;border-radius:3px;background:var(--s4)}
.ex-checkpoint-rewind .k1 .lb{width:52px}
.ex-checkpoint-rewind .k2 .lb{width:38px}
.ex-checkpoint-rewind .k3 .lb{width:46px}
.ex-checkpoint-rewind .k1{animation:aExCkrK1 5s cubic-bezier(.2,1,.3,1) infinite}
.ex-checkpoint-rewind .k2{animation:aExCkrK2 5s cubic-bezier(.2,1,.3,1) infinite}
.ex-checkpoint-rewind .k3{animation:aExCkrK3 5s cubic-bezier(.2,1,.3,1) infinite}
@keyframes aExCkrK1{0%,9%{opacity:0;transform:translateX(-4px)}
  14%,94%{opacity:1;transform:translateX(0)}96%,100%{opacity:0;transform:translateX(-4px)}}
@keyframes aExCkrK2{0%,33%{opacity:0;transform:translateX(-4px)}
  38%,94%{opacity:1;transform:translateX(0)}96%,100%{opacity:0;transform:translateX(-4px)}}
@keyframes aExCkrK3{0%,57%{opacity:0;transform:translateX(-4px)}
  62%,94%{opacity:1;transform:translateX(0)}96%,100%{opacity:0;transform:translateX(-4px)}}

/* skeleton-morph, la grille dans les DEUX sens. La scene abstraite reservait
   la boite et alignait trois colonnes, celle ci prend la deuxieme consigne
   au pied de la lettre avec un tableau, ou la grille a des lignes autant que
   des colonnes.
   Les deux couches sont declarees avec la MEME grille, trois colonnes de 30
   et 4 d ecart, trois rangees de 9 et 4 d ecart, soit 98 sur 35. L alignement
   n est donc pas approche, il est structurel, aucune cellule ne peut se
   decaler puisqu elles partagent la meme declaration.
   Le squelette est uniforme, le tableau reel ne l est pas. C est la seule
   chose qu un squelette ne peut pas connaitre avant les donnees, et c est
   aussi la seule chose qui change a l echange.
   Le fondu va de 40 a 44, soit 4 points sur 4,6 secondes, ce qui vaut 184
   millisecondes et tient sous la limite de 200.
   Le pied de page ne bouge jamais, il n a aucune animation, parce que la
   boite fait 35 depuis le premier pour cent.
   Gele, on ne verrait pas que l echange ne deplace rien. */
.ex-skeleton-morph{display:flex;flex-direction:column;align-items:flex-start;gap:8px}
/* la boite est reservee, 35 des le depart */
.ex-skeleton-morph .tb{position:relative;display:block;width:98px;height:35px}
.ex-skeleton-morph .sk,.ex-skeleton-morph .rl{position:absolute;left:0;top:0;display:grid;
  grid-template-columns:repeat(3,30px);grid-auto-rows:9px;gap:4px}
.ex-skeleton-morph .sk i{display:block;border-radius:2px;background:var(--s3)}
.ex-skeleton-morph .rl i{display:block;border-radius:2px;background:var(--s2)}
.ex-skeleton-morph .c1,.ex-skeleton-morph .c2,.ex-skeleton-morph .c3{background:var(--ink-3)}
.ex-skeleton-morph .c5,.ex-skeleton-morph .c9{background:var(--lime)}
.ex-skeleton-morph .sk{animation:aExSkmSk 4.6s linear infinite}
.ex-skeleton-morph .rl{opacity:0;animation:aExSkmRl 4.6s linear infinite}
/* aucune animation, la boite ne bouge pas donc lui non plus */
.ex-skeleton-morph .ft{display:block;width:98px;height:7px;border-radius:4px;background:var(--ink-3)}
@keyframes aExSkmSk{0%,40%{opacity:1}44%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExSkmRl{0%,40%{opacity:0}44%,94%{opacity:1}96%,100%{opacity:0}}

/* context-composer, le declencheur et son aller retour complet. La scene
   abstraite montrait le retour a la ligne et la croissance vers le haut,
   celle ci prend la troisieme consigne, ouvrir le selecteur sur l arobase.
   A 18 l arobase est frappee et apparait dans le champ. A 24 le selecteur
   s ouvre, et il s ouvre VERS LE HAUT lui aussi, puisqu il est ancre sur le
   composeur. A 44 le pointeur choisit le deuxieme element. A 50 le selecteur
   se ferme, l arobase est CONSOMMEE et disparait, et la deuxieme puce
   apparait a sa place.
   L arobase qui disparait est le detail qui compte. Elle etait une commande
   et non du texte, elle ne doit donc rien laisser dans ce que le modele
   verra.
   Le selecteur fait 3 fois 9 plus 2 fois 4 plus 10 de padding, soit 45. Le
   composeur en fait 42, le selecteur est donc a 46 du bas et son bord haut
   a 104 moins 46 moins 45, soit 13. Le deuxieme element commence a 13 plus
   1 de bordure plus 5 de padding plus 9 plus 4, soit 32, son centre est
   donc a 62 et 36,5. Le coin du pointeur doit atteindre 55 et 29,5.
   Gele, on ne verrait pas d ou vient la puce. */
.ex-context-composer{position:relative;display:block;width:124px;height:104px}
/* ancre sur le composeur, il s ouvre donc vers le haut aussi */
.ex-context-composer .pick{position:absolute;left:0;right:0;bottom:46px;display:flex;
  flex-direction:column;gap:4px;height:0;overflow:hidden;padding:0 5px;border-radius:8px;
  background:var(--s1);border:1px solid var(--line-2);box-sizing:border-box;
  animation:aExCccPk 5.5s cubic-bezier(.3,0,.2,1) infinite}
.ex-context-composer .pick i{display:block;height:9px;border-radius:3px;background:var(--s3);
  flex:0 0 auto}
.ex-context-composer .p2{animation:aExCccP2 5.5s steps(1,end) infinite}
.ex-context-composer .cmp{position:absolute;left:0;right:0;bottom:0;display:flex;
  flex-direction:column;gap:6px;padding:6px;border-radius:9px;background:var(--s1);
  border:1px solid var(--line-2);box-sizing:border-box}
.ex-context-composer .chips{display:flex;flex-wrap:wrap;gap:5px}
.ex-context-composer .chips i{display:flex;align-items:center;justify-content:flex-end;width:26px;
  height:12px;flex:0 0 auto;padding-right:3px;border-radius:4px;background:var(--s3);
  box-sizing:border-box}
.ex-context-composer .chips i b{display:block;width:5px;height:5px;border-radius:3px;
  background:var(--a4)}
/* la puce nait du choix, a 50 */
.ex-context-composer .c2{width:0;margin-right:-5px;opacity:0;
  animation:aExCccC2 5.5s cubic-bezier(.2,1,.3,1) infinite}
.ex-context-composer .fld{display:flex;align-items:center;gap:5px;height:10px}
/* elle est une commande et non du texte, elle est consommee */
.ex-context-composer .at{display:block;font:700 8px/10px var(--mono);color:var(--a2);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;
  animation:aExCccAt 5.5s steps(1,end) infinite}
.ex-context-composer .typed{display:block;width:34px;height:6px;border-radius:3px;
  background:var(--s3)}
.ex-context-composer .cur{left:100px;top:88px;animation:aExCccCur 5.5s cubic-bezier(.3,0,.2,1) infinite}
/* 45 mesures, trois elements de 9, deux ecarts de 4 et 10 de padding */
@keyframes aExCccPk{0%,23%{height:0;padding-top:0;padding-bottom:0}
  30%,49%{height:45px;padding-top:5px;padding-bottom:5px}
  56%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExCccP2{0%,43%{background:var(--s3)}44%,49%{background:var(--lime)}
  50%,100%{background:var(--s3)}}
@keyframes aExCccAt{0%,17%{opacity:0}18%,49%{opacity:1}50%,100%{opacity:0}}
@keyframes aExCccC2{0%,49%{width:0;margin-right:-5px;opacity:0}
  56%,94%{width:26px;margin-right:0;opacity:1}96%,100%{width:0;margin-right:-5px;opacity:0}}
@keyframes aExCccCur{0%,32%{transform:translate(0,0)}44%,56%{transform:translate(-45px,-58px)}
  96%,100%{transform:translate(0,0)}}

/* voice-waveform, les TROIS modes d un seul controle. La scene abstraite
   opposait le direct et la boucle prefabriquee, celle ci montre la troisieme
   consigne au complet, le changement de forme entre ecouter et parler, et
   elle ajoute le mode au repos que l abstrait n avait pas.
   Au repos, de 0 a 14, les barres sont a plat et grises. Le controle est la
   mais il n entend rien, et cela doit se voir sans lire un mot.
   En ecoute, de 18 a 56, elles suivent l amplitude et passent en lime.
   En parole, de 62 a 92, elles prennent une hauteur uniforme et passent en
   a2, parce qu une voix synthetisee n a pas d amplitude mesuree. L anneau du
   bouton respire alors, ce qui distingue une sortie d une entree.
   Les cinq barres font 7 avec 5 d ecart, soit 5 fois 7 plus 4 fois 5, 55. Le
   bouton fait 26, la rangee vaut donc 26 plus 8 plus 55, soit 89.
   Gele, un seul des trois modes serait visible, et un controle vocal qui ne
   dit pas dans quel mode il est ne sert a rien. */
.ex-voice-waveform{display:flex;flex-direction:column;gap:8px;width:92px}
.ex-voice-waveform .hd{display:flex;align-items:flex-end;gap:8px}
.ex-voice-waveform .btn{position:relative;display:block;width:26px;height:26px;border-radius:14px;
  background:var(--s2);flex:0 0 auto}
/* il ne respire qu en mode parole, ce qui distingue la sortie de l entree */
.ex-voice-waveform .ring{position:absolute;inset:0;display:block;border-radius:14px;
  box-sizing:border-box;border:1.5px solid var(--a2);opacity:0;
  animation:aExVwfRing 4.4s ease-out infinite}
.ex-voice-waveform .mic{position:absolute;left:10px;top:7px;display:block;width:6px;height:12px;
  border-radius:3px;background:var(--ink-3)}
.ex-voice-waveform .bars{display:flex;align-items:flex-end;gap:5px;height:26px}
.ex-voice-waveform .bars i{display:block;width:7px;height:3px;border-radius:4px;
  background:var(--s4);flex:0 0 auto}
.ex-voice-waveform .b1{animation:aExVwf1 4.4s ease-in-out infinite}
.ex-voice-waveform .b2{animation:aExVwf2 4.4s ease-in-out infinite}
.ex-voice-waveform .b3{animation:aExVwf3 4.4s ease-in-out infinite}
.ex-voice-waveform .b4{animation:aExVwf4 4.4s ease-in-out infinite}
.ex-voice-waveform .b5{animation:aExVwf5 4.4s ease-in-out infinite}
.ex-voice-waveform .md{position:relative;display:block;height:9px}
.ex-voice-waveform .md em{position:absolute;left:0;top:0;font:700 7px/9px var(--mono);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;white-space:nowrap}
.ex-voice-waveform .m1{color:var(--s4);animation:aExVwfM1 4.4s ease infinite}
.ex-voice-waveform .m2{color:var(--lime);animation:aExVwfM2 4.4s ease infinite}
.ex-voice-waveform .m3{color:var(--a2);animation:aExVwfM3 4.4s ease infinite}
@keyframes aExVwfRing{0%,61%{opacity:0;transform:scale(1)}
  70%{opacity:1;transform:scale(1.16)}92%,100%{opacity:0;transform:scale(1)}}
@keyframes aExVwf1{0%,14%{height:3px;background:var(--s4)}
  22%{height:13px;background:var(--lime)}34%{height:20px;background:var(--lime)}
  46%{height:11px;background:var(--lime)}56%{height:16px;background:var(--lime)}
  62%,92%{height:10px;background:var(--a2)}96%,100%{height:3px;background:var(--s4)}}
@keyframes aExVwf2{0%,14%{height:3px;background:var(--s4)}
  22%{height:18px;background:var(--lime)}34%{height:26px;background:var(--lime)}
  46%{height:15px;background:var(--lime)}56%{height:22px;background:var(--lime)}
  62%,92%{height:10px;background:var(--a2)}96%,100%{height:3px;background:var(--s4)}}
@keyframes aExVwf3{0%,14%{height:3px;background:var(--s4)}
  22%{height:15px;background:var(--lime)}34%{height:22px;background:var(--lime)}
  46%{height:13px;background:var(--lime)}56%{height:19px;background:var(--lime)}
  62%,92%{height:10px;background:var(--a2)}96%,100%{height:3px;background:var(--s4)}}
@keyframes aExVwf4{0%,14%{height:3px;background:var(--s4)}
  22%{height:10px;background:var(--lime)}34%{height:15px;background:var(--lime)}
  46%{height:9px;background:var(--lime)}56%{height:13px;background:var(--lime)}
  62%,92%{height:10px;background:var(--a2)}96%,100%{height:3px;background:var(--s4)}}
@keyframes aExVwf5{0%,14%{height:3px;background:var(--s4)}
  22%{height:8px;background:var(--lime)}34%{height:11px;background:var(--lime)}
  46%{height:7px;background:var(--lime)}56%{height:10px;background:var(--lime)}
  62%,92%{height:10px;background:var(--a2)}96%,100%{height:3px;background:var(--s4)}}
@keyframes aExVwfM1{0%,2%{opacity:1}16%,96%{opacity:0}98%,100%{opacity:1}}
@keyframes aExVwfM2{0%,15%{opacity:0}20%,57%{opacity:1}61%,100%{opacity:0}}
@keyframes aExVwfM3{0%,59%{opacity:0}64%,92%{opacity:1}96%,100%{opacity:0}}

/* barge-in, la marque de coupure. La scene abstraite mesurait le retard et
   gardait la reponse tronquee, celle ci repond a la question que la reponse
   gardee pose, comment le lecteur sait-il qu elle a ete COUPEE et non
   terminee.
   La bulle grandit puis s arrete net a 40. A 42 un trait de coupure et le mot
   apparaissent au bout. Sans eux, une reponse tronquee se lit comme une
   reponse courte, et le lecteur croit que le modele n avait rien de plus a
   dire. La marque est donc ce qui rend la conservation honnete.
   La marque arrive APRES l arret, a 42 contre 40, jamais avant.
   La bulle monte a 62, la marque fait 2 plus 4 d ecart, le total reste sous
   les 108 utiles.
   Gele, on ne pourrait pas distinguer une reponse coupee d une reponse
   breve. */
.ex-barge-in{display:flex;flex-direction:column;gap:7px;width:112px}
.ex-barge-in .one{display:flex;align-items:center;gap:4px;height:10px}
.ex-barge-in .one b{display:block;height:7px;border-radius:4px;width:0}
.ex-barge-in .asst{background:var(--a2);animation:aExBgiAs 4.4s cubic-bezier(.3,0,.2,1) infinite}
.ex-barge-in .user{background:var(--lime);animation:aExBgiUs 4.4s cubic-bezier(.3,0,.2,1) infinite}
/* la coupure arrive apres l arret, jamais avant */
.ex-barge-in .cut{display:block;width:2px;height:10px;border-radius:1px;background:var(--a4);
  opacity:0;flex:0 0 auto;animation:aExBgiCut 4.4s steps(1,end) infinite}
.ex-barge-in .lbl{display:block;font:700 6px/10px var(--mono);color:var(--a4);font-style:normal;
  letter-spacing:0;text-transform:none;opacity:0;white-space:nowrap;
  animation:aExBgiCut 4.4s steps(1,end) infinite}
@keyframes aExBgiAs{0%,7%{width:0}40%,94%{width:62px}96%,100%{width:0}}
@keyframes aExBgiUs{0%,47%{width:0}64%,94%{width:76px}96%,100%{width:0}}
@keyframes aExBgiCut{0%,41%{opacity:0}42%,94%{opacity:1}96%,100%{opacity:0}}

/* ai-presence-glow, la lumiere de bord. La scene abstraite montrait la faute
   d ordre du DOM, celle ci montre l emploi reel qui rend la consigne
   possible, une lumiere sur le CADRE de la fenetre.
   Le halo n est plus autour d un panneau, il est sur le bord de
   l application, a douze pixels du premier mot. C est ce que la consigne
   appelle perceptible en vision peripherique, et c est aussi la seule
   position ou la question du passage par-dessus le texte ne se pose meme
   plus.
   La respiration fait 2 secondes, le fondu de sortie va de 88 a 95, soit 7
   points sur 4 secondes, ce qui vaut 280 millisecondes.
   Le contenu est totalement INERTE, aucune de ses quatre lignes n a
   d animation. C est voulu, le halo doit etre la seule chose qui bouge, sans
   quoi la presence se confondrait avec du travail en cours a l ecran.
   Gele, la fenetre aurait l air eteinte alors que le modele travaille. */
.ex-ai-presence-glow{display:grid;place-items:center;width:124px;height:70px}
.ex-ai-presence-glow .app{position:relative;display:block;width:116px;height:62px;
  border-radius:12px;background:var(--s1);border:1px solid var(--line-2);box-sizing:border-box}
/* sur le bord du cadre, a douze pixels du premier mot */
.ex-ai-presence-glow .edge{position:absolute;inset:-3px;display:block;border-radius:14px;
  box-sizing:border-box;border:3px solid var(--lime);filter:blur(5px);opacity:0;
  animation:aExApgBr 2s ease-in-out infinite,aExApgWin 4s linear infinite,
  aExApgHue 2.6s ease-in-out infinite}
/* inerte, le halo doit etre la seule chose qui bouge */
.ex-ai-presence-glow .ct{position:relative;display:flex;flex-direction:column;gap:6px;
  padding:12px}
.ex-ai-presence-glow .ct i{display:block;height:6px;border-radius:3px;background:var(--ink-3)}
.ex-ai-presence-glow .ct i:nth-child(1){width:88px}
.ex-ai-presence-glow .ct i:nth-child(2){width:66px}
.ex-ai-presence-glow .ct i:nth-child(3){width:78px}
.ex-ai-presence-glow .ct i:nth-child(4){width:52px}
@keyframes aExApgBr{0%,100%{transform:scale(.98)}50%{transform:scale(1.02)}}
/* 7 points sur 4 secondes, soit 280 millisecondes */
@keyframes aExApgWin{0%,4%{opacity:0}12%,88%{opacity:.55}95%,100%{opacity:0}}
@keyframes aExApgHue{0%,100%{border-color:var(--lime)}50%{border-color:var(--a2)}}

/* provenance-marker, le badge ouvert. La scene abstraite prouvait que le
   badge suit l artefact, celle ci prend la deuxieme consigne, deplier sur
   quel modele a tourne ET ce qui a change.
   La definition demande deux informations, la scene en montre donc deux
   lignes, le modele et la modification, pas une seule.
   Le pointeur atteint le badge a 36 et le panneau s ouvre a 40, la cause
   precede l effet, et le pointeur reste jusqu a 78 pour que la fermeture ait
   une cause elle aussi.
   Le badge est a 3 du bord droit d une image de 96, son centre est donc a
   96 moins 3 moins 7, soit 86, et a 3 plus 7, soit 10, plus 8 de padding du
   haut, soit 18. Le coin du pointeur doit atteindre 79 et 11.
   Deux lignes de 6 avec 4 d ecart et 10 de padding font 26.
   Gele, on ne saurait pas que le badge mene quelque part. */
.ex-provenance-marker{position:relative;display:flex;flex-direction:column;gap:6px;width:96px;
  padding-top:8px}
.ex-provenance-marker .art{position:relative;display:block;width:96px;height:40px;
  border-radius:9px;background:linear-gradient(135deg,var(--s3),var(--s1))}
.ex-provenance-marker .bd{position:absolute;right:3px;top:3px;display:block;width:14px;height:14px;
  border-radius:8px;background:var(--a2)}
.ex-provenance-marker .det{display:flex;flex-direction:column;gap:4px;height:0;overflow:hidden;
  padding:0 5px;border-radius:7px;background:var(--s1);box-sizing:border-box;
  animation:aExPrvDet 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-provenance-marker .det i{display:block;height:6px;border-radius:3px;flex:0 0 auto}
/* quel modele a tourne */
.ex-provenance-marker .ml{width:72px;background:var(--a2)}
/* et ce qui a change */
.ex-provenance-marker .ch{width:54px;background:var(--s4)}
.ex-provenance-marker .cur{left:92px;top:64px;animation:aExPrvCur 5s cubic-bezier(.3,0,.2,1) infinite}
/* deux lignes de 6, un ecart de 4 et 10 de padding font 26 */
@keyframes aExPrvDet{0%,39%{height:0;padding-top:0;padding-bottom:0}
  46%,82%{height:26px;padding-top:5px;padding-bottom:5px}
  90%,100%{height:0;padding-top:0;padding-bottom:0}}
@keyframes aExPrvCur{0%,26%{transform:translate(0,0)}36%,78%{transform:translate(-13px,-53px)}
  92%,100%{transform:translate(0,0)}}

/* motion-token, la forme de l indirection. La scene abstraite prouvait
   l effet en le ratant sur un composant, celle ci montre la STRUCTURE, une
   declaration et quatre consommateurs.
   Les quatre s allument AU MEME INSTANT, a 34, pas en cascade. C est le
   point, un token ne propage pas de proche en proche, il est lu par les
   quatre en meme temps. Une cascade decalee dirait exactement le contraire
   et suggererait une propagation, donc un risque de derive.
   Les quatre traits de liaison partent d un point unique et divergent, ce
   qui est la lecture inverse et complementaire de l abstrait, un vers
   plusieurs plutot que plusieurs qui suivent un.
   Les consommateurs font 22 avec 6 d ecart, 4 fois 22 plus 3 fois 6 font
   106. Les traits sont a leurs centres, donc a 11, 39, 67 et 95.
   Gele, on verrait quatre boites et une ligne de texte sans lien entre
   elles. */
.ex-motion-token{display:flex;flex-direction:column;align-items:center;gap:0;width:106px}
.ex-motion-token .decl{display:block;padding:3px 5px;border-radius:5px;background:var(--s2);
  font:700 6px/8px var(--mono);color:var(--lime);font-style:normal;letter-spacing:0;
  text-transform:none}
/* un bus horizontal et quatre descentes. Des barres pivotees ne pouvaient
   pas rejoindre un point a 42 de distance horizontale sur 14 de hauteur.
   Le bus va du premier au dernier consommateur, donc de 11 a 95, soit 84 de
   large avec 11 de decalage. */
.ex-motion-token .fan{position:relative;display:block;width:84px;height:14px;margin-left:11px;
  border-top:1.5px solid var(--s4)}
.ex-motion-token .fan i{position:absolute;top:0;display:block;width:1.5px;height:14px;
  background:var(--s4)}
.ex-motion-token .f1{left:-0.75px}
.ex-motion-token .f2{left:27.25px}
.ex-motion-token .f3{left:55.25px}
.ex-motion-token .f4{left:83.25px}
.ex-motion-token .uses{display:flex;gap:6px}
/* les quatre s allument au meme instant, pas en cascade */
.ex-motion-token .uses i{display:block;width:22px;height:18px;border-radius:5px;
  background:var(--s2);flex:0 0 auto;animation:aExMtkU 4.4s steps(1,end) infinite}
@keyframes aExMtkU{0%,33%{background:var(--s2)}34%,88%{background:var(--lime)}
  92%,100%{background:var(--s2)}}

/* duration-scale, la revue. La scene abstraite montrait le calage et la
   valeur libre, celle ci prend la troisieme consigne, REFUSER la valeur hors
   echelle au moment de la revue.
   240 est signale a 24. A 34 les deux barreaux voisins sont proposes, 250 et
   200, et dans cet ordre, parce que 250 est a 10 de la valeur demandee et
   200 a 40. Proposer le plus proche en premier est la moitie du travail.
   A 58 le pointeur prend 250, la valeur corrigee apparait et le drapeau
   s eteint. Le drapeau qui s eteint est la preuve que la revue a une SORTIE,
   sans quoi elle ne serait qu un reproche.
   La rangee du haut occupe 8 a 18, l ecart va jusqu a 26, les options sont
   donc a 26 sur 12 de haut et leur centre a 32. L option 250 fait 22, son
   centre est a 11. Le coin du pointeur doit atteindre 4 et 25.
   Gele, on verrait un nombre barre sans savoir par quoi le remplacer. */
.ex-duration-scale{position:relative;display:flex;flex-direction:column;gap:8px;width:96px;
  padding-top:8px}
.ex-duration-scale .rw{display:flex;align-items:center;gap:6px;height:10px}
.ex-duration-scale .bad{display:block;font:700 8px/10px var(--mono);color:var(--a4);
  font-style:normal;letter-spacing:0;text-transform:none}
/* il s eteint quand la valeur est corrigee, la revue a une sortie */
.ex-duration-scale .flag{display:block;width:8px;height:8px;border-radius:2px;background:var(--a4);
  opacity:0;flex:0 0 auto;animation:aExDscFlag 5s steps(1,end) infinite}
.ex-duration-scale .opts{display:flex;gap:6px;height:12px}
.ex-duration-scale .opts em{display:block;width:22px;padding:2px 0;border-radius:4px;
  background:var(--s2);font:700 6px/8px var(--mono);color:var(--ink-2);text-align:center;
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;box-sizing:border-box}
/* le plus proche en premier, 250 est a 10 et 200 a 40 */
.ex-duration-scale .o1{animation:aExDscO1 5s steps(1,end) infinite}
.ex-duration-scale .o2{animation:aExDscO2 5s steps(1,end) infinite}
.ex-duration-scale .fixed{display:block;font:700 8px/10px var(--mono);color:var(--lime);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;
  animation:aExDscFix 5s steps(1,end) infinite}
.ex-duration-scale .cur{left:88px;top:70px;animation:aExDscCur 5s cubic-bezier(.3,0,.2,1) infinite}
@keyframes aExDscFlag{0%,23%{opacity:0}24%,57%{opacity:1}58%,100%{opacity:0}}
@keyframes aExDscO1{0%,33%{opacity:0;background:var(--s2)}34%,57%{opacity:1;background:var(--s2)}
  58%,62%{opacity:1;background:var(--lime)}66%,100%{opacity:0;background:var(--s2)}}
@keyframes aExDscO2{0%,33%{opacity:0}34%,57%{opacity:1}66%,100%{opacity:0}}
@keyframes aExDscFix{0%,63%{opacity:0}68%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aExDscCur{0%,44%{transform:translate(0,0)}58%,72%{transform:translate(-84px,-45px)}
  92%,100%{transform:translate(0,0)}}

/* easing-scale, l ensemble reste FERME. La scene abstraite montrait la
   correspondance entre courbe et emploi, celle ci prend la premiere
   consigne et la fait tenir sous pression, que se passe-t-il quand
   quelqu un demande une cinquieme courbe.
   Les quatre emplacements sont pleins et immobiles, ce sont les quatre
   courbes autorisees, et leur immobilite EST la consigne. Un ensemble ferme
   ne bouge pas.
   A 30 une cinquieme apparait, en pointille, la demande sur mesure. A 62
   elle retombe a largeur nulle et le troisieme emplacement pulse, parce que
   la demande a ete repliee sur la courbe la plus proche deja autorisee.
   Refuser sans rediriger serait un mur, la scene montre donc les deux, le
   refus ET la redirection.
   Les emplacements font 20 avec 5 d ecart, 4 font donc 4 fois 20 plus 3 fois
   5, soit 95, et la cinquieme demanderait 120, ce qui ne tient pas dans les
   95 declares. La contrainte est donc aussi geometrique.
   Gele, on ne saurait pas ce qui arrive a la cinquieme demande. */
.ex-easing-scale{display:flex;flex-direction:column;align-items:flex-start;gap:8px;width:95px}
.ex-easing-scale .set{display:flex;gap:5px;height:20px}
/* les quatre autorisees, immobiles, leur immobilite est la consigne */
.ex-easing-scale .set i{display:block;width:20px;height:20px;border-radius:5px;
  background:var(--lime);flex:0 0 auto}
/* la demande sur mesure, refusee puis repliee sur la troisieme */
.ex-easing-scale .c5{width:0;background:none;box-sizing:border-box;
  border:0 dashed var(--a4);animation:aExEscC5 5s cubic-bezier(.3,0,.2,1) infinite}
.ex-easing-scale .c3{animation:aExEscC3 5s ease-out infinite}
.ex-easing-scale .lbls em{display:block;font:700 6px/8px var(--mono);color:var(--lime);
  font-style:normal;letter-spacing:0;text-transform:none}
/* la bordure part de zero aussi, sinon un liseré resterait avant la demande */
@keyframes aExEscC5{0%,29%{width:0;border-width:0;margin-left:-5px}
  36%,61%{width:20px;border-width:1.5px;margin-left:0}
  68%,100%{width:0;border-width:0;margin-left:-5px}}
/* elle absorbe la demande a 62 */
@keyframes aExEscC3{0%,61%{transform:scale(1)}68%{transform:scale(1.18)}
  78%,100%{transform:scale(1)}}

/* semantic-token, la chaine des trois etages. La scene abstraite montrait le
   retheme et le composant qui court-circuite l etage, celle ci montre la
   structure elle meme, et surtout la deuxieme consigne, le composant ne voit
   QUE le premier etage.
   La fenetre lime est sur le premier etage et elle n en bouge jamais. C est
   tout ce qu un composant a le droit de lire. Elle n a aucune animation,
   parce que le jour ou elle bougerait, la regle serait cassee.
   Le jeton de resolution descend de l etage 1 vers 2 a 30, puis vers 3 a 58.
   La resolution se fait donc en bas, dans le systeme, et non dans le
   composant. Les deux fleches marquent les sauts.
   Les trois etages font 14 avec 8 d ecart pour les fleches, la hauteur vaut
   donc 3 fois 14 plus 2 fois 8, soit 58.
   Le nom du premier etage ne contient AUCUN nombre, celui du troisieme n est
   qu un nombre. C est la traduction en une image de ce que les trois etages
   servent a faire.
   Gele, on ne verrait pas dans quel sens la chaine se lit. */
.ex-semantic-token{display:flex;flex-direction:column;align-items:flex-start;gap:0;width:82px}
.ex-semantic-token .ch{position:relative;display:flex;align-items:center;padding:3px 5px;
  border-radius:5px;background:var(--s2);height:14px;box-sizing:border-box}
.ex-semantic-token .ch em{display:block;font:700 6px/8px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;white-space:nowrap}
.ex-semantic-token .t1 em{color:var(--lime)}
.ex-semantic-token .t2 em{color:var(--a2)}
.ex-semantic-token .t3 em{color:var(--ink-2)}
/* tout ce que le composant a le droit de lire, et elle ne bouge jamais */
.ex-semantic-token .win{position:absolute;left:-4px;top:-3px;right:-4px;bottom:-3px;display:block;
  border-radius:7px;box-sizing:border-box;border:1.5px solid var(--lime)}
.ex-semantic-token .ar{display:block;width:8px;height:8px;margin:0 0 0 12px;
  background:var(--s4);clip-path:polygon(0 0,8px 0,4px 8px);opacity:.3}
/* le jeton descend, la resolution se fait dans le systeme */
.ex-semantic-token .a1{animation:aExStkA1 5s steps(1,end) infinite}
.ex-semantic-token .a2{animation:aExStkA2 5s steps(1,end) infinite}
@keyframes aExStkA1{0%,29%{opacity:.3;background:var(--s4)}
  30%,94%{opacity:1;background:var(--lime)}96%,100%{opacity:.3;background:var(--s4)}}
@keyframes aExStkA2{0%,57%{opacity:.3;background:var(--s4)}
  58%,94%{opacity:1;background:var(--lime)}96%,100%{opacity:.3;background:var(--s4)}}

/* transition-token, l expansion au BUILD. La scene abstraite montrait le
   depliage et les alias, celle ci prend la troisieme consigne, l expansion
   en CSS a la compilation.
   En haut la source, trois lignes qui ne contiennent que des NOMS. Elle est
   inerte du premier au dernier pour cent, parce qu une source ne bouge pas,
   c est un fichier.
   A 46 la fleche s allume, a 52 la ligne CSS apparait, et elle contient les
   VALEURS. Des noms entrent, des nombres sortent, et cela se produit une
   fois a la compilation et jamais a l execution. C est pour cela que la
   ligne apparait d un coup et ne se construit pas mot a mot.
   La fleche precede la sortie de six points, sans quoi la sortie n aurait pas
   de cause.
   Gele, on aurait un fichier et une ligne de CSS sans savoir dans quel sens
   ca va. */
.ex-transition-token{display:flex;flex-direction:column;align-items:flex-start;gap:6px;width:130px}
/* la source ne bouge pas, c est un fichier */
.ex-transition-token .src{display:flex;flex-direction:column;gap:4px}
.ex-transition-token .src i{display:block;height:6px;border-radius:3px;background:var(--a2)}
.ex-transition-token .n1{width:78px}
.ex-transition-token .n2{width:62px}
.ex-transition-token .n3{width:86px}
.ex-transition-token .ar{display:block;width:6px;height:8px;margin-left:12px;background:var(--s4);
  clip-path:polygon(0 0,6px 0,3px 8px);animation:aExTtkAr 4.6s steps(1,end) infinite}
/* la sortie apparait d un coup, l expansion a eu lieu a la compilation */
.ex-transition-token .out{display:block;font:700 6px/9px var(--mono);color:var(--lime);
  font-style:normal;letter-spacing:0;text-transform:none;white-space:nowrap;opacity:0;
  animation:aExTtkOut 4.6s steps(1,end) infinite}
@keyframes aExTtkAr{0%,45%{background:var(--s4)}46%,94%{background:var(--lime)}
  96%,100%{background:var(--s4)}}
@keyframes aExTtkOut{0%,51%{opacity:0}52%,94%{opacity:1}96%,100%{opacity:0}}

/* productive-motion, l autre levier. La scene abstraite prouvait le plafond
   par la repetition, celle ci prend la deuxieme consigne, le trajet COURT.
   Les deux rangees ont la MEME duree, 240 millisecondes, soit 5,5 points.
   Seule la distance change, 24 en haut et 90 en bas. Les vitesses valent donc
   4,4 et 16,4 par point, un rapport de 3,7.
   La lecon est la. Quand la duree est plafonnee, la seule variable qui reste
   est la distance, et un long trajet dans un temps court ne peut donner
   qu une ruade. On ne corrige pas cela en allongeant la duree, le plafond
   l interdit, on le corrige en raccourcissant le trajet.
   La piste fait 104 et le mobile 8, la course maximale vaut donc 96, les 90
   de la rangee du bas tiennent donc juste.
   Gele, les deux rangees seraient a des endroits differents et on croirait
   voir deux durees. */
.ex-productive-motion{display:flex;flex-direction:column;gap:8px;width:136px}
.ex-productive-motion .one{display:flex;align-items:center;gap:6px}
.ex-productive-motion em{font:600 6px/8px var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:26px;flex:0 0 auto}
.ex-productive-motion .ok em{color:var(--lime)}
.ex-productive-motion .ko em{color:var(--a4)}
.ex-productive-motion .tk{position:relative;display:block;width:104px;height:8px;
  border-radius:4px;background:var(--s2);flex:0 0 auto}
.ex-productive-motion .kn{position:absolute;left:0;top:0;display:block;width:8px;height:8px;
  border-radius:4px}
/* 24 en 5,5 points, 4,4 par point */
.ex-productive-motion .ok .kn{background:var(--lime);
  animation:aExPmoOk 4.4s cubic-bezier(.3,0,.2,1) infinite}
/* 90 en 5,5 points, 16,4 par point */
.ex-productive-motion .ko .kn{background:var(--a4);
  animation:aExPmoKo 4.4s cubic-bezier(.3,0,.2,1) infinite}
.ex-productive-motion .note{display:block;width:auto;color:var(--ink-3)}
@keyframes aExPmoOk{0%,19%{transform:translateX(0)}24.5%,94%{transform:translateX(24px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExPmoKo{0%,19%{transform:translateX(0)}24.5%,94%{transform:translateX(90px)}
  96%,100%{transform:translateX(0)}}

/* expressive-motion, l ALLOCATION. La scene abstraite montrait la bande et le
   depassement, celle ci prend la deuxieme consigne, une fois par session, et
   la traite comme un budget.
   La bande du haut est une session de douze moments. Onze sont des traits
   bas et INERTES, du motion productif ordinaire. Un seul est haut, et c est
   le seul qui bouge. Le rapport est donc de un sur douze, et il se lit d un
   coup d oeil.
   C est la seule facon de montrer une regle de RARETE. Une scene qui
   montrerait un beau depassement tout seul ne dirait rien de sa rarete, elle
   en ferait meme la promotion.
   Le compteur dit un sur un, la reserve est epuisee. Il s allume APRES le
   moment, a 62 contre 30, parce qu on ne sait qu on a depense qu apres avoir
   depense.
   Les douze traits font 5 avec 4 d ecart, soit 12 fois 5 plus 11 fois 4, 104.
   Gele, on verrait onze traits bas et un haut, ce qui est deja presque tout,
   mais on ne saurait pas lequel est le moment. */
.ex-expressive-motion{display:flex;flex-direction:column;gap:9px;width:104px}
.ex-expressive-motion .ses{display:flex;align-items:flex-end;gap:4px;height:26px}
/* onze moments ordinaires, inertes, ils sont le fond sur lequel le rare se lit */
.ex-expressive-motion .ses i{display:block;width:5px;height:7px;border-radius:2px;
  background:var(--s3);flex:0 0 auto}
/* le seul moment expressif de la session */
.ex-expressive-motion .s8{height:26px;background:var(--lime);transform-origin:50% 100%;
  animation:aExXmoOne 4.6s cubic-bezier(.2,1.4,.3,1) infinite}
.ex-expressive-motion .cnt{display:block;height:9px}
.ex-expressive-motion .cnt em{display:block;font:700 7px/9px var(--mono);color:var(--lime);
  font-style:normal;letter-spacing:0;text-transform:none;opacity:0;
  animation:aExXmoCnt 4.6s ease infinite}
/* 0,27 de 26 font 7, soit exactement la hauteur des onze autres. Au repos il
   leur ressemble, et c est le moment qui le fait grandir. */
@keyframes aExXmoOne{0%,19%{transform:scaleY(.27)}30%{transform:scaleY(1.08)}
  38%,94%{transform:scaleY(1)}96%,100%{transform:scaleY(.27)}}
/* on ne sait qu on a depense qu apres avoir depense */
@keyframes aExXmoCnt{0%,61%{opacity:0}66%,94%{opacity:1}96%,100%{opacity:0}}

/* duration-scalar, le POURQUOI du x3. La scene abstraite montrait les trois
   reglages et le rapport de trois, celle ci montre a quoi le x3 sert
   vraiment, relire une transition trop rapide pour etre jugee.
   La transition a trois phases, un fondu, un deplacement et une pose. En bas,
   a x1, les trois tiennent dans 6 points, soit 264 millisecondes, et elles se
   chevauchent a l oeil. On voit qu il se passe quelque chose sans pouvoir
   dire quoi.
   En haut, a x3, les trois occupent 18 points. Elles se detachent, chacune a
   son tour, et on peut enfin dire laquelle est trop longue.
   Le rapport est exactement de trois, 6 contre 18, ce qui est la garantie que
   le x3 ne deforme rien. Un ralenti qui changerait les proportions ne servirait
   a rien pour juger.
   Gele, il n y aurait aucune phase a lire, et c est le seul cas ou geler la
   scene detruit litteralement l objet du terme. */
.ex-duration-scalar{display:flex;flex-direction:column;gap:8px;width:136px}
.ex-duration-scalar .one{display:flex;align-items:center;gap:6px}
.ex-duration-scalar em{font:800 8px/10px var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:22px;flex:0 0 auto}
.ex-duration-scalar .ok em{color:var(--lime)}
.ex-duration-scalar .ko em{color:var(--a4)}
.ex-duration-scalar .tk{position:relative;display:flex;gap:4px;width:104px;height:10px;
  flex:0 0 auto}
.ex-duration-scalar .tk b{display:block;height:10px;border-radius:3px;background:var(--s2);
  flex:1 1 auto;opacity:.25}
/* dix huit points, les trois phases se detachent */
.ex-duration-scalar .ok .p1{animation:aExDslA1 4.4s steps(1,end) infinite}
.ex-duration-scalar .ok .p2{animation:aExDslA2 4.4s steps(1,end) infinite}
.ex-duration-scalar .ok .p3{animation:aExDslA3 4.4s steps(1,end) infinite}
/* six points, elles se chevauchent a l oeil */
.ex-duration-scalar .ko .p1{animation:aExDslB1 4.4s steps(1,end) infinite}
.ex-duration-scalar .ko .p2{animation:aExDslB2 4.4s steps(1,end) infinite}
.ex-duration-scalar .ko .p3{animation:aExDslB3 4.4s steps(1,end) infinite}
@keyframes aExDslA1{0%,19%{opacity:.25;background:var(--s2)}
  20%,25%{opacity:1;background:var(--lime)}26%,100%{opacity:.25;background:var(--s2)}}
@keyframes aExDslA2{0%,25%{opacity:.25;background:var(--s2)}
  26%,31%{opacity:1;background:var(--lime)}32%,100%{opacity:.25;background:var(--s2)}}
@keyframes aExDslA3{0%,31%{opacity:.25;background:var(--s2)}
  32%,37%{opacity:1;background:var(--lime)}38%,100%{opacity:.25;background:var(--s2)}}
@keyframes aExDslB1{0%,19%{opacity:.25;background:var(--s2)}
  20%,21%{opacity:1;background:var(--a4)}22%,100%{opacity:.25;background:var(--s2)}}
@keyframes aExDslB2{0%,21%{opacity:.25;background:var(--s2)}
  22%,23%{opacity:1;background:var(--a4)}24%,100%{opacity:.25;background:var(--s2)}}
@keyframes aExDslB3{0%,23%{opacity:.25;background:var(--s2)}
  24%,25%{opacity:1;background:var(--a4)}26%,100%{opacity:.25;background:var(--s2)}}

/* distance-duration, la SORTIE. La scene abstraite prouvait la vitesse
   constante sur trois distances, celle ci prend la troisieme consigne, une
   sortie qui vaut la moitie de son entree.
   Le mobile entre sur 60 en 6,8 points, soit 300 millisecondes. Il sort sur
   les memes 60 en 3,4 points, soit 150. Les deux barres du bas mesurent ces
   deux durees, 6,8 et 3,4 sur une echelle de 1,5 par point, donc 10 et 5.
   La sortie va donc deux fois plus vite pour la meme distance, et cela est
   voulu. On regarde une chose arriver, on ne regarde pas une chose partir.
   La regle a l air de contredire la vitesse constante, elle ne la contredit
   pas, elle dit simplement que l attention n est pas symetrique.
   La piste fait 104 et le mobile 10, les 60 tiennent donc largement.
   Gele, on ne verrait ni l entree ni la sortie, seulement une position. */
.ex-distance-duration{display:flex;flex-direction:column;gap:9px;width:104px}
.ex-distance-duration .tk{position:relative;display:block;width:104px;height:10px;
  border-radius:5px;background:var(--s2)}
.ex-distance-duration .kn{position:absolute;left:0;top:0;display:block;width:10px;height:10px;
  border-radius:5px;background:var(--lime);animation:aExDdu 4.4s infinite}
.ex-distance-duration .bars{display:flex;align-items:center;gap:6px;height:6px}
/* 6,8 points a 1,5 par point, soit 10 */
.ex-distance-duration .en{display:block;width:10px;height:6px;border-radius:3px;
  background:var(--lime)}
/* 3,4 points, soit 5, exactement la moitie */
.ex-distance-duration .ex{display:block;width:5px;height:6px;border-radius:3px;
  background:var(--a2)}
/* entree de 6,8 points en decelerant, sortie de 3,4 en accelerant */
@keyframes aExDdu{0%,19%{transform:translateX(0);animation-timing-function:cubic-bezier(0,0,.2,1)}
  25.8%,60%{transform:translateX(60px);animation-timing-function:cubic-bezier(.4,0,1,1)}
  63.4%,100%{transform:translateX(0)}}

/* motion-personality, la signature sur DEUX SURFACES. La scene abstraite
   mesurait le rapport de depassement a deux echelles, celle ci le pose sur
   deux surfaces reelles, un menu et une carte de succes, ce qui est la
   premiere consigne, deux styles nommes.
   Le menu descend de 22 et depasse de 2,2. La carte descend de 44 et depasse
   de 4,4. Dans les deux cas le depassement vaut 10 pour cent de la course.
   Les deux cibles sont des traits horizontaux au bord bas du mobile pose,
   donc a 22 et a 44, et les sommets de depassement tombent a 24,2 et 48,4,
   ce qui tient dans les scenes de 30 et de 52.
   Les durees sont differentes, 12 points pour le menu et 26 pour la carte,
   parce qu un menu est productif et une carte de succes est expressive. Ce
   qui les rend reconnaissables comme un seul produit n est donc ni la duree
   ni la distance, c est le rapport.
   Un lecteur peut le verifier a la regle, 2,2 sur 22 et 4,4 sur 44.
   Gele, on aurait un menu ouvert et une carte posee, sans aucun lien entre
   les deux. */
.ex-motion-personality{display:flex;flex-direction:column;gap:10px;width:120px}
.ex-motion-personality .one{display:flex;align-items:flex-start;gap:6px}
.ex-motion-personality em{font:600 6px/8px var(--mono);color:var(--lime);font-style:normal;
  letter-spacing:0;text-transform:none;width:34px;flex:0 0 auto;padding-top:2px}
.ex-motion-personality .stg{position:relative;display:block;width:78px;flex:0 0 auto;
  overflow:hidden;border-radius:6px;background:var(--s1)}
.ex-motion-personality .a .stg{height:30px}
.ex-motion-personality .b .stg{height:52px}
.ex-motion-personality .mv{position:absolute;left:0;right:0;top:0;display:block;border-radius:6px;
  background:var(--lime)}
/* 22 de course, 2,2 de depassement, soit 10 pour cent */
.ex-motion-personality .a .mv{height:22px;animation:aExMpeA 4.6s cubic-bezier(.3,0,.2,1) infinite}
/* 44 de course, 4,4 de depassement, le meme 10 pour cent */
.ex-motion-personality .b .mv{height:44px;animation:aExMpeB 4.6s cubic-bezier(.3,0,.2,1) infinite}
/* les deux cibles, fixes. Ce sont des positions de REPOS, donc des lignes
   horizontales, au bord bas du mobile pose, soit 22 et 44. */
.ex-motion-personality .tg{position:absolute;left:0;right:0;display:block;height:1.5px;
  background:var(--s5)}
.ex-motion-personality .a .tg{top:22px}
.ex-motion-personality .b .tg{top:44px}
@keyframes aExMpeA{0%,19%{transform:translateY(-22px)}27%{transform:translateY(2.2px)}
  33%,94%{transform:translateY(0)}96%,100%{transform:translateY(-22px)}}
@keyframes aExMpeB{0%,19%{transform:translateY(-44px)}35%{transform:translateY(4.4px)}
  45%,94%{transform:translateY(0)}96%,100%{transform:translateY(-44px)}}

/* motion-audit, la FEUILLE. La scene abstraite montrait le regroupement et
   l ordre, celle ci prend la premiere consigne au mot, une feuille avec le
   declencheur, la duree, la courbe et les proprietes animees.
   Quatre colonnes, parce que la consigne en nomme quatre. Trois lignes se
   remplissent COLONNE PAR COLONNE, a 12, 30, 48 et 66, et non ligne par
   ligne. C est ainsi qu un audit se fait vraiment, on releve la meme chose
   sur toutes les animations avant de passer a la suivante, sinon on change de
   critere en route et le relevé n est plus comparable.
   L en-tete est inerte, ce sont les quatre colonnes, elles sont decidees
   avant de commencer.
   Les colonnes font 20 avec 4 d ecart, soit 4 fois 20 plus 3 fois 4, 92.
   Gele, on aurait un tableau rempli sans savoir dans quel ordre il a ete
   rempli, ce qui est la seule chose que l audit impose. */
.ex-motion-audit{display:flex;flex-direction:column;gap:5px;width:92px}
/* inerte, les quatre colonnes sont decidees avant de commencer */
.ex-motion-audit .hd{display:flex;gap:4px}
.ex-motion-audit .hd i{display:block;width:20px;height:5px;border-radius:2px;background:var(--s4);
  flex:0 0 auto}
.ex-motion-audit .rw{display:flex;gap:4px}
.ex-motion-audit .rw i{display:block;width:20px;height:8px;border-radius:3px;background:var(--s2);
  flex:0 0 auto}
/* colonne par colonne, jamais ligne par ligne */
.ex-motion-audit .c1{animation:aExMauC1 4.6s steps(1,end) infinite}
.ex-motion-audit .c2{animation:aExMauC2 4.6s steps(1,end) infinite}
.ex-motion-audit .c3{animation:aExMauC3 4.6s steps(1,end) infinite}
.ex-motion-audit .c4{animation:aExMauC4 4.6s steps(1,end) infinite}
@keyframes aExMauC1{0%,11%{background:var(--s2)}12%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExMauC2{0%,29%{background:var(--s2)}30%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExMauC3{0%,47%{background:var(--s2)}48%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}
@keyframes aExMauC4{0%,65%{background:var(--s2)}66%,94%{background:var(--a2)}
  96%,100%{background:var(--s2)}}

/* motion-principles, l ANCRAGE. La scene abstraite montrait les paires a
   faire et a ne pas faire et l arbitrage, celle ci prend la deuxieme
   consigne, chaque principe rattache a un token ou a un motif.
   Les trois premiers principes portent une pastille de token a droite, et
   elles s allument a 14, 34 et 54. La quatrieme ligne n en a PAS. Elle est
   en pointille et elle ne s allume jamais.
   C est le defaut qui compte. Un principe sans ancrage se lit tres bien, il
   sonne meme mieux que les autres, mais on ne peut pas le verifier. Rien ne
   dit s il est respecte, et il finira cite dans les deux camps du prochain
   desaccord.
   Les lignes font 8 avec 5 d ecart, la pastille 8, et la rangee 60 plus 5
   plus 8, soit 73.
   Gele, les quatre lignes se ressembleraient, et c est justement pour cela
   qu on oublie d ancrer la quatrieme. */
.ex-motion-principles{display:flex;flex-direction:column;gap:5px;width:76px}
.ex-motion-principles .pr{display:flex;align-items:center;gap:5px;height:8px}
.ex-motion-principles .tx{display:block;width:60px;height:6px;border-radius:3px;
  background:var(--s3);flex:0 0 auto}
.ex-motion-principles .tkn{display:block;width:8px;height:8px;border-radius:3px;
  background:var(--s2);flex:0 0 auto}
.ex-motion-principles .p1 .tkn{animation:aExMprT1 4.6s steps(1,end) infinite}
.ex-motion-principles .p2 .tkn{animation:aExMprT2 4.6s steps(1,end) infinite}
.ex-motion-principles .p3 .tkn{animation:aExMprT3 4.6s steps(1,end) infinite}
/* sans ancrage, elle ne peut rien allumer, et elle ne bouge jamais */
.ex-motion-principles .ko .tx{background:none;box-sizing:border-box;
  border:1px dashed var(--line-2)}
@keyframes aExMprT1{0%,13%{background:var(--s2)}14%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExMprT2{0%,33%{background:var(--s2)}34%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExMprT3{0%,53%{background:var(--s2)}54%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}

/* token-lint, la SORTIE. La scene abstraite arretait la file et la laissait
   arretee, celle ci montre ce que l abstrait avait volontairement retenu, la
   correction et la reprise.
   A 30 la valeur en dur est remplacee par le nom du token. A 40 la barriere
   n a plus rien a refuser, et les trois lignes passent, l une apres l autre,
   a 46, 54 et 62.
   L ordre est tout. Le remplacement precede la reprise de dix points. Une
   file qui repartirait avant la correction dirait qu il suffit d attendre.
   Les trois passent, pas seulement celle qui etait fautive. C est ce que
   coute un lint et ce qu il rapporte, tout est bloque et tout repart
   ensemble.
   Les lignes font 24, la piste 104, et la barriere est a 56.
   Gele, on ne saurait pas si la file est bloquee ou en train de passer. */
.ex-token-lint{position:relative;display:flex;flex-direction:column;gap:7px;width:104px}
.ex-token-lint .rw{position:relative;display:block;height:9px}
.ex-token-lint .rw b{position:absolute;left:0;top:0;display:block;font:700 6px/9px var(--mono);
  font-style:normal;letter-spacing:0;text-transform:none;white-space:nowrap}
.ex-token-lint .bad{color:var(--a4);animation:aExTlnBad 5s steps(1,end) infinite}
.ex-token-lint .good{color:var(--lime);opacity:0;animation:aExTlnGood 5s steps(1,end) infinite}
.ex-token-lint .q{display:flex;flex-direction:column;gap:4px}
.ex-token-lint .q i{display:block;width:24px;height:8px;border-radius:4px;background:var(--lime)}
/* elles passent apres la correction, toutes les trois */
.ex-token-lint .q1{animation:aExTlnQ1 5s cubic-bezier(.4,0,.6,1) infinite}
.ex-token-lint .q2{animation:aExTlnQ2 5s cubic-bezier(.4,0,.6,1) infinite}
.ex-token-lint .q3{animation:aExTlnQ3 5s cubic-bezier(.4,0,.6,1) infinite}
/* la barriere, fixe, elle n a plus rien a refuser */
.ex-token-lint .gate{position:absolute;left:56px;bottom:0;display:block;width:2px;height:32px;
  background:var(--s5)}
@keyframes aExTlnBad{0%,29%{opacity:1}30%,94%{opacity:0}96%,100%{opacity:1}}
@keyframes aExTlnGood{0%,29%{opacity:0}30%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aExTlnQ1{0%,45%{transform:translateX(0)}54%,94%{transform:translateX(80px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExTlnQ2{0%,53%{transform:translateX(0)}62%,94%{transform:translateX(80px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExTlnQ3{0%,61%{transform:translateX(0)}70%,94%{transform:translateX(80px)}
  96%,100%{transform:translateX(0)}}

/* do-not-animate, l ADJACENCE. La scene abstraite montrait les six cas et la
   case a cocher, celle ci prend la premiere consigne au pied de la lettre,
   publier la liste A COTE des tokens.
   Deux colonnes, cote a cote, dans le meme panneau. A gauche ce qui est
   autorise, a droite ce qui ne l est jamais. Les deux colonnes se remplissent
   EN ALTERNANCE, a 12 puis 20 puis 28 et ainsi de suite, parce qu un systeme
   se lit dans les deux sens et qu on choisit un token en sachant ce qui est
   interdit.
   Une liste rangee dans un document de designer serait au meme endroit dans
   le fichier et a des kilometres au moment de la decision. La distance qui
   compte n est pas celle du disque, c est celle du regard.
   Les deux colonnes font 44 avec 8 d ecart, soit 96.
   Gele, on aurait deux colonnes et on ne saurait pas qu elles se lisent
   ensemble. */
.ex-do-not-animate{display:flex;gap:8px;width:96px}
.ex-do-not-animate .col{display:flex;flex-direction:column;gap:4px;width:44px}
.ex-do-not-animate em{display:block;font:700 6px/8px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;margin-bottom:1px}
.ex-do-not-animate .tok em{color:var(--lime)}
.ex-do-not-animate .ban em{color:var(--a4)}
.ex-do-not-animate .col i{display:block;height:6px;border-radius:3px;background:var(--s2)}
/* elles se remplissent en alternance, on lit les deux colonnes ensemble */
.ex-do-not-animate .tok i:nth-child(2){animation:aExDnaT1 4.6s steps(1,end) infinite}
.ex-do-not-animate .ban i:nth-child(2){animation:aExDnaB1 4.6s steps(1,end) infinite}
.ex-do-not-animate .tok i:nth-child(3){animation:aExDnaT2 4.6s steps(1,end) infinite}
.ex-do-not-animate .ban i:nth-child(3){animation:aExDnaB2 4.6s steps(1,end) infinite}
.ex-do-not-animate .tok i:nth-child(4){animation:aExDnaT3 4.6s steps(1,end) infinite}
.ex-do-not-animate .ban i:nth-child(4){animation:aExDnaB3 4.6s steps(1,end) infinite}
.ex-do-not-animate .tok i:nth-child(5){animation:aExDnaT4 4.6s steps(1,end) infinite}
.ex-do-not-animate .ban i:nth-child(5){animation:aExDnaB4 4.6s steps(1,end) infinite}
@keyframes aExDnaT1{0%,11%{background:var(--s2)}12%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaB1{0%,19%{background:var(--s2)}20%,94%{background:var(--a4)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaT2{0%,27%{background:var(--s2)}28%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaB2{0%,35%{background:var(--s2)}36%,94%{background:var(--a4)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaT3{0%,43%{background:var(--s2)}44%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaB3{0%,51%{background:var(--s2)}52%,94%{background:var(--a4)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaT4{0%,59%{background:var(--s2)}60%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExDnaB4{0%,67%{background:var(--s2)}68%,94%{background:var(--a4)}
  96%,100%{background:var(--s2)}}

/* animation-budget, les PHASES. La scene abstraite comparait deux couts et
   montrait la frame perdue, celle ci prend la deuxieme consigne et montre
   POURQUOI, en decomposant le travail d une image.
   En haut, transform et opacity seuls. Une seule phase, composite, 8
   millisecondes. Le compositeur travaille et le fil principal ne fait rien.
   En bas, width. Trois phases, mise en page puis peinture puis composite, 6
   plus 5 plus 3, soit 14. Le cout n est pas plus eleve par malchance, il est
   plus eleve parce qu il y a deux phases de plus, et ces deux phases ne
   peuvent pas etre evitees quand on anime une propriete de mise en page.
   L axe fait 100 pour 16,7 millisecondes, soit 5,99 par milliseconde. Le
   repere des 10 est donc a 60. Les phases du bas mesurent 36, 30 et 18, ce
   qui deborde de 24.
   Les phases s allument dans l ordre, mise en page a 20, peinture a 34,
   composite a 48, parce que ce sont des etapes et non des categories.
   Gele, on verrait deux barres de longueurs differentes sans savoir de quoi
   elles sont faites. */
.ex-animation-budget{display:flex;flex-direction:column;gap:8px;width:136px}
.ex-animation-budget .one{display:flex;align-items:center;gap:6px}
.ex-animation-budget em{font:600 6px/8px var(--mono);font-style:normal;letter-spacing:0;
  text-transform:none;width:30px;flex:0 0 auto}
.ex-animation-budget .ok em{color:var(--lime)}
.ex-animation-budget .ko em{color:var(--a4)}
.ex-animation-budget .ph{display:flex;gap:1px;width:100px;height:9px;flex:0 0 auto}
.ex-animation-budget .ph i{display:block;height:9px;border-radius:2px;flex:0 0 auto;opacity:.2}
/* une seule phase, 8 millisecondes, soit 48 */
.ex-animation-budget .ok .cp{width:48px;background:var(--lime);
  animation:aExAbgP3 4.4s steps(1,end) infinite}
/* trois phases, 6 plus 5 plus 3, soit 36 puis 30 puis 18 */
.ex-animation-budget .ko .ly{width:36px;background:var(--a4);
  animation:aExAbgP1 4.4s steps(1,end) infinite}
.ex-animation-budget .ko .pa{width:30px;background:var(--a4);
  animation:aExAbgP2 4.4s steps(1,end) infinite}
.ex-animation-budget .ko .cp{width:18px;background:var(--a2);
  animation:aExAbgP3 4.4s steps(1,end) infinite}
/* l axe et le repere des 10 millisecondes, inertes, c est la limite declaree */
.ex-animation-budget .ax{position:relative;display:block;width:100px;height:6px;
  margin-left:36px;border-top:1px solid var(--line)}
.ex-animation-budget .mk{position:absolute;left:60px;top:0;display:block;width:1.5px;height:6px;
  background:var(--s5)}
@keyframes aExAbgP1{0%,19%{opacity:.2}20%,94%{opacity:1}96%,100%{opacity:.2}}
@keyframes aExAbgP2{0%,33%{opacity:.2}34%,94%{opacity:1}96%,100%{opacity:.2}}
@keyframes aExAbgP3{0%,47%{opacity:.2}48%,94%{opacity:1}96%,100%{opacity:.2}}

/* motion-budget, l ETAPE et non l ecran. La scene abstraite comptait les
   choses sur un ecran, celle ci prend la deuxieme consigne, les 500
   millisecondes par etape de tache, et une etape traverse plusieurs ecrans.
   Trois ecrans se succedent, a 10, 34 et 58. Chacun respecte le plafond de
   trois elements. Chacun ajoute son attente, 180 puis 160 puis 140
   millisecondes, et la barre du bas les CUMULE, elle ne se remet pas a zero
   entre les ecrans.
   Le total fait 480, ce qui passe sous les 500 de justesse. Trois ecrans
   irreprochables un par un peuvent donc echouer ensemble, et c est la seule
   chose que cette lecture ajoute.
   La barre fait 100 pour 600 millisecondes, soit 0,167 par milliseconde. Les
   trois paliers tombent a 30, 57 et 80, et le repere des 500 est a 83.
   Gele, on verrait une barre remplie sans savoir qu elle additionne trois
   ecrans. */
.ex-motion-budget{display:flex;flex-direction:column;gap:9px;width:100px}
.ex-motion-budget .steps{display:flex;gap:6px}
.ex-motion-budget .sc{position:relative;display:block;width:29px;height:20px;border-radius:5px;
  box-sizing:border-box;border:1px solid var(--line-2)}
.ex-motion-budget .sc b{position:absolute;left:3px;right:3px;top:7px;display:block;height:6px;
  border-radius:3px;background:var(--s2)}
.ex-motion-budget .s1 b{animation:aExMbgS1 4.6s steps(1,end) infinite}
.ex-motion-budget .s2 b{animation:aExMbgS2 4.6s steps(1,end) infinite}
.ex-motion-budget .s3 b{animation:aExMbgS3 4.6s steps(1,end) infinite}
.ex-motion-budget .sum{position:relative;display:block;width:100px;height:7px;border-radius:4px;
  background:var(--s2)}
/* elle cumule, elle ne se remet pas a zero entre les ecrans */
.ex-motion-budget .sum b{display:block;width:0;height:7px;border-radius:4px;background:var(--a2);
  animation:aExMbgSum 4.6s steps(1,end) infinite}
/* le repere des 500, a 83, fixe */
.ex-motion-budget .mk{position:absolute;left:83px;top:-3px;display:block;width:1.5px;height:13px;
  background:var(--s5)}
@keyframes aExMbgS1{0%,9%{background:var(--s2)}10%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExMbgS2{0%,33%{background:var(--s2)}34%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExMbgS3{0%,57%{background:var(--s2)}58%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
/* 180 puis 340 puis 480 millisecondes, soit 30 puis 57 puis 80 */
@keyframes aExMbgSum{0%,9%{width:0}10%,33%{width:30px}34%,57%{width:57px}
  58%,94%{width:80px}96%,100%{width:0}}

/* motion-hierarchy, l ETIQUETTE d abord. La scene abstraite montrait le rang
   dans le mouvement, celle ci prend la premiere consigne et montre le sens de
   lecture, on etiquette puis on en deduit les valeurs.
   Les trois etiquettes s allument a 12, 12 et 12, ENSEMBLE, parce qu on
   classe les trois elements d un coup en regardant la maquette. Les valeurs
   arrivent ensuite, a 34 et a 52, deduites du rang.
   C est l inverse de ce qui se passe d habitude. On choisit d abord des
   durees qui plaisent, puis on cherche une justification. Ici la duree et la
   distance sont des CONSEQUENCES, et la scene le dit par l ordre.
   Les barres de duree valent 40, 26 et 14. Celles de distance 44, 22 et 8.
   Les deux jeux decroissent dans le meme sens, ce qui est la deuxieme
   consigne.
   Gele, on aurait trois lignes de valeurs sans savoir laquelle a decide de
   l autre. */
.ex-motion-hierarchy{display:flex;flex-direction:column;gap:7px;width:112px}
.ex-motion-hierarchy .rw{display:flex;align-items:center;gap:5px;height:8px}
.ex-motion-hierarchy em{display:block;width:38px;font:700 6px/8px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto;opacity:0}
.ex-motion-hierarchy .r1 em::before{content:"primary";color:var(--lime)}
.ex-motion-hierarchy .r2 em::before{content:"secondary";color:var(--a2)}
.ex-motion-hierarchy .r3 em::before{content:"background";color:var(--s4)}
/* les trois etiquettes ensemble, on classe d un coup */
.ex-motion-hierarchy em{animation:aExMhiTag 4.6s steps(1,end) infinite}
.ex-motion-hierarchy .du,.ex-motion-hierarchy .di{display:block;height:6px;border-radius:3px;
  width:0;flex:0 0 auto}
.ex-motion-hierarchy .du{background:var(--lime);animation:aExMhiDu 4.6s cubic-bezier(.3,0,.2,1) infinite}
.ex-motion-hierarchy .di{background:var(--a2);animation:aExMhiDi 4.6s cubic-bezier(.3,0,.2,1) infinite}
/* les valeurs sont deduites du rang, elles arrivent apres */
.ex-motion-hierarchy .r1 .du{--w:40px}
.ex-motion-hierarchy .r2 .du{--w:26px}
.ex-motion-hierarchy .r3 .du{--w:14px}
.ex-motion-hierarchy .r1 .di{--w:44px}
.ex-motion-hierarchy .r2 .di{--w:22px}
.ex-motion-hierarchy .r3 .di{--w:8px}
@keyframes aExMhiTag{0%,11%{opacity:0}12%,94%{opacity:1}96%,100%{opacity:0}}
@keyframes aExMhiDu{0%,33%{width:0}34%,94%{width:var(--w)}96%,100%{width:0}}
@keyframes aExMhiDi{0%,51%{width:0}52%,94%{width:var(--w)}96%,100%{width:0}}

/* motion-spec, la valeur LITTERALE. La scene abstraite comptait les huit
   champs et montrait le lien insuffisant, celle ci prend la deuxieme
   consigne, des nombres et jamais un adjectif.
   En haut le champ dit smooth. C est un mot sur lequel deux personnes ne
   seront jamais d accord, et il ne se dessine pas. La courbe reste donc vide
   jusqu a 40.
   En bas le meme champ dit quatre nombres. A 44 la courbe apparait, tracee
   depuis ces nombres, et elle est la meme pour tout le monde.
   C est la preuve la plus simple du glossaire. Un adjectif ne produit aucun
   trace, quatre nombres en produisent un seul.
   Le trace utilise pathLength 1 pour que le dessin ne depende pas de la
   longueur reelle du chemin.
   Gele, on aurait deux lignes de texte et une courbe, sans savoir laquelle
   des deux l a produite. */
.ex-motion-spec{display:flex;flex-direction:column;gap:6px;width:96px}
.ex-motion-spec .rw{display:flex;align-items:center;gap:5px;height:9px}
.ex-motion-spec em{display:block;width:28px;font:600 6px/9px var(--mono);color:var(--ink-2);
  font-style:normal;letter-spacing:0;text-transform:none;flex:0 0 auto}
.ex-motion-spec .v{display:block;font:700 6px/9px var(--mono);letter-spacing:0;
  text-transform:none;white-space:nowrap}
/* un mot, il ne se dessine pas */
.ex-motion-spec .ko .v{color:var(--a4)}
/* quatre nombres, ils se dessinent */
.ex-motion-spec .ok .v{color:var(--lime)}
.ex-motion-spec .crv{display:block;width:40px;height:28px;margin-left:33px}
.ex-motion-spec svg{display:block;width:40px;height:28px}
.ex-motion-spec path{fill:none;stroke:var(--lime);stroke-width:2;stroke-linecap:round;
  stroke-dasharray:1;stroke-dashoffset:1;animation:aExMspCrv 4.6s cubic-bezier(.4,0,.2,1) infinite}
/* elle n existe qu apres les nombres, a 44 */
@keyframes aExMspCrv{0%,43%{stroke-dashoffset:1}70%,94%{stroke-dashoffset:0}
  96%,100%{stroke-dashoffset:1}}

/* essential-motion, le TROISIEME niveau. La scene abstraite opposait
   l essentiel au decoratif, celle ci prend la troisieme consigne, le motion
   optionnel enferme dans une requete no-preference, qui n est ni l un ni
   l autre.
   Trois rangees, trois comportements DIFFERENTS sous reduced motion, allume
   a 50, et ce sont exactement les trois que la consigne nomme.
   L essentiel garde son signal en FONDU sur place, il porte une information.
   L optionnel passe par un CHANGEMENT D ETAT INSTANTANE, il saute a sa place
   en une image et il y reste. Il avait le droit d exister tant que personne
   ne demandait le contraire, on ne le supprime donc pas, on le degraisse.
   Le decoratif est ABSENT, il ne se produit plus du tout.
   Fondu, saut, absence. Si l optionnel et le decoratif avaient le meme sort,
   la classification en trois niveaux ne servirait a rien.
   La piste fait 68 et le mobile 10, la course de 54 tient donc.
   Gele, les trois rangees se ressembleraient et il n en resterait rien. */
.ex-essential-motion{display:flex;flex-direction:column;gap:7px;width:112px}
.ex-essential-motion .rw{display:flex;align-items:center;gap:5px;height:10px}
.ex-essential-motion em{display:block;width:34px;font:600 6px/10px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto}
.ex-essential-motion .t1 em::before{content:"essential";color:var(--lime)}
.ex-essential-motion .t2 em::before{content:"optional";color:var(--a2)}
.ex-essential-motion .t3 em::before{content:"decorative";color:var(--a4)}
.ex-essential-motion .st{position:relative;display:block;width:68px;height:10px;border-radius:5px;
  background:var(--s2);flex:0 0 auto}
.ex-essential-motion .st b{position:absolute;left:0;top:0;display:block;width:10px;height:10px;
  border-radius:5px}
/* il garde son signal, en fondu sur place */
.ex-essential-motion .t1 b{background:var(--lime);animation:aExEmo1 4.4s cubic-bezier(.3,0,.2,1) infinite}
/* il saute a sa place en une image et il y reste */
.ex-essential-motion .t2 b{background:var(--a2);animation:aExEmo2 4.4s steps(1,end) infinite}
/* absent, il ne se produit plus du tout */
.ex-essential-motion .t3 b{background:var(--a4);animation:aExEmo3 4.4s cubic-bezier(.3,0,.2,1) infinite}
.ex-essential-motion .sw{display:block;align-self:flex-start;padding:2px 5px;border-radius:4px;
  background:var(--s2);font:700 6px/8px var(--mono);color:var(--a2);font-style:normal;
  letter-spacing:0;text-transform:none;opacity:.25;animation:aExEmoSw 4.4s steps(1,end) infinite}
@keyframes aExEmo1{0%,7%{transform:translateX(0);opacity:1}
  22%,44%{transform:translateX(54px);opacity:1}
  50%,58%{transform:translateX(54px);opacity:0}
  70%,94%{transform:translateX(54px);opacity:1}
  96%,100%{transform:translateX(0);opacity:1}}
/* saut a sa place en une image, et il y reste */
@keyframes aExEmo2{0%,7%{transform:translateX(0);opacity:1}
  22%,44%{transform:translateX(54px);opacity:1}
  49%{transform:translateX(0);opacity:0}
  50%,94%{transform:translateX(54px);opacity:1}
  96%,100%{transform:translateX(0);opacity:1}}
@keyframes aExEmo3{0%,7%{transform:translateX(0);opacity:1}
  22%,44%{transform:translateX(54px);opacity:1}
  50%,94%{transform:translateX(0);opacity:0}
  96%,100%{transform:translateX(0);opacity:1}}
@keyframes aExEmoSw{0%,49%{opacity:.25}50%,94%{opacity:1}96%,100%{opacity:.25}}

/* lottie-handoff, les deux CHIFFRES. La scene abstraite tracait la frontiere
   entre l illustratif et l etat d interface, celle ci prend les deux consignes
   chiffrees, les 60 kilo-octets et la derniere image.
   La barre du haut fait 100 pour 100 kilo-octets, soit 1 par kilo-octet. Le
   repere des 60 est donc a 60, et le poids atteint 52. On est sous le budget,
   avec 8 de marge.
   La rangee du milieu dessine le trace en entier, de 8 a 62. La rangee du bas
   est la meme en reduced motion, elle est deja a sa DERNIERE IMAGE des le
   premier pour cent et elle ne se dessine jamais. On garde le resultat et on
   jette le trajet, ce qui est exactement ce que la consigne demande.
   C est la seule facon de montrer un saut a la derniere image, il faut mettre
   les deux etats cote a cote, sinon un trace complet et un trace saute se
   ressemblent une fois termines.
   Le trace utilise pathLength 1.
   Gele, les deux rangees seraient identiques, puisque leur etat final est le
   meme, et c est bien la que le mouvement porte toute l information. */
.ex-lottie-handoff{display:flex;flex-direction:column;gap:8px;width:96px}
.ex-lottie-handoff .wt{position:relative;display:block;width:100px;height:7px;border-radius:4px;
  background:var(--s2)}
/* 52 kilo-octets sur une echelle de 100, le repere des 60 est a 60 */
.ex-lottie-handoff .wt b{display:block;width:52px;height:7px;border-radius:4px;
  background:var(--lime)}
.ex-lottie-handoff .mk{position:absolute;left:60px;top:-3px;display:block;width:1.5px;height:13px;
  background:var(--s5)}
.ex-lottie-handoff .one{display:flex;align-items:center;gap:5px}
.ex-lottie-handoff em{display:block;width:30px;font:600 6px/8px var(--mono);font-style:normal;
  letter-spacing:0;text-transform:none;flex:0 0 auto}
.ex-lottie-handoff .one em{color:var(--lime)}
.ex-lottie-handoff .rd em{color:var(--a2)}
.ex-lottie-handoff .stg{display:block;width:60px;height:20px;flex:0 0 auto}
.ex-lottie-handoff svg{display:block;width:60px;height:20px}
.ex-lottie-handoff path{fill:none;stroke:var(--lime);stroke-width:2.5;stroke-linecap:round;
  stroke-dasharray:1}
/* il se dessine en entier */
.ex-lottie-handoff .one path{stroke-dashoffset:1;animation:aExLhoDraw 5s cubic-bezier(.4,0,.2,1) infinite}
/* deja a sa derniere image, il ne se dessine jamais */
.ex-lottie-handoff .rd path{stroke-dashoffset:0;stroke:var(--a2)}
@keyframes aExLhoDraw{0%,7%{stroke-dashoffset:1}62%,94%{stroke-dashoffset:0}
  96%,100%{stroke-dashoffset:1}}

/* smart-animate, la TRANSCRIPTION. La scene abstraite montrait que
   l appariement se fait par le nom, celle ci prend la troisieme consigne et
   ferme la boucle du glossaire, les valeurs doivent etre recopiees A LA MAIN
   dans la fiche, parce que l outil ne sait pas enregistrer une courbe sur
   mesure.
   Le prototype joue, a gauche, de 10 a 34. Puis la fleche s allume a 44, et
   les deux champs de la fiche se remplissent a 50 et 62. Un par un, apres
   coup, et par une main.
   L ordre dit tout. Rien ne part du prototype vers la fiche tout seul. Si la
   fleche s allumait en meme temps que le prototype, on croirait a un export.
   Les deux champs sont la duree et l easing, les deux seules choses que
   l outil connait et ne peut pas transmettre.
   Gele, on aurait un prototype et une fiche remplie sans savoir qui a
   recopie. */
.ex-smart-animate{display:flex;flex-direction:column;align-items:flex-start;gap:6px;width:88px}
.ex-smart-animate .proto{position:relative;display:block;width:80px;height:20px;border-radius:5px;
  box-sizing:border-box;border:1px solid var(--line-2)}
.ex-smart-animate .proto i{position:absolute;top:4px;display:block;width:10px;height:10px;
  border-radius:3px}
/* le fantome du depart, en pointille, sans quoi il serait invisible sous le
   mobile au premier pour cent */
.ex-smart-animate .a{left:4px;background:none;box-sizing:border-box;
  border:1.5px dashed var(--s4)}
/* il joue, mais rien n en sort tout seul */
.ex-smart-animate .b{left:4px;background:var(--lime);animation:aExSmaMv 4.6s cubic-bezier(.3,0,.2,1) infinite}
.ex-smart-animate .ar{display:block;width:6px;height:8px;margin-left:14px;background:var(--s4);
  clip-path:polygon(0 0,6px 0,3px 8px);animation:aExSmaAr 4.6s steps(1,end) infinite}
.ex-smart-animate .sheet{display:flex;flex-direction:column;gap:4px}
.ex-smart-animate .fd{display:flex;align-items:center;gap:4px;height:6px}
.ex-smart-animate .fd i{display:block;width:22px;height:6px;border-radius:3px;background:var(--s4);
  flex:0 0 auto}
.ex-smart-animate .fd b{display:block;width:44px;height:6px;border-radius:3px;background:var(--s2)}
/* recopies a la main, un par un, apres coup */
.ex-smart-animate .f1 b{animation:aExSmaF1 4.6s steps(1,end) infinite}
.ex-smart-animate .f2 b{animation:aExSmaF2 4.6s steps(1,end) infinite}
/* 62 de course, 80 moins 4 de marge de chaque cote moins 10 du mobile */
@keyframes aExSmaMv{0%,9%{transform:translateX(0)}34%,94%{transform:translateX(62px)}
  96%,100%{transform:translateX(0)}}
@keyframes aExSmaAr{0%,43%{background:var(--s4)}44%,94%{background:var(--lime)}
  96%,100%{background:var(--s4)}}
@keyframes aExSmaF1{0%,49%{background:var(--s2)}50%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
@keyframes aExSmaF2{0%,61%{background:var(--s2)}62%,94%{background:var(--lime)}
  96%,100%{background:var(--s2)}}
