/* ==========================================================================
   fx/morphing-text.css — habillage du composant « liquid / morphing text »
   Port vanilla du morphing-text de Magic UI (registry/magicui/morphing-text.tsx).
   Palette Fragment440 : #f0efec sur #090909, rien d'autre.
   Aucune taille de police imposée : tout hérite de l'élément hôte.
   ========================================================================== */

/* L'élément hôte garde son display, sa typo, son alignement, sa couleur. */
.mtx-host {
  /* Rien d'imposé volontairement : l'hôte reste ce que le site en fait.
     En particulier PAS de couleur : une valeur en dur ici (le blanc cassé du
     site) rendait le texte illisible sur contact.html, qui est une page à fond
     clair — le titre morphé sortait en blanc sur #d0cfcd. La couleur doit
     venir de la page, toujours. */
}

.mtx {
  display: inline-block;
  max-width: 100%;
}

/* Le vrai texte : présent dans le DOM, indexable, lu par les lecteurs
   d'écran. Masqué visuellement sans display:none ni visibility:hidden. */
.mtx__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Scène : porte le filtre SVG « gooey » et sert de repère de position
   pour les deux calques superposés. */
.mtx__stage {
  position: relative;
  display: inline-block;
  max-width: 100%;
  /* filter est posé en JS (référence absolue url(<href>#id) pour Safari). */
}

/* Fantôme de dimensionnement : le mot le plus long, invisible mais dans le
   flux — c'est lui qui donne sa hauteur et sa largeur au bloc, à n'importe
   quelle taille de police. Aucune hauteur codée en dur. */
.mtx__sizer {
  display: block;
  visibility: hidden;
  pointer-events: none;
  user-select: none;
  white-space: pre;
}

/* Les deux calques de texte qui se croisent. */
.mtx__layer {
  position: absolute;
  inset: 0;
  display: block;
  white-space: pre;
  color: inherit;
  will-change: filter, opacity;
}

/* Second calque invisible tant que l'animation n'a pas démarré. */
.mtx__layer + .mtx__layer {
  opacity: 0;
}

/* --------------------------------------------------------------------------
   prefers-reduced-motion : le texte reste net, lisible et fixe.
   Ceinture ET bretelles — le JS coupe déjà le morphing, le CSS garantit
   qu'aucun flou ni aucune opacité intermédiaire ne subsiste, même si une
   feuille de style tierce ou un état résiduel traînait.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .mtx__stage {
    filter: none !important;
  }
  .mtx__layer {
    filter: none !important;
    transition: none !important;
    animation: none !important;
    will-change: auto;
  }
  .mtx__layer:first-of-type {
    opacity: 1 !important;
  }
  .mtx__layer + .mtx__layer {
    opacity: 0 !important;
  }
}

/* Même garantie via l'attribut posé par le JS, pour les moteurs qui
   n'évaluent pas la media query au bon moment. */
.mtx-host[data-mtx-mode="static"] .mtx__stage,
.mtx-host[data-mtx-mode="cycle"] .mtx__stage {
  filter: none !important;
}
.mtx-host[data-mtx-mode="static"] .mtx__layer,
.mtx-host[data-mtx-mode="cycle"] .mtx__layer {
  filter: none !important;
}

/* --------------------------------------------------------------------------
   Repli sans filtre SVG : le composant fonctionne quand même (fondu flouté).
   Rien à faire ici — le JS n'applique simplement pas le filtre.
   -------------------------------------------------------------------------- */
@supports not (filter: url("#x")) {
  .mtx__stage { filter: none; }
}

/* ==========================================================================
   MODE « REVEAL » (défaut depuis v3) — apparition unique, puis repos
   --------------------------------------------------------------------------
   Ce mode ne reconstruit AUCUN DOM : le JS pose un `filter` et une `opacity`
   en style inline sur l'élément hôte, puis les retire. Il n'y a donc presque
   rien à faire ici — seulement deux filets de sécurité.
   L'état est lisible dans l'attribut data-mtx-reveal :
     pending  = en attente d'entrer dans le viewport — AUCUN style posé, c'est
                du texte net et normal tant qu'il n'est pas armé
     running  = matérialisation en cours
     done     = terminé, plus aucun style posé par le composant. L'élément reste
                sous l'œil : il rejouera son apparition s'il repasse de caché à
                visible (survol, section rouverte, rentrée dans le viewport).
   ========================================================================== */

/* Filet 1 — on ne promeut en couche que pendant l'effet, c'est-à-dire sur
   "running" SEULEMENT.
   Ni "done" (l'élément doit redevenir un texte ordinaire, net et
   sélectionnable), ni "pending" : un élément en attente ne peint rien, et sur
   musique.html ils sont jusqu'à 202 à la fois — 202 couches promues pour rien,
   dont le coût de compositing participait au blocage du rendu constaté à
   l'arrivée sur la page. Le JS pose de toute façon `will-change` en style
   inline au moment exact où il commence à peindre, et le retire à la fin. */
[data-mtx-reveal="running"] {
  will-change: filter;
}

/* Filet 1 bis — état « masked » (v6) : masqué AVANT la première peinture,
   démarrage différé au lever du rideau d'ouverture. Le composant a déjà posé
   son filtre en style inline ; on neutralise seulement une transition du site
   qui, sinon, interpolerait ce filtre et rendrait le masquage progressif —
   donc visible. Le masquage doit être instantané ou il n'a aucun intérêt. */
[data-mtx-reveal="masked"] {
  will-change: filter;
  transition: none !important;
}

/* Filet 2 — mouvement réduit : aucun effet, texte net immédiatement.
   Volontairement limité aux états pending/running : sur "done" le composant
   n'a plus rien posé, et un !important y écraserait un filtre du site. */
@media (prefers-reduced-motion: reduce) {
  [data-mtx-reveal="pending"],
  [data-mtx-reveal="masked"],
  [data-mtx-reveal="running"] {
    filter: none !important;
    opacity: 1 !important;
    will-change: auto !important;
  }
}
