/*
 * BAZA-S301 — EFFETS VISUELS SECRETS DE MESSAGERIE (couche additive).
 *
 * POURQUOI UNE FEUILLE `public/` ET PAS `src/index.css` : la feuille compilée
 * `assets/index-*.css` est GELÉE (invariant S141, empreinte épinglée). Servie
 * par `<link>` depuis `public/`, cette feuille n'est jamais fusionnée par Vite :
 * l'empreinte gelée reste intacte, ce que la porte S141 vérifie.
 *
 * TOUTES LES CLASSES SONT NEUVES (`s301fx-*`) : aucune règle existante n'est
 * redéfinie, donc aucun risque de cascade sur une surface non concernée.
 *
 * GARANTIES TENUES ICI (mandat S301 §5) :
 *   · couche `pointer-events: none` — l'effet n'intercepte JAMAIS un geste, et
 *     ne peut donc pas rendre un bouton « mort » (piège S296) ;
 *   · animations `transform` / `opacity` UNIQUEMENT — aucune propriété qui
 *     déclenche un recalcul de mise en page ; la mise en page ne bouge pas ;
 *   · durée bornée à 2,2 s (plafond du contrat partagé : 2,5 s) ;
 *   · aucun média distant, aucune police, aucune image : que des glyphes et des
 *     formes CSS — donc AUCUNE requête réseau supplémentaire ;
 *   · aucun clignotement rapide : les opacités varient lentement et une seule
 *     fois (aucun risque photosensible) ;
 *   · `prefers-reduced-motion: reduce` — TOUTE animation est neutralisée ici,
 *     et le composant rend à la place une confirmation STATIQUE discrète.
 */

/* ------------------------------------------------------------------ couche */

.s301fx-layer {
  position: fixed;
  inset: 0;
  /* NON INTERACTIVE : la couche est purement décorative. */
  pointer-events: none;
  /* Au-dessus du fil, sous les surfaces système ; jamais au-dessus d'un modal. */
  z-index: 60;
  overflow: hidden;
  /* Isole la composition : les animations n'invalident rien au-dehors. */
  contain: strict;
}

.s301fx-item {
  position: absolute;
  /* Origine commune : bas-centre pour les montées, recalée par effet. */
  left: 50%;
  top: 50%;
  display: block;
  line-height: 1;
  will-change: transform, opacity;
  opacity: 0;
  /* Point de départ commun — chaque effet ne redéfinit QUE sa trajectoire. */
  transform: translate3d(-50%, -50%, 0);
}

/* --------------------------------------------------------------- firework */
/* Gerbe : des points partent du centre, ralentissent et s'éteignent. */

.s301fx-firework .s301fx-item {
  /* 12 px, et non 10. Les points ne rétrécissent PLUS en vol (voir la fin de
     `s301fx-spark`) : la recette visuelle §8 montrait, à 320 ms, dix-huit
     pastilles de six pixels disposées en anneau — c'est-à-dire l'image exacte
     d'un indicateur de chargement, pas d'une gerbe. Un effet de fête qui se
     lit comme une attente est un effet raté. */
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--s301fx-teinte, #ffce54);
  animation: s301fx-spark 1.5s cubic-bezier(0.15, 0.7, 0.3, 1) forwards;
  animation-delay: var(--s301fx-retard, 0ms);
}

@keyframes s301fx-spark {
  0% {
    opacity: 0;
    transform: translate3d(-50%, -50%, 0) scale(0.3);
  }
  12% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate3d(
        calc(-50% + var(--s301fx-dx, 0px)),
        calc(-50% + var(--s301fx-dy, 0px)),
        0
      )
      scale(0.85);
  }
}

/* ------------------------------------------------------------------ plane */
/* Un seul avion traverse l'écran, légèrement incliné. */

.s301fx-plane .s301fx-item {
  color: #3d7fbf; /* même raison que les cœurs : glyphe TEXTE, couleur peinte ici */
  font-size: 34px;
  animation: s301fx-fly 2.2s linear forwards;
}

@keyframes s301fx-fly {
  0% {
    opacity: 0;
    transform: translate3d(-70vw, -50%, 0) rotate(-8deg);
  }
  10%,
  88% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate3d(70vw, calc(-50% - 8vh), 0) rotate(-8deg);
  }
}

/* ----------------------------------------------------------------- hearts */
/* Des cœurs montent du bas en ondulant très légèrement. */

.s301fx-hearts .s301fx-item {
  font-size: 26px;
  /* COULEUR EXPLICITE — et non l'emoji du système.
     Le glyphe U+2764 sans sélecteur de variante est rendu par la plupart des
     polices comme un caractère TEXTE monochrome : la recette visuelle §8 l'a
     photographié en gris foncé, ce qui donnait à un effet de fête l'allure
     d'un faire-part. On aurait pu ajouter U+FE0F pour forcer la présentation
     emoji, mais l'apparence dépendrait alors de la police de l'appareil, qui
     varie d'un téléphone à l'autre. Peindre nous-mêmes la couleur rend le même
     résultat partout, sans aucun téléchargement. */
  color: #e0457b;
  animation: s301fx-rise 2.1s ease-out forwards;
  animation-delay: var(--s301fx-retard, 0ms);
}

@keyframes s301fx-rise {
  0% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px)), 45vh, 0) scale(0.6);
  }
  15% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px) * 1.4), -45vh, 0) scale(1);
  }
}

/* ------------------------------------------------------------------ stars */
/* Pluie d'étoiles : descente lente depuis le haut, scintillement UNIQUE. */

.s301fx-stars .s301fx-item {
  color: #e8a900; /* même raison : U+2726 est un symbole texte, pas un emoji doré */
  font-size: 20px;
  animation: s301fx-fall 2s ease-in forwards;
  animation-delay: var(--s301fx-retard, 0ms);
}

@keyframes s301fx-fall {
  0% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px)), -48vh, 0) scale(0.7);
  }
  18% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px)), 48vh, 0) scale(1);
  }
}

/* ---------------------------------------------------------------- bubbles */
/* Bulles colorées translucides qui remontent et se dissipent. */

.s301fx-bubbles .s301fx-item {
  width: var(--s301fx-taille, 22px);
  height: var(--s301fx-taille, 22px);
  border-radius: 50%;
  background: var(--s301fx-teinte, rgba(88, 196, 220, 0.45));
  border: 1px solid rgba(255, 255, 255, 0.5);
  animation: s301fx-float 2.2s ease-in-out forwards;
  animation-delay: var(--s301fx-retard, 0ms);
}

@keyframes s301fx-float {
  0% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px)), 46vh, 0) scale(0.5);
  }
  20% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--s301fx-dx, 0px) * 0.6), -46vh, 0) scale(1.1);
  }
}

/* ------------------------------------------------ mouvement réduit : ARRÊT */
/*
 * Repli ACCESSIBLE : aucune animation n'est jouée. Le composant rend à la
 * place une pastille STATIQUE, très discrète, retirée du DOM après un court
 * délai — une confirmation, pas un effet. Elle n'est pas annoncée aux lecteurs
 * d'écran (décorative) et ne bouge pas.
 */

.s301fx-still {
  position: fixed;
  left: 50%;
  bottom: 22vh;
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 60;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 18px;
  line-height: 1;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .s301fx-layer .s301fx-item {
    animation: none !important;
    opacity: 0 !important;
  }
}
