/* ═══════════════════════════════════════════════════════════════════
   LE PLAN DE TRAVAIL — la grammaire de mouvement du site
   ───────────────────────────────────────────────────────────────────
   CE FICHIER NE PORTE QUE DES GESTES. Pas une couleur, pas une taille,
   pas un selecteur de section. Il declare COMMENT une chose entre, sort
   ou se deplace ; ce qui entre est decide ailleurs.

   ───────────────────────────────────────────────────────────────────
   POURQUOI IL EXISTE
   ───────────────────────────────────────────────────────────────────
   Compte fait avant de l'ecrire : le site avait HUIT facons distinctes
   de faire entrer un element — scramble, typing, fu, maskRevealUp,
   maskRevealLeft, clipReveal, clipRevealLeft, fadeBlur — plus six
   classes `reveal-*`. Aucune ne disait la meme chose deux fois.

   Un visiteur apprend la grammaire d'un site en trente secondes, sans
   s'en rendre compte : il voit deux entrees, il devine la troisieme. A
   huit vocabulaires il n'en devine aucune, et le mouvement cesse d'etre
   une signature pour devenir du bruit. Le site ne manquait pas
   d'animations. Il manquait d'une seule.

   ───────────────────────────────────────────────────────────────────
   TROIS GESTES, ET LA LISTE EST FERMEE
   ───────────────────────────────────────────────────────────────────
   Comme la palette (treize couleurs) et les polices (trois familles),
   cette liste ne s'allonge pas. Un quatrieme vocabulaire ne s'AJOUTE
   pas : il en REMPLACE un — et s'il ne remplace rien, c'est qu'il n'a
   pas de travail.

   POSER    L'element est depose, il ne se fond pas. Masque, course
            courte, arret net. C'est le geste par defaut du site.
   DECOUPER Revelation par lame : un clip-path traverse. Pour les
            images et les plaques, ou une translation ferait glisser une
            photo au lieu de la reveler.
   GLISSER  La surface passe sous un cadre fixe. Scenes epinglees,
            travelling, parallaxe. Le seul geste ou la PAGE bouge.

   IL Y EN A EU QUATRE PENDANT UN JOUR. Le quatrieme s'appelait
   ECLAIRER : le vert traversait un element, sur l'idee — juste — que
   la palette le decrit comme « une source de lumiere, pas une couleur
   de remplissage ». Il a ete retire faute de travail.

   Cherche ou le poser, il n'y avait aucune place ou il faisait autre
   chose que decorer. Le CTA principal est DEJA vert : une lumiere
   verte dessus ne se voit pas. Sur les cartes du portfolio, le survol
   porte deja une levee, une echelle, un assombrissement de l'image,
   une meta qui monte et un filet accent — un balayage de plus aurait
   ete l'effet gratuit par excellence. Dans la decomposition de la
   cover, l'animation CSS aurait tourne sur son horloge au milieu
   d'une scene pilotee par le scroll : le defaut que tl.call() avait
   deja cause la.

   L'IDEE SURVIT LA OU ELLE A UNE FONCTION. `data-trace` fait courir
   le vert le long du bord de la coupe : il ne decore pas, il marque
   ou la lame est passee. Et la fiche du dispositif 01 porte depuis
   toujours sa `.fiche-brillance`, une lumiere qui suit le curseur —
   faite a la main, liee a un objet, justifiee par lui.

   Un geste declare et jamais employe est exactement le poids mort que
   ce fichier retire ailleurs. Trois, comme les trois polices.

   ───────────────────────────────────────────────────────────────────
   UNE COURBE, PARCE QU'ELLE ETAIT DEJA LA
   ───────────────────────────────────────────────────────────────────
   Releve dans mdg.css avant d'inventer quoi que ce soit :
   cubic-bezier(.16,1,.3,1) compte 104 emplois sur 116. Elle EST deja la
   courbe du site ; elle n'etait simplement pas nommee, donc les douze
   autres se sont installees sans que personne ne s'en apercoive.

   Elle est nommee ici. Deux exceptions gardees, parce qu'elles disent
   autre chose et le disent bien :
     --ease-lame  (.76,0,.18,1)   part vite, finit net. Le chargeur
                                  l'utilise deja pour se lever.
     --ease-appui (.34,1.2,.64,1) depasse puis revient. Le retour
                                  physique d'un bouton qu'on presse.

   ───────────────────────────────────────────────────────────────────
   MOUVEMENT REDUIT
   ───────────────────────────────────────────────────────────────────
   Le site n'honore plus prefers-reduced-motion, sur decision du
   proprietaire — voir la note en tete de mdg.css. Ce fichier suit la
   meme regle et ne pose aucun garde-fou : en poser un ici rendrait le
   site incoherent avec lui-meme, moitie fige moitie anime.
   ═══════════════════════════════════════════════════════════════════ */

:root{
  /* ── Les courbes ─────────────────────────────────────────────── */
  --ease-pose:cubic-bezier(.16,1,.3,1);
  --ease-lame:cubic-bezier(.76,0,.18,1);
  --ease-appui:cubic-bezier(.34,1.2,.64,1);

  /* ── L'echelle de durees ──────────────────────────────────────
     Quatre valeurs, comme une echelle typographique. Une duree qui
     n'est pas dans cette liste est une duree que personne n'a choisie.

     --t-reflexe  le retour d'un geste. Sous 120ms l'oeil ne lit pas un
                  mouvement, il lit une reaction : c'est ce qu'on veut
                  d'un survol.
     --t-court    un element qui se pose.
     --t-moyen    un groupe qui se pose, ou une lame qui traverse.
     --t-long     une scene courte, non pilotee par le scroll. */
  --t-reflexe:.12s;
  --t-court:.34s;
  --t-moyen:.62s;
  --t-long:1.05s;

  /* LE CRAN DE COURSE. Toute translation d'entree part de cette valeur,
     et d'aucune autre. Elle est petite EXPRES : un element qui arrive
     de 40px a l'air de tomber, un element qui arrive de 14px a l'air
     d'etre pose. Le site est deja plein de vide, le mouvement n'a pas
     besoin d'en traverser davantage. */
  --course:14px;
}

/* ═══════════════════════════════════════════════════════════════════
   AUCUN GESTE NE CACHE QUOI QUE CE SOIT AVANT QUE LE MOTEUR N'EXISTE
   ───────────────────────────────────────────────────────────────────
   TOUT CE QUI SUIT EST SOUS `.motion-arme`, une classe que motion.js
   pose sur <html> a sa premiere ligne. C'est la precaution la plus
   importante du fichier, et elle merite d'etre dite avant les gestes.

   Un geste d'entree cache d'abord et revele ensuite. Si la feuille
   cachait toute seule, il suffirait que le script ne parte pas pour
   que la page reste vide : elle serait la, lisible par les moteurs de
   recherche, invisible pour un visiteur.

   Ce risque n'est pas theorique ici. accueil.js fait 7 939 lignes en
   un seul fichier, et son propre en-tete previent qu'une exception
   dans un bloc arrete tous les suivants. Un fichier de 410 Ko peut
   aussi arriver tronque sur un reseau lent. Dans les deux cas la
   classe n'est pas posee, aucune regle de depart ne s'applique, et le
   site s'ouvre sur son contenu — immobile, mais entier.

   La regle est donc : le CSS ne cache jamais de son propre chef ; il
   ne cache que sur invitation du script qui saura reveler.
   ═══════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════
   01 · POSER
   ───────────────────────────────────────────────────────────────────
   L'ETAT D'ARRIVEE EST L'ETAT NATUREL : `.est-pose` n'ajoute rien, il
   RETIRE le decalage de depart. Ecrire l'arrivee comme une absence de
   regle evite qu'une valeur finale se mette un jour a diverger de la
   valeur naturelle de l'element.
   ═══════════════════════════════════════════════════════════════════ */
.motion-arme [data-poser]{
  opacity:0;
  transform:translate3d(0,var(--course),0);
  /* `scale` EST DANS LA LISTE PARCE QU'UN ELEMENT POSABLE EST SOUVENT
     CLIQUABLE. Les huit questions de la FAQ portent data-poser ET
     repondent a l'appui : sans cette propriete ici, la regle d'appui
     plus bas — moins specifique, (0,1,0) contre (0,2,0) — se faisait
     ecraser par celle-ci, et les questions s'enfoncaient d'un coup
     sans relachement. Deux regles a moi qui se battaient, ce qui est
     la seule sorte de conflit qu'une feuille de grammaire n'a aucune
     excuse de laisser passer. */
  transition:opacity var(--t-court) var(--ease-pose),
             transform var(--t-moyen) var(--ease-pose),
             scale var(--t-court) var(--ease-appui);
  /* LE RETARD SE LIT DANS --rang, pose par le script au moment de
     l'observation : le CSS ne sait pas compter ses freres, et
     :nth-child() se serait trompe des qu'un element est masque par
     media-requete — la variante mobile d'une mise en page desktop garde
     son rang dans l'arbre mais n'a pas de boite.

     80ms entre deux : assez pour lire une sequence, pas assez pour
     attendre le dernier. A six elements le groupe entier est pose en
     400ms. */
  transition-delay:calc(var(--rang, 0) * 80ms);
}
.motion-arme [data-poser].est-pose{opacity:1;transform:none}

/* Pose depuis le cote. Pour ce qui borde une colonne — etiquettes,
   numeros, filets — ou une montee lirait comme un decalage vertical de
   plus dans une page qui en compte deja. */
.motion-arme [data-poser="cote"]{transform:translate3d(calc(var(--course) * -1),0,0)}

/* ═══════════════════════════════════════════════════════════════════
   02 · DECOUPER
   ───────────────────────────────────────────────────────────────────
   La lame passe, l'image reste. C'est la difference avec POSER, et elle
   compte : une photo qui monte de 14px GLISSE, elle ne se revele pas.
   La lame ne deplace rien — elle decouvre ce qui etait deja a sa place.

   clip-path plutot que overflow + transform sur un enfant : deux noeuds
   au lieu d'un, et le compositeur traite un inset anime comme un
   masque, sans repeindre la photo a chaque image.
   ═══════════════════════════════════════════════════════════════════ */
.motion-arme [data-couper]{
  clip-path:inset(0 0 100% 0);
  transition:clip-path var(--t-moyen) var(--ease-lame),
             scale var(--t-court) var(--ease-appui);
  transition-delay:calc(var(--rang, 0) * 80ms);
}
.motion-arme [data-couper].est-coupe{clip-path:inset(0 0 0% 0)}

/* La lame par la gauche : pour ce qui se lit dans le sens de la
   lecture — un titre en bandeau, un filet, une legende. */
.motion-arme [data-couper="gauche"]{clip-path:inset(0 100% 0 0)}
.motion-arme [data-couper="gauche"].est-coupe{clip-path:inset(0 0% 0 0)}

/* LA LAME QUI LAISSE UNE TRACE. Le vert suit le bord de la coupe et
   s'efface derriere lui. C'est le seul endroit ou DECOUPER et ECLAIRER
   se rencontrent, et c'est reserve aux entrees de scene : la palette
   n'autorise qu'UN aplat vert par ecran, une lame verte sur chaque
   image en ferait douze.

   Sur ::after et non sur un noeud : la trace ne doit jamais compter
   dans la mise en page ni etre lue par un lecteur d'ecran. */
.motion-arme [data-couper][data-trace]{position:relative}
.motion-arme [data-couper][data-trace]::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--accent);
  transform:scaleX(0);transform-origin:left;
  opacity:0;pointer-events:none;
}
.motion-arme [data-couper][data-trace].est-coupe::after{
  animation:trace-lame var(--t-long) var(--ease-lame) forwards;
}
@keyframes trace-lame{
  0%  {transform:scaleX(0);opacity:1}
  55% {transform:scaleX(1);opacity:1}
  100%{transform:scaleX(1);opacity:0}
}

/* ═══════════════════════════════════════════════════════════════════
   03 · GLISSER
   ───────────────────────────────────────────────────────────────────
   Le seul geste ou la surface bouge sous un cadre fixe. Il est PILOTE
   PAR LE SCROLL, donc il vit dans motion.js et pas ici : une timeline
   scrubee ecrit ses transforms elle-meme, une transition CSS ne ferait
   que se battre avec elle.

   Ce qui reste ici, c'est le cadre : ce qui deborde ne doit pas
   deborder.

   PAS DE will-change EN DUR. Il est pose par le script a l'entree dans
   l'ecran et retire a la sortie — voir la note en pied de fichier.
   ═══════════════════════════════════════════════════════════════════ */
[data-glisser-cadre]{overflow:clip;position:relative}

/* ═══════════════════════════════════════════════════════════════════
   LA DISCIPLINE DU will-change
   ───────────────────────────────────────────────────────────────────
   POSE PAR LE SCRIPT AVANT LE MOUVEMENT, RETIRE APRES. Jamais en dur
   dans une regle qui vaut pour toute la page.

   Un will-change permanent reserve une couche de composition pour la
   duree de la visite. Sur une page de 22 549 px, quelques dizaines
   d'elements ainsi marques tiennent en memoire video plusieurs fois la
   surface de l'ecran, et le telephone rend le reste plus lentement —
   exactement l'inverse de ce qu'on cherchait en l'ecrivant.

   La classe est posee par motion.js le temps du geste, et retiree a
   `transitionend`. C'est le seul endroit du site ou will-change
   apparait pour les gestes POSER, DECOUPER et GLISSER.
   ═══════════════════════════════════════════════════════════════════ */
.en-mouvement{will-change:transform,opacity,clip-path}

/* ═══════════════════════════════════════════════════════════════════
   L'APPUI — le seul geste que le doigt rend, et il manquait
   ───────────────────────────────────────────────────────────────────
   COMPTE FAIT AVANT D'ECRIRE : le site portait seize etats :active, et
   les seize appartenaient au lecteur video plein ecran et au menu
   mobile. Aucun bouton de la page n'en avait. On survolait, ca se
   soulevait ; on cliquait, rien ne se passait jusqu'a ce que la page
   suivante arrive.

   C'est le defaut de retour le plus courant et le plus facile a
   corriger : entre le clic et la reponse du site il y a toujours un
   delai — le reseau, une ancre, un panneau qui s'ouvre — et pendant ce
   delai, rien ne dit que le clic a ete pris.

   ───────────────────────────────────────────────────────────────────
   DEUX COURBES, PARCE QU'APPUYER ET RELACHER NE SONT PAS LE MEME GESTE
   ───────────────────────────────────────────────────────────────────
   L'APPUI EST IMMEDIAT. --t-reflexe, 120 ms, en lineaire. Sous ce
   seuil l'oeil ne lit pas un mouvement mais une reaction, et c'est
   exactement ce qu'on veut : le bouton doit avoir deja repondu au
   moment ou le doigt s'en apercoit. Une courbe d'accompagnement ici
   ferait « mou » — la matiere ne met pas 300 ms a s'enfoncer.

   LE RELACHEMENT DEPASSE. --ease-appui, la seule courbe du site qui
   depasse sa cible avant d'y revenir. C'est ce qui donne la sensation
   de matiere : un objet qu'on relache ne s'arrete pas net a sa
   position de repos, il la franchit un peu. La transition est portee
   par l'etat NORMAL et non par :active — c'est ce qui permet aux deux
   sens d'avoir chacun sa courbe.

   ───────────────────────────────────────────────────────────────────
   `scale` ET NON `transform`, ET C'EST LA CLEF
   ───────────────────────────────────────────────────────────────────
   `scale` est une propriete a part entiere, distincte de `transform`,
   et le navigateur COMPOSE les deux. Un bouton qui porte deja
   `transform:translateY(-2px)` au survol garde donc sa levee et prend
   l'appui par-dessus, sans qu'on ait a reecrire la levee ici.

   Ecrit en `transform:scale(.97)`, il aurait fallu repeter dans chaque
   regle d'appui la translation du survol correspondant — sinon
   l'element retombait au clic. Et il aurait fallu battre
   `#hero.nhero .nh-btn--p:hover`, qui vaut (1,2,0) : une guerre de
   specificite pour un effet de 3 %.
   ═══════════════════════════════════════════════════════════════════ */
.motion-arme :where(
  .nh-btn, .lc-cta, .pass-cta, .svc-cta, .scta-btn, .mob-cta,
  .cov-arrow, .xp-rail-f, .port-new-tab-btn, .xp-t,
  .work-card, .cov-card, .faq-item2
){
  transition:scale var(--t-court) var(--ease-appui);
}
.motion-arme :where(
  .nh-btn, .lc-cta, .pass-cta, .svc-cta, .scta-btn, .mob-cta,
  .cov-arrow, .xp-rail-f, .port-new-tab-btn, .xp-t
):active{
  scale:.97;
  transition:scale var(--t-reflexe) linear;
}

/* LES SURFACES S'ENFONCENT MOINS QUE LES BOUTONS. Une carte de 360 px
   qui prend 3 % se deplace de onze pixels sur chaque bord : ce n'est
   plus un appui, c'est un recul. A 0,6 % elle bouge de deux pixels,
   ce qui suffit — l'appui se lit a la vitesse, pas a la distance. */
.motion-arme :where(.work-card, .cov-card, .faq-item2):active{
  scale:.994;
  transition:scale var(--t-reflexe) linear;
}

/* ══════════════════════════════════════════════════════════════════
   LA SURBRILLANCE D'APPUI DU NAVIGATEUR
   ──────────────────────────────────────────────────────────────────
   AU DOIGT, LE NAVIGATEUR PEINT SON PROPRE RECTANGLE. Sur mobile, un
   appui sur un lien declenche -webkit-tap-highlight-color : un aplat
   translucide pose sur la BOITE de l'element. Sur un bouton
   rectangulaire, personne ne le remarque. Sur un disque, le rectangle
   deborde des quatre cotes — et ce qu'on voit, ce n'est pas un retour
   d'appui, c'est la FORME de l'element qui change.

   C'est ce qui arrivait au badge de la section « La preuve ». Vu sur
   telephone, jamais sur ordinateur : aucune mesure faite au pointeur
   ne pouvait le montrer, et j'en ai fait trois avant de comprendre
   que la capture venait d'un mobile.

   POURQUOI GLOBALE ET PAS UNE DE PLUS. mdg.css neutralise deja cette
   surbrillance DIX-NEUF FOIS, chaque fois sur un selecteur precis :
   le lecteur video, les onglets, les cartes, les resumes... Dix-neuf
   fois la meme decision, prise dix-neuf fois. Tout element ajoute
   depuis echappe a la liste — c'est exactement ce qui vient
   d'arriver, et ca se reproduira au suivant.

   Le retour d'appui du site ne disparait pas avec elle : il y a
   l'echelle de 3 % plus haut, et l'onde au clic. Ce qui part est le
   rectangle du navigateur, qui ne ressemble a rien de ce que le site
   dessine.
   ══════════════════════════════════════════════════════════════════ */
.motion-arme :where(a, button, summary, label, input, select, textarea,
                    [role="button"], [role="tab"], [tabindex]){
  -webkit-tap-highlight-color:transparent;
}

/* ── LA CONTRAINTE QUE CET APPUI POSE AU RESTE DU SITE ────────────
   UNE REGLE QUI DECLARE `transition` REMPLACE LE RACCOURCI ENTIER, et
   perd donc le `scale` pose ici. Trois regles de mdg.css coiffaient un
   element cliquable avec leur propre liste — #hero.nhero .nh-btn,
   #hero.nhero .lc-cta — et leurs boutons s'enfoncaient sans se
   relacher. Elles recitent maintenant `scale`.

   Corrige la-bas et non ici : ces regles valent (1,2,0), les battre
   depuis cette feuille aurait demande un ID ou un !important. Une
   feuille de grammaire qui gagne par la force apprend a tout le monde
   que les exceptions se reglent par la force.

   DONC : un nouveau bouton qui declare son propre `transition` doit y
   ajouter `scale var(--t-court) var(--ease-appui)`, ou n'en declarer
   aucun et laisser celui d'ici s'appliquer. */

/* :where() NE PESE RIEN, ET C'EST VOULU. Il met toute la liste a une
   specificite de (0,1,0) — celle de `.motion-arme` seule. Une regle
   propre a un bouton, ecrite ailleurs dans mdg.css, l'emporte donc
   toujours : ces valeurs sont un plancher commun, pas une mainmise. */
