/* ====================================================================
   animate.css : la mécanique du skill /animate, et rien d'autre.

   Aucune couleur, aucune forme de marque : la page hôte a sa charte. Les
   deux couleurs de la bascule passent par des propriétés que l'hôte pose
   (--anim-nappe, --anim-mot-survol). Toutes les classes sont préfixées
   `anim-` pour ne rien heurter.

   Rien ici ne cache quoi que ce soit : les états de départ sont posés par
   animate.js, seulement quand GSAP est là et que le mouvement est permis.
   Sans script, tout est lisible. Seule exception, voulue : le squelette
   d'`attente`, qui n'est pas du contenu, attend son délai avant de
   paraître.

   Les nombres de ce fichier sont des REPLIS : la valeur de référence est
   dans REGLAGES (animate.js), qui la publie sur <html> en propriété
   personnalisée au chargement. Le repli ne sert qu'à une page où le script
   ne s'exécute pas, et doit rester égal à REGLAGES.
   ==================================================================== */

/* -------------------------------------------------------- flip, revele */
/* `.anim-mot` est insécable : ce sont les espaces entre les mots, restées
   des nœuds de texte, qui portent les retours à la ligne. La coupure du
   titre découpé est celle du texte d'origine. `.anim-car` tourne autour de
   son bord inférieur : il sort de sous la ligne au lieu de pivoter sur son
   milieu. Le masque de la ligne (découpe, perspective) est posé en ligne
   par le moteur, d'après REGLAGES.

   `revele` ne prend que `.anim-mot`, sans ligne ni caractère : ses mots
   gardent la coupure du texte, seule leur couleur change au défilement. */
.anim-ligne { display: block; }
.anim-mot { display: inline-block; white-space: nowrap; }
.anim-car {
  display: inline-block;
  transform-origin: 50% 100%;
  will-change: transform, opacity;
}

/* ------------------------------------------------------ compte, rouleau */
/* Chiffres tabulaires en permanence, pas seulement pendant la course :
   sinon la largeur sauterait au moment où le texte d'origine revient. */
[data-anim~="compte"],
[data-anim~="rouleau"] { font-variant-numeric: tabular-nums; }

.anim-compte,
.anim-rouleau { white-space: nowrap; }

/* Le fantôme porte la valeur finale, invisible : il donne la largeur et la
   ligne de base. La valeur qui compte se cale dessus, par la droite. */
.anim-compte { display: inline-grid; }
.anim-compte > * { grid-area: 1 / 1; }
.anim-fantome { visibility: hidden; }
.anim-valeur { justify-self: end; }

/* Une roue par chiffre : la bande de chiffres est en absolu sur le
   fantôme, la découpe la réduit à une ligne. Une découpe plutôt qu'un
   débordement caché : `overflow` sur un inline-block déplacerait sa ligne
   de base sur son bord inférieur. */
.anim-roue {
  position: relative;
  display: inline-block;
  clip-path: inset(0);
}
.anim-bande {
  position: absolute;
  inset: 0 0 auto 0;
  text-align: center;
  will-change: transform;
}
.anim-bande > span { display: block; }

/* Pour les lecteurs d'écran seulement : la valeur finale, lue une fois. */
.anim-lecteur {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------- zoom */
/* Le cadre rogne : l'image grandit dedans sans pousser la mise en page. */
[data-anim~="zoom"] { overflow: hidden; }

/* -------------------------------------------------------------- recul */
/* Le héros reste collé en haut, la page monte par-dessus. Les sections qui
   suivent doivent avoir un fond opaque : c'est à la page hôte de le poser. */
[data-anim~="recul"] {
  position: sticky;
  top: 0;
  z-index: 0;
}

/* ------------------------------------------------------------ bascule */
[data-anim~="bascule"] {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

/* La nappe part de la flèche et grandit jusqu'à couvrir le bouton. Le
   rayon de 140 % couvre le coin opposé quel que soit le libellé : à 100 %,
   la référence CSS vaut sqrt(l² + h²) / sqrt(2), moins que la diagonale
   réelle depuis un centre excentré. Sans couleur fournie par l'hôte, la
   nappe prend le texte du bouton à 14 % : un repli visible qui ne rend
   jamais le libellé illisible. */
.anim-nappe {
  position: absolute;
  inset: var(--anim-nappe-retrait, 0px);
  z-index: -1;
  border-radius: inherit;
  background: var(--anim-nappe, color-mix(in srgb, currentColor 14%, transparent));
  clip-path: circle(0 at var(--anim-nappe-origine, calc(100% - 1.5em) 50%));
  pointer-events: none;
}
[data-anim~="bascule"]:hover .anim-nappe,
[data-anim~="bascule"]:focus-visible .anim-nappe,
[data-anim~="bascule"].anim-actif .anim-nappe {
  clip-path: circle(140% at var(--anim-nappe-origine, calc(100% - 1.5em) 50%));
}

.anim-libelle { display: inline-flex; flex: 0 0 auto; white-space: nowrap; }
[data-anim~="bascule"]:hover .anim-libelle,
[data-anim~="bascule"]:focus-visible .anim-libelle,
[data-anim~="bascule"].anim-actif .anim-libelle { color: var(--anim-mot-survol, currentColor); }

/* Une lettre est une fenêtre d'une ligne sur une pile de deux copies : la
   pile monte d'une demi-hauteur et la seconde copie prend la place. La
   seconde copie est un ::after, jamais un doublon dans le document. Chaque
   copie porte un talon sous sa ligne : le jambage d'un « p » de la première
   copie, qui déborde de sa ligne dans les polices à grande descente,
   tombe dans ce talon au lieu de pointer dans la fenêtre de la seconde. */
.anim-lettre {
  display: inline-block;
  overflow: hidden;
  height: 1.2em;
  line-height: 1.2;
}
.anim-pile { display: block; }
.anim-copie,
.anim-pile::after { display: block; padding-bottom: 0.3em; }
.anim-pile::after { content: attr(data-lettre); }

/* La flèche se décale en CSS, pas en GSAP : un CDN injoignable la
   laisserait sinon immobile. `translate` et non `transform`, pour ne jamais
   se battre avec une transformation posée par l'hôte. */
[data-anim~="bascule"]:hover .anim-fleche,
[data-anim~="bascule"]:focus-visible .anim-fleche,
[data-anim~="bascule"].anim-actif .anim-fleche {
  translate: var(--anim-fleche-x, 2px) var(--anim-fleche-y, -2px);
}

/* ------------------------------------------------------------ souleve */
/* `translate` et `scale`, propriétés individuelles : une carte qui porte
   aussi `montee` est déplacée par GSAP sur `transform`, et les deux ne se
   marchent pas dessus. Le survol n'existe que sur un appareil qui survole ;
   `.anim-actif` le déclenche à la main (démo, slide). */
@media (hover: hover) {
  [data-anim~="souleve"]:hover { translate: 0 var(--anim-souleve-y, -4px); }
  [data-anim~="souleve"]:hover img { scale: var(--anim-souleve-image, 1.03); }
}
[data-anim~="souleve"].anim-actif { translate: 0 var(--anim-souleve-y, -4px); }
[data-anim~="souleve"].anim-actif img { scale: var(--anim-souleve-image, 1.03); }

/* ----------------------------------------------------------- souligne */
/* Le trait est un dégradé de fond entre deux bords, `debut` et `fin`,
   transitionnés à part (animate.js explique le cas du retour). Un fond et
   non un ::after : sur un lien coupé en plusieurs lignes, le fond court
   sur toutes ses lignes à la suite, là où un pseudo-élément n'en
   couvrirait qu'une. C'est l'exception à la règle 4 : deux propriétés
   enregistrées qui repeignent le fond du lien, sans mise en page. Sans
   @property, le trait change d'état sans course.

   Hors d'un <nav>, un trait de repos reste sous le lien, en partie de la
   couleur du texte : un lien de paragraphe ne se distingue pas par sa
   seule couleur (WCAG 1.4.1). Le trait tracé le recouvre au survol. Un
   lien en bloc porte son trait au bas de sa boîte : c'est son
   `padding-bottom` qui règle l'écart. */
@property --anim-souligne-debut { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --anim-souligne-fin { syntax: "<percentage>"; inherits: false; initial-value: 0%; }

[data-anim~="souligne"] {
  --anim-souligne-debut: 100%;
  --anim-souligne-fin: 0%;
  text-decoration: none;
  background-image:
    linear-gradient(to right, transparent var(--anim-souligne-debut), currentColor var(--anim-souligne-debut) var(--anim-souligne-fin), transparent var(--anim-souligne-fin)),
    linear-gradient(color-mix(in srgb, currentColor var(--anim-souligne-repos, 40%), transparent) 0 0);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% var(--anim-souligne-epaisseur, 1px);
}
nav [data-anim~="souligne"] { background-size: 100% var(--anim-souligne-epaisseur, 1px), 0 0; }

[data-anim~="souligne"]:focus-visible,
[data-anim~="souligne"].anim-actif { --anim-souligne-debut: 0%; --anim-souligne-fin: 100%; }
@media (hover: hover) {
  [data-anim~="souligne"]:hover { --anim-souligne-debut: 0%; --anim-souligne-fin: 100%; }
}

/* ------------------------------------------------------------ attente */
/* Le squelette de chargement d'une web app. Seul état caché posé par le
   CSS, et c'est voulu : il n'apparaît qu'après un délai, pour qu'une
   réponse rapide ne le fasse jamais clignoter ; passé ce délai, il est là
   même sans script. Les blocs prennent une part de la couleur du texte
   (ou de --anim-attente-encre), mêlée au fond par la transparence : aucun
   gris codé en dur, lisible en thème clair comme sombre.

   Un seul reflet traverse tout le squelette : chaque bloc porte une nappe
   aussi large que le squelette, qu'il rogne à sa forme. Le reflet glisse
   en `transform`, du même pas sur des blocs de toutes largeurs.

   Balisage : le conteneur qui attend porte aria-busy="true", le squelette
   aria-hidden="true", ses blocs la classe anim-bloc. La page leur donne
   taille et arrondi. */
[data-anim~="attente"] {
  position: relative;
  animation: anim-attente-apparition var(--anim-attente-apparition, 0.2s) ease-out var(--anim-attente-delai, 0.3s) both;
}
@keyframes anim-attente-apparition {
  from { opacity: 0; }
}
.anim-bloc {
  display: block;
  background: color-mix(in srgb, var(--anim-attente-encre, currentColor) var(--anim-attente-teinte, 8%), transparent);
  clip-path: border-box;
}
.anim-bloc::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 35%, color-mix(in srgb, var(--anim-attente-encre, currentColor) var(--anim-attente-eclat, 6%), transparent) 50%, transparent 65%);
  transform: translateX(-100%);
  pointer-events: none;
}
@keyframes anim-attente-reflet {
  to { transform: translateX(100%); }
}

/* ------------------------------------------------------------- relais */
/* L'élément qui voyage d'une page à l'autre. Son nom est posé par
   animate.js, sa course suit REGLAGES. Le navigateur anime le groupe
   (place et taille de l'instantané) dans sa couche de transition, hors
   de la mise en page du document. Sans View Transitions entre
   documents, la navigation reste normale. */
::view-transition-group(*.anim-relais) {
  animation-duration: var(--anim-relais-duree, 0.55s);
  animation-timing-function: var(--anim-relais-courbe, cubic-bezier(0.32, 0.72, 0, 1));
}

/* ------------------------------------------------------ transitions CSS */
/* Déclarées seulement quand le mouvement est permis : sous mouvement
   réduit, les survols changent d'état sans course. */
@media (prefers-reduced-motion: no-preference) {
  .anim-nappe {
    transition: clip-path var(--anim-nappe-duree, 0.42s) var(--anim-nappe-courbe, cubic-bezier(0.32, 0.72, 0, 1));
  }
  .anim-libelle {
    transition: color var(--anim-nappe-duree, 0.42s) var(--anim-nappe-courbe, cubic-bezier(0.32, 0.72, 0, 1));
  }
  .anim-fleche {
    transition: translate var(--anim-nappe-duree, 0.42s) var(--anim-nappe-courbe, cubic-bezier(0.32, 0.72, 0, 1));
  }
  [data-anim~="souleve"],
  [data-anim~="souleve"] img {
    transition:
      translate var(--anim-souleve-duree, 0.265s) var(--anim-souleve-courbe, cubic-bezier(0.24, 0.6, 0.28, 1)),
      scale var(--anim-souleve-duree, 0.265s) var(--anim-souleve-courbe, cubic-bezier(0.24, 0.6, 0.28, 1));
  }

  /* souligne, au repos : le début part vers la droite ; la fin attend la
     fin de sa course pour revenir, le trait déjà invisible. */
  [data-anim~="souligne"] {
    transition:
      --anim-souligne-debut var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1)),
      --anim-souligne-fin 0s var(--anim-souligne-duree, 0.3s);
  }
  /* Actif : la fin avance depuis la gauche ; le début y saute, le trait
     encore invisible. */
  [data-anim~="souligne"]:focus-visible,
  [data-anim~="souligne"].anim-actif {
    transition:
      --anim-souligne-fin var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1)),
      --anim-souligne-debut 0s;
  }
  /* Retour pendant la sortie : le début revient en glissant. */
  [data-anim~="souligne"].anim-retour:focus-visible,
  [data-anim~="souligne"].anim-retour.anim-actif {
    transition:
      --anim-souligne-fin var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1)),
      --anim-souligne-debut var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1));
  }
  @media (hover: hover) {
    [data-anim~="souligne"]:hover {
      transition:
        --anim-souligne-fin var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1)),
        --anim-souligne-debut 0s;
    }
    [data-anim~="souligne"].anim-retour:hover {
      transition:
        --anim-souligne-fin var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1)),
        --anim-souligne-debut var(--anim-souligne-duree, 0.3s) var(--anim-souligne-courbe, cubic-bezier(0.32, 0.72, 0, 1));
    }
  }

  .anim-bloc::after {
    animation: anim-attente-reflet var(--anim-attente-reflet, 1.6s) linear infinite;
    will-change: transform;
  }
}

/* ------------------------------------------------------------ passage */
/* Transition entre deux pages du même site : l'ancienne s'efface, la
   nouvelle monte. Actif dès que les deux pages chargent ce fichier. Les
   deux keyframes sont nommées pour qu'une page puisse les rejouer sur un
   groupe à elle (la démo le fait sur deux mini-pages). */
@view-transition { navigation: auto; }

@keyframes anim-passage-sortie {
  to { opacity: 0; }
}
@keyframes anim-passage-entree {
  from { opacity: 0; transform: translateY(var(--anim-passage-y, 12px)); }
}

::view-transition-old(root) {
  animation: anim-passage-sortie var(--anim-passage-sortie, 0.2s) var(--anim-passage-sortie-courbe, cubic-bezier(0.4, 0, 0.2, 1)) both;
}
::view-transition-new(root) {
  animation: anim-passage-entree var(--anim-passage-entree, 0.45s) var(--anim-passage-entree-courbe, cubic-bezier(0.23, 1, 0.32, 1)) both;
}

/* ------------------------------------------------- ceinture du mouvement réduit */
/* Sous mouvement réduit, le moteur n'arme rien. Cette ceinture couvre le
   cas restant : la préférence change après l'armement. Rien ne reste
   caché, découpé ou déformé, et les nombres montrent leur valeur finale. */
@media (prefers-reduced-motion: reduce) {
  /* `derive` est nommée en plus de la marque : sur un élément qui porte aussi
     `glisse`, c'est la glisse qui pose la marque et qui la retire en
     arrivant, alors que la dérive tient encore son `transform`. */
  [data-anim-arme],
  [data-anim~="derive"],
  .anim-car {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
  /* Une forme SVG en cours de tracé : son tiret couvre toute la forme.
     Un attribut à part : `transform: none` écraserait l'attribut
     `transform` d'une forme SVG. */
  [data-anim-trait] { stroke-dashoffset: 0 !important; }
  /* Un mot de `revele` que le moteur a atténué reprend la couleur de son
     paragraphe. Un attribut à part, là encore : la ceinture ne touche pas à
     la couleur des autres éléments armés, qui ont la leur. */
  [data-anim-muet] { color: inherit !important; }
  /* Le squelette garde son délai, qui n'est pas un mouvement, mais
     paraît d'un coup et sans reflet. */
  [data-anim~="attente"] { animation-duration: 0.001s; }
  .anim-bloc::after { display: none; }
  .anim-ligne { clip-path: none !important; }
  [data-anim~="recul"] { position: static; }
  [data-anim~="souleve"],
  [data-anim~="souleve"] img,
  .anim-fleche { translate: none !important; scale: none !important; }
  .anim-compte,
  .anim-rouleau { display: none; }
  .anim-lecteur {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    white-space: inherit;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
