/* ===== rotate.css — mot qui défile dans un titre =====
 *
 * Reprise fidèle de « Dia Text » (21st.dev / @animbits) en CSS + JS natifs,
 * sans React ni motion. Le composant d'origine fait trois choses à la fois,
 * et c'est leur superposition qui donne l'effet :
 *   1. le mot sortant glisse vers le HAUT, le suivant arrive du BAS ;
 *   2. les deux traversent un flou de 4 px et un fondu, mais sur 0,2 s
 *      seulement — quatre fois plus court que le glissement, si bien que le
 *      mot est déjà net quand il finit de se placer ;
 *   3. une fois posé, un dégradé BALAYE le mot de droite à gauche puis se
 *      retire, laissant le texte à sa couleur normale.
 * Le sortant part AVANT que l'entrant n'arrive (le `mode="wait"` de
 * AnimatePresence) : jamais deux mots à l'écran en même temps.
 *
 * ⚠️ Le glissement n'est pas une courbe de Bézier mais un vrai RESSORT
 * (`stiffness: 300, damping: 30, mass: 1`). Amorti à ζ = 0,866, il dépasse
 * sa cible de 0,4 % puis revient — c'est ce micro-rebond qui fait la
 * différence avec un `ease-out`, qui arrive « mou ». La courbe ci-dessous a
 * été échantillonnée sur l'équation du ressort, d'où le `linear()`.
 * `cubic-bezier` en repli : aucune Bézier ne peut dépasser 1 puis revenir,
 * donc les navigateurs sans `linear()` (Safari < 17.2) ont un simple ease-out.
 */

.rot {
  /* le mot est en absolu par-dessus un fantôme qui, lui, tient la place */
  position: relative;
  display: inline-block;
  overflow: hidden;          /* c'est lui qui coupe le mot pendant qu'il glisse */
  vertical-align: bottom;
  /* 1ch et non 2ch comme l'original : la fonte de titre est large, 2ch valait
     84 px et « VTT » (60 px) traînait derrière lui un vide qui décentrait
     visiblement le titre. La largeur réelle vient de toute façon du JS. */
  min-width: 1ch;
  /* ⚠️ Pas de `max-width: 100%` ici. Sur un inline-block posé en fin de ligne,
     le pourcentage se résout sur l'espace RESTANT de la ligne, pas sur la
     largeur du titre : « Randonnée » se retrouvait plafonné à 84 px et coupé
     par l'`overflow: hidden` ci-dessus. Le plafonnement se fait donc en JS,
     où la boîte de référence est explicite. */
  /* la largeur est posée en pixels par rotate.js (le fantôme donnerait un
     saut sec à chaque mot) : on l'anime pour que le début de phrase glisse
     au lieu de sauter */
  transition: width 0.42s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Fantôme : invisible mais DANS le flux — il donne sa hauteur à la boîte
   (le mot réel étant en absolu, sans lui la ligne s'effondrerait). */
.rot-ghost {
  visibility: hidden;
  white-space: nowrap;
}

.rot-word {
  position: absolute;
  inset: 0;
  white-space: nowrap;
  transform: translateY(0);
  opacity: 1;
  filter: blur(0);
  transition:
    transform 0.51s var(--rot-spring, cubic-bezier(0.22, 1, 0.36, 1)),
    opacity 0.2s linear,
    filter 0.2s linear;
}

/* Position de DÉPART du mot entrant : posée sans transition, puis retirée à
   la frame suivante — c'est ce qui déclenche le glissement vers le repos. */
.rot-word.is-enter {
  transform: translateY(100%);
  opacity: 0;
  filter: blur(4px);
  transition: none;
}

/* Sortie : vers le haut, le mot suivant viendra du bas. */
.rot-word.is-exit {
  transform: translateY(-100%);
  opacity: 0;
  filter: blur(4px);
}

/* --- Balayage du dégradé, une fois le mot posé ---
   Le texte devient une fenêtre sur un dégradé 2,5× plus large que lui. À
   `100%` on voit sa partie colorée, à `0%` sa partie `currentColor` : le mot
   passe donc de coloré à sa couleur normale, et non l'inverse. La classe est
   retirée à la fin de l'animation pour rendre au texte son rendu habituel
   (ombre portée du titre comprise). */
.rot-word.is-sweep {
  background-image: var(--rot-sweep,
    linear-gradient(90deg, currentColor 50%, #ef8f6e 50%, #ffb199, #ffcdb2, #7bc6c9));
  background-size: 250% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: rot-sweep 0.8s ease-in-out 0.1s both;
}
@keyframes rot-sweep {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}

@supports (transition-timing-function: linear(0, 1)) {
  .rot-word {
    --rot-spring: linear(0.0000 0.00%, 0.0548 4.17%, 0.1774 8.33%, 0.3236 12.50%,
      0.4680 16.67%, 0.5970 20.83%, 0.7051 25.00%, 0.7914 29.17%, 0.8577 33.33%,
      0.9069 37.50%, 0.9421 41.67%, 0.9664 45.83%, 0.9826 50.00%, 0.9929 54.17%,
      0.9990 58.33%, 1.0024 62.50%, 1.0039 66.67%, 1.0043 70.83%, 1.0041 75.00%,
      1.0036 79.17%, 1.0030 83.33%, 1.0023 87.50%, 1.0018 91.67%, 1.0013 95.83%,
      1.0009 100.00%);
  }
}

/* Accessibilité : plus de glissement ni de flou, le mot se contente de
   fondre. Le balayage, lui, est purement décoratif — il saute entièrement. */
@media (prefers-reduced-motion: reduce) {
  .rot { transition: none; }
  .rot-word { transition: opacity 0.2s linear; }
  .rot-word.is-enter,
  .rot-word.is-exit { transform: none; filter: none; }
  .rot-word.is-sweep { animation: none; }
}
