/* ==========================================================================
   motion-patterns.css — bibliotheque de mouvement, zero dependance
   Kit refonte visuelle v2. A copier dans clients/{slug}/assets/.

   ARCHITECTURE EN COUCHES (voir docs/charte-motion-theme.md)
   Couche 0 : transitions et retours au toucher. Marche partout, toujours.
   Couche 1 : reveals au defilement, pilotes par IntersectionObserver (kit.js).
   Couche 2 : effets continus lies au defilement, en CSS scroll-driven natif.
              Progressive : absent sur Firefox stable, et ce n'est pas grave.

   REGLE ABSOLUE
   L'etat par defaut d'un element est TOUJOURS son etat final visible.
   On ne masque un element que lorsque JS tourne (classe .js-anim posee par
   kit.js). Sans cette regle, un navigateur sans JS affiche une page blanche.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Courbes
   linear() approxime un ressort sans une seule ligne de JavaScript.
   -------------------------------------------------------------------------- */
:root {
  --ressort-doux: linear(
    0, 0.006, 0.025, 0.101, 0.539, 0.826, 0.967, 1.024, 1.035, 1.024, 1.007,
    0.999, 1
  );
  --ressort-vif: linear(
    0, 0.032, 0.126, 0.278, 0.65, 0.936, 1.062, 1.09, 1.07, 1.03, 1.004, 1
  );
  --sortie-douce: cubic-bezier(0.22, 0.61, 0.36, 1);

  --duree-courte: 140ms;
  --duree-moyenne: 320ms;
  --duree-longue: 620ms;
}

/* ==========================================================================
   COUCHE 0 — retour immediat au toucher
   Apple design : le retour vit sur la pression, jamais sur le relachement.
   ========================================================================== */

.tactile {
  transition: transform var(--duree-moyenne) var(--ressort-doux);
}

.tactile:active {
  transform: scale(0.97);
  transition-duration: var(--duree-courte); /* la reponse doit etre instantanee */
}

/* Elevation au survol, uniquement la ou il y a un vrai curseur.
   On n'anime que transform : animer box-shadow force un repaint a chaque image. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .elevable {
    transition: transform var(--duree-moyenne) var(--sortie-douce);
  }

  .elevable:hover {
    transform: translateY(-4px);
  }
}

/* ==========================================================================
   COUCHE 1 — apparition au defilement (IntersectionObserver)
   Etat par defaut : VISIBLE. Le masquage n'existe que si .js-anim est pose.
   ========================================================================== */

.revele {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  :root.js-anim .revele {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity var(--duree-longue) var(--sortie-douce),
      transform var(--duree-longue) var(--sortie-douce);
  }

  :root.js-anim .revele.est-visible {
    opacity: 1;
    transform: none;
  }

  /* Decalage en cascade pour une grille ou une liste.
     Pose data-retard="1".."6" sur les enfants. */
  :root.js-anim .revele[data-retard='1'] { transition-delay: 60ms; }
  :root.js-anim .revele[data-retard='2'] { transition-delay: 120ms; }
  :root.js-anim .revele[data-retard='3'] { transition-delay: 180ms; }
  :root.js-anim .revele[data-retard='4'] { transition-delay: 240ms; }
  :root.js-anim .revele[data-retard='5'] { transition-delay: 300ms; }
  :root.js-anim .revele[data-retard='6'] { transition-delay: 360ms; }

  /* Variante : devoilement par masque, plus caractere qu'un simple fondu.
     clip-path est compose par le GPU, donc sans cout de mise en page.

     On utilise une ANIMATION avec « both » plutot qu'une transition : une
     transition qui ne se declenche pas laisse l'element masque et donc
     invisible, alors qu'une animation « both » garantit l'etat final meme si
     elle est interrompue. Constate en conditions reelles sur canoune, ou la
     galerie restait entierement noire. */
  :root.js-anim .revele-masque:not(.est-visible) {
    clip-path: inset(0 0 100% 0);
  }

  :root.js-anim .revele-masque.est-visible {
    animation: devoiler-masque var(--duree-longue) var(--sortie-douce) both;
  }
}

/* ==========================================================================
   COUCHE 2 — effets continus lies au defilement (CSS natif)
   Support ~82,6% au 2026-07-20 : Chrome 115+, Safari 26+, Firefox derriere
   un flag. Strictement decoratif : rien d'essentiel ne doit en dependre.
   ========================================================================== */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Parallaxe douce sur une image de section.
       Amplitude volontairement faible : au-dela de ~40px l'effet devient
       du mal des transports sur mobile. */
    .parallaxe {
      animation: derive linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* Barre de progression de lecture, collee en haut de page. */
    .progression-lecture {
      transform-origin: 0 50%;
      animation: remplir linear both;
      animation-timeline: scroll(root block);
    }

    /* Retrecissement de l'en-tete quand on quitte le haut de page. */
    .entete-compacte {
      animation: compacter linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 140px;
    }
  }
}

@keyframes devoiler-masque {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0 0); }
}

@keyframes derive {
  from { transform: translateY(-24px); }
  to   { transform: translateY(24px); }
}

@keyframes remplir {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes compacter {
  to {
    padding-block: 0.5rem;
    background-color: var(--voile);
    backdrop-filter: blur(10px);
  }
}

/* ==========================================================================
   Garde-fous
   ========================================================================== */

/* Le defilement horizontal parasite vient presque toujours d'un element
   anime en translateX. Cette regle le contient au niveau de la page. */
body {
  overflow-x: clip;
}

/* Le focus reste visible en toutes circonstances, animations comprises. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
