/* ============================================================
   Animation globale de la présentation
   ------------------------------------------------------------
   Trois principes :
   1. Guider l'œil dans l'ordre de lecture, jamais distraire.
   2. Ne jamais faire attendre : tout est joué en moins d'une seconde,
      et la navigation reste instantanée même en pleine animation.
   3. Ne rien casser : on anime « translate » et « opacity », jamais
      « transform », que la mise en page utilise déjà pour centrer
      et incliner certains éléments.
   ============================================================ */

:root{
  --anim-slide:      520ms;   /* changement de slide                          */
  --anim-elem:       900ms;   /* durée d'apparition d'un bloc                 */
  --anim-course:      20px;   /* distance parcourue en apparaissant           */

  /* Rythme de lecture — c'est ici que se règle l'enchaînement.
     Le titre arrive d'abord, seul. Une pause le laisse se poser. Puis les
     blocs de contenu se succèdent assez espacés pour être lus l'un après
     l'autre, tandis que les détails d'un même bloc suivent de près.        */
  --anim-pas-titre:   90ms;   /* entre les éléments du bloc de titre          */
  --anim-pause:      520ms;   /* respiration après le titre                   */
  --anim-pas-bloc:   380ms;   /* entre deux cartes : le temps de les lire     */
  --anim-pas-detail: 110ms;   /* entre les détails d'une même rangée          */
  --anim-enveloppe: 2800ms;   /* durée maximale de la phase de contenu        */

  --anim-courbe: cubic-bezier(.16, 1, .3, 1);     /* départ franc, fin douce  */
  --anim-sortie: cubic-bezier(.4, 0, .7, .2);
}

/* ---------- Changement de slide : fondu + glissement directionnel ---------- */
.slide{
  transition:opacity var(--anim-slide) var(--anim-courbe),
             translate var(--anim-slide) var(--anim-courbe),
             visibility 0s linear var(--anim-slide);
  translate:0 0;
}
.slide.is-active{
  transition:opacity var(--anim-slide) var(--anim-courbe),
             translate var(--anim-slide) var(--anim-courbe),
             visibility 0s;
}
/* La slide qui sort recule dans le sens de la navigation, celle qui
   entre arrive de l'autre côté : le mouvement raconte l'avancée. */
.deck[data-sens="avant"]  .slide:not(.is-active){ translate:-26px 0; }
.deck[data-sens="arriere"] .slide:not(.is-active){ translate:26px 0; }

/* ---------- Apparition des éléments ---------- */
/* L'état masqué n'est appliqué que si le script tourne : sans JS,
   la présentation reste entièrement lisible. */
.js-anim .slide [data-rv]{ opacity:0; }

.js-anim .slide.is-active [data-rv]{
  animation:rv-entree var(--anim-elem) var(--anim-courbe) var(--rvd, 0ms) both;
}
@keyframes rv-entree{
  from{ opacity:0; translate:0 var(--anim-course); }
  to  { opacity:1; translate:0 0; }
}

/* Les fonds photo pleine page ne bougent pas : ils se posent, c'est tout.
   Un fond qui glisse décollerait du cadre et trahirait le montage.
   Le retard compte quand même : un bandeau blanc qui arrive à l'instant zéro,
   vide, avant même le titre, se lit comme un défaut d'affichage. */
.js-anim .slide.is-active [data-rv="fond"]{
  animation:rv-fond calc(var(--anim-elem) * .95) ease-out var(--rvd, 0ms) both;
}
@keyframes rv-fond{ from{ opacity:0; } to{ opacity:1; } }

/* La pastille de numéro tombe du haut : elle est ancrée au bord. */
.js-anim .slide.is-active [data-rv="pastille"]{
  animation:rv-pastille calc(var(--anim-elem) * .85) var(--anim-courbe) var(--rvd, 0ms) both;
}
@keyframes rv-pastille{
  from{ opacity:0; translate:0 -14px; }
  to  { opacity:1; translate:0 0; }
}

/* Les personnages entrent depuis leur bord : ils habitent la marge. */
.js-anim .slide.is-active [data-rv="gauche"]{
  animation:rv-gauche calc(var(--anim-elem) * 1.15) var(--anim-courbe) var(--rvd, 0ms) both;
}
@keyframes rv-gauche{
  from{ opacity:0; translate:-26px 0; }
  to  { opacity:1; translate:0 0; }
}
.js-anim .slide.is-active [data-rv="droite"]{
  animation:rv-droite calc(var(--anim-elem) * 1.15) var(--anim-courbe) var(--rvd, 0ms) both;
}
@keyframes rv-droite{
  from{ opacity:0; translate:26px 0; }
  to  { opacity:1; translate:0 0; }
}

/* Les filets et soulignés se tracent au lieu d'apparaître. */
.js-anim .slide.is-active [data-rv="trait"]{
  animation:rv-trait calc(var(--anim-elem) * 1.1) var(--anim-courbe) var(--rvd, 0ms) both;
  transform-origin:left center;
}
@keyframes rv-trait{
  from{ opacity:0; scale:0 1; }
  to  { opacity:1; scale:1 1; }
}

/* ---------- Respiration du bouton principal ---------- */
/* Une seule pulsation, une fois la slide posée : elle désigne l'action
   sans transformer la page en sapin de Noël. */
.js-anim .slide.is-active [data-pulse]{
  animation:rv-entree var(--anim-elem) var(--anim-courbe) var(--rvd,0ms) both,
            pulse-cta 1.9s ease-out calc(var(--rvd, 0ms) + var(--anim-elem) + 200ms) 2;
}
@keyframes pulse-cta{
  0%,100%{ box-shadow:0 10px 26px rgba(255,10,78,.32); }
  50%    { box-shadow:0 10px 26px rgba(255,10,78,.32), 0 0 0 10px rgba(255,10,78,.10); }
}

/* ---------- Fil de discussion du téléphone (page 10) ---------- */
/* Le téléphone entre d'un bloc ; ses messages s'écrivent ensuite, un par
   un, comme une vraie conversation. Le décalage part de `--rvd`, posé sur
   le téléphone par le moteur : les enfants en héritent. */
.js-anim .slide.is-active .tel__msg{
  animation:tel-msg 380ms var(--anim-courbe)
            calc(var(--rvd, 0ms) + var(--anim-elem) * .55 + var(--i, 0) * 360ms) both;
}
@keyframes tel-msg{
  from{ opacity:0; translate:0 9px; scale:.97; }
  to  { opacity:1; translate:0 0;   scale:1; }
}
/* Le curseur de saisie ne s'allume qu'une fois la conversation posée. */
.js-anim .slide.is-active .tel__saisie{
  animation:rv-fond 420ms ease-out calc(var(--rvd, 0ms) + var(--anim-elem) * .55 + 6 * 360ms) both;
}

/* ---------- Compteurs ---------- */
/* Les chiffres se stabilisent sur leur valeur : dans une présentation
   commerciale, un chiffre qui se pose se retient mieux qu'un chiffre posé. */
[data-count]{ font-variant-numeric:tabular-nums; }

/* ---------- Respect des préférences système ---------- */
@media (prefers-reduced-motion:reduce){
  :root{ --anim-slide:160ms; --anim-elem:1ms; --anim-course:0px;
         --anim-pas-titre:0ms; --anim-pause:0ms; --anim-pas-bloc:0ms;
         --anim-pas-detail:0ms; --anim-enveloppe:0ms; }
  .deck[data-sens] .slide:not(.is-active){ translate:0 0; }
  .js-anim .slide.is-active [data-rv],
  .js-anim .slide.is-active [data-rv="fond"],
  .js-anim .slide.is-active [data-rv="pastille"],
  .js-anim .slide.is-active [data-rv="gauche"],
  .js-anim .slide.is-active [data-rv="droite"],
  .js-anim .slide.is-active [data-rv="trait"],
  .js-anim .slide.is-active [data-pulse]{
    animation:rv-fond 160ms linear both;
  }
  .js-anim .slide.is-active .tel__msg,
  .js-anim .slide.is-active .tel__saisie{ animation:none; opacity:1; }
}

/* ══════════════════════════════════════════════════════════════════════
   Le coup de cœur

   Quand le grand chiffre a fini de monter, il bat deux fois — puis le
   libellé bat à son tour, un peu après. Deux battements décalés attrapent
   l'œil, là où un mouvement simultané se lit comme un simple sursaut de
   mise en page.

   On anime `transform`, jamais `scale` : la révélation d'entrée occupe
   déjà les propriétés individuelles, et les deux se marcheraient dessus.
   ══════════════════════════════════════════════════════════════════════ */
[data-boum]{ transform-origin:left center; }
.bat-boum{ animation:bat-boum 900ms cubic-bezier(.22,1,.32,1) both; }

@keyframes bat-boum{
  0%   { transform:scale(1) }
  12%  { transform:scale(1.072) }
  26%  { transform:scale(.998) }
  40%  { transform:scale(1.048) }
  58%  { transform:scale(1) }
  100% { transform:scale(1) }
}

/* Le chiffre s'allume en même temps qu'il enfle : la couleur du battement
   vient du dégradé de la page, pas d'un halo rapporté. */
.s04__big.bat-boum{ animation-name:bat-boum, bat-lueur; }
@keyframes bat-lueur{
  0%, 100% { filter:drop-shadow(0 0 0 rgba(252,12,69,0)) }
  22%      { filter:drop-shadow(0 6px 22px rgba(252,60,75,.34)) }
  50%      { filter:drop-shadow(0 4px 16px rgba(252,60,75,.20)) }
}

@media (prefers-reduced-motion:reduce){
  .bat-boum{ animation:none; }
}

/* ---------- Impression ---------- */
/* Les 13 slides s'impriment à la suite : aucune n'est « active », donc
   aucune n'aurait joué son animation. On rend tout visible d'office. */
@media print{
  .js-anim .slide .tel__msg,
  .js-anim .slide .tel__saisie,
  .js-anim .slide [data-rv]{
    opacity:1 !important;
    translate:0 0 !important;
    scale:1 1 !important;
    animation:none !important;
  }
  .slide{ translate:0 0 !important; transition:none !important; }
}
