/* =====================================================================
 * motion.css — couche d'animation et de micro-interactions.
 *
 * Chargée APRÈS app.css (application) et après le <style> de
 * public_base.html (site public) : c'est la seule source de vérité pour
 * le mouvement. Aucune règle de mise en page ici, aucun JavaScript.
 *
 * Trois familles :
 *   1. Entrée      — ce qui arrive à l'écran s'annonce (charge + scroll).
 *   2. Réaction    — ce qui est cliquable le montre (survol, appui).
 *   3. État        — ce qui travaille le montre (en cours, quota, flux).
 *
 * Les révélations au scroll utilisent `animation-timeline: view()`
 * (Chrome/Edge). Ailleurs (Safari, Firefox) le contenu s'affiche
 * normalement, sans état initial invisible : jamais de contenu masqué
 * faute de support.
 * ===================================================================== */

/* ---------------------------------------------------------- keyframes */
@keyframes m-up      {from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:none}}
@keyframes m-in      {from{opacity:0} to{opacity:1}}
@keyframes m-pop     {from{opacity:0;transform:translateY(10px) scale(.96)} to{opacity:1;transform:none}}
@keyframes m-left    {from{opacity:0;transform:translateX(-10px)} to{opacity:1;transform:none}}
@keyframes m-drop    {from{opacity:0;transform:translateY(-10px)} to{opacity:1;transform:none}}
@keyframes m-flow    {to{background-position:16px 0}}          /* pointillés qui défilent */
@keyframes m-sheen   {to{background-position:220% 0}}          /* reflet sur une barre    */
@keyframes m-pulse   {50%{opacity:.35}}
@keyframes m-ring    {0%{box-shadow:0 0 0 0 rgba(84,74,227,.35)} 70%,100%{box-shadow:0 0 0 7px rgba(84,74,227,0)}}
@keyframes m-spin    {to{transform:rotate(360deg)}}
@keyframes m-float   {50%{transform:translateY(-5px)}}

/* =====================================================================
 * 1. ENTRÉE
 * ===================================================================== */

/* Au chargement : le haut de page se compose au lieu d'apparaître d'un bloc.
   Décalages courts (≤ 240 ms) — l'attente perçue reste nulle. */
.g-hero > *          {animation:m-up .55s cubic-bezier(.22,.7,.3,1) both}
.g-hero > *:nth-child(2){animation-delay:.10s}
.mock                {animation:m-pop .7s cubic-bezier(.22,.7,.3,1) .06s both}
.mock .chip          {animation:m-pop .4s ease-out both}
.mock .chip:nth-child(n+2){animation-delay:calc(.18s + .045s * var(--i,1))}
.mock .chip:nth-child(2){--i:1}.mock .chip:nth-child(3){--i:2}
.mock .chip:nth-child(4){--i:3}.mock .chip:nth-child(5){--i:4}
.mock .chip:nth-child(6){--i:5}.mock .chip:nth-child(7){--i:6}
/* Le journal se remplit ligne à ligne : on lit le déroulé d'une réplication. */
.mock .log > div     {animation:m-left .45s ease-out both}
.mock .log > div:nth-child(1){animation-delay:.35s}
.mock .log > div:nth-child(2){animation-delay:.55s}
.mock .log > div:nth-child(3){animation-delay:.75s}
.mock .log > div:nth-child(4){animation-delay:.95s}
.mock .stats         {animation:m-in .5s ease-out 1.1s both}

/* Grille d'applications (accueil connecté) : apparition en cascade. */
.apps-grid > *{animation:m-pop .42s cubic-bezier(.22,.7,.3,1) both}
.apps-grid > :nth-child(1){animation-delay:.02s}.apps-grid > :nth-child(2){animation-delay:.05s}
.apps-grid > :nth-child(3){animation-delay:.08s}.apps-grid > :nth-child(4){animation-delay:.11s}
.apps-grid > :nth-child(5){animation-delay:.14s}.apps-grid > :nth-child(6){animation-delay:.17s}
.apps-grid > :nth-child(7){animation-delay:.20s}.apps-grid > :nth-child(8){animation-delay:.23s}
.apps-grid > :nth-child(9){animation-delay:.26s}.apps-grid > :nth-child(n+10){animation-delay:.29s}

/* Écrans applicatifs : les cartes se posent, sans cascade interminable. */
main > .card,main > form > .card{animation:m-up .38s ease-out both}
main > .card:nth-of-type(2),main > form > .card:nth-of-type(2){animation-delay:.06s}
main > .card:nth-of-type(n+3),main > form > .card:nth-of-type(n+3){animation-delay:.10s}

/* Messages flash : ils tombent du haut, on ne les rate pas. */
.flash{animation:m-drop .35s cubic-bezier(.22,.7,.3,1) both}

/* Révélation au défilement : DÉSACTIVÉE (2026-09-23). `animation-timeline:
   view()` laissait des titres et des cartes bloqués à faible opacité selon
   la position de scroll au chargement — un vrai problème de lisibilité
   signalé sur la page d'accueil, pas un simple effet manqué. Mieux vaut du
   contenu toujours visible qu'une entrée animée qui peut rester à moitié
   invisible. Si on veut réintroduire un effet d'entrée au scroll un jour,
   le faire avec un IntersectionObserver qui ajoute une classe (état de
   repli = visible), jamais un état masqué par défaut. */

/* =====================================================================
 * 2. RÉACTION — survol, appui, focus
 * ===================================================================== */

button,.btn,.btn2,.tbtn{
  transition:background .16s ease,border-color .16s ease,color .16s ease,
             transform .16s cubic-bezier(.22,.7,.3,1),box-shadow .16s ease}
.btn:hover,button:not(.ghost):not(.danger):hover{
  transform:translateY(-1px);box-shadow:0 6px 16px -6px rgba(84,74,227,.55)}
.btn2:hover{transform:translateY(-1px);box-shadow:0 6px 16px -10px rgba(23,20,42,.5)}
button:active,.btn:active,.btn2:active,.tbtn:active{transform:translateY(0) scale(.985);box-shadow:none}

/* Cartes cliquables : léger décollement, ombre portée douce. */
.scard,.fcard,.pcard,.dcard{transition:transform .2s cubic-bezier(.22,.7,.3,1),
  box-shadow .2s ease,border-color .2s ease}
.scard:hover,.fcard:hover,.pcard:hover,.dcard:hover{
  transform:translateY(-3px);border-color:#CFCBE8;box-shadow:0 18px 34px -22px rgba(23,20,42,.45)}

/* Tuiles d'application : l'icône répond avant la tuile. */
.app-tile{transition:transform .18s cubic-bezier(.22,.7,.3,1),box-shadow .18s ease,background .18s ease}
.app-tile .tile-icon{transition:transform .22s cubic-bezier(.34,1.4,.5,1),box-shadow .22s ease}
.app-tile:hover .tile-icon{transform:translateY(-3px) scale(1.06);box-shadow:0 10px 20px -10px rgba(24,26,32,.35)}
.app-tile:active{transform:translateY(0) scale(.98)}

/* Navigation publique : trait qui se déploie sous le lien actif/survolé. */
.nav-main a{position:relative;padding-bottom:3px}
.nav-main a::after{content:'';position:absolute;left:0;right:0;bottom:0;height:2px;
  background:#544AE3;border-radius:2px;transform:scaleX(0);transform-origin:left;
  transition:transform .22s cubic-bezier(.22,.7,.3,1)}
.nav-main a:hover::after,.nav-main a[aria-current]::after{transform:scaleX(1)}

/* Onglets applicatifs : la pastille active se pose au lieu de sauter. */
.topbar a.menu,.subnav-tab,.subnav-home{transition:background .15s ease,color .15s ease}

/* Lignes de tableau : le survol suit l'œil sur les tableaux denses. */
tbody tr{transition:background .12s ease}
tbody tr:hover{background:var(--surface,#FAFBFC)}

/* Logo : les deux pastilles jouent la réplication (source → copie). */
.logo:hover .dot i:last-child,.logo:hover .logo-dot i:last-child{
  animation:m-ring .9s ease-out}
.logo .dot i,.logo .logo-dot i{transition:transform .25s cubic-bezier(.34,1.4,.5,1)}
.logo:hover .dot i:first-child,.logo:hover .logo-dot i:first-child{transform:translateX(-2px)}

/* FAQ / accordéons : le contenu se déplie au lieu d'apparaître sec. */
.faq-item summary::after,.accordion summary::before{transition:transform .2s ease}
.faq-item[open] > p,.faq-item[open] > *:not(summary),
.accordion[open] > *:not(summary),.tnode[open] > .tbody{
  animation:m-up .28s ease-out both}

/* =====================================================================
 * 3. ÉTAT — ce qui travaille, ce qui progresse
 * ===================================================================== */

/* Barre de progression : reflet qui balaie pendant l'exécution.
   Réservé à .progress (présente quand un run tourne) ; la jauge de quota
   est permanente à l'écran et garde sa simple transition de largeur. */
.progress > span{
  background-image:linear-gradient(100deg,transparent 20%,rgba(255,255,255,.45) 40%,transparent 60%);
  background-size:220% 100%;animation:m-sheen 1.9s linear infinite}

/* Statuts : « en cours » respire, les statuts finaux non. */
.pill.running,.pill.pending,.st.active{animation:m-pulse 1.6s ease-in-out infinite}
.wiz-rail .wst.active b{animation:m-ring 1.8s ease-out infinite}

/* Indicateur circulaire réutilisable : <span class="spin"></span>. */
.spin{display:inline-block;width:13px;height:13px;vertical-align:-2px;
  border:2px solid var(--accent-line,#D8D5F8);border-top-color:var(--accent,#544AE3);
  border-radius:50%;animation:m-spin .7s linear infinite}

/* Flux source → destination de la maquette : les pointillés avancent,
   ce qui rend le sens de la copie lisible d'un coup d'œil. */
.mock-arrow .bar-h{border-top:0;height:2px;width:44px;
  background-image:linear-gradient(90deg,#4FD2E8 0 8px,transparent 8px 16px);
  background-size:16px 2px;animation:m-flow .8s linear infinite}
.mock-arrow .bar-v{border-left:0;width:2px;height:20px;
  background-image:linear-gradient(180deg,#4FD2E8 0 8px,transparent 8px 16px);
  background-size:2px 16px;animation:m-flow-v .8s linear infinite}
@keyframes m-flow-v{to{background-position:0 16px}}
.mock-arrow svg,.mock-arrow .gl-h,.mock-arrow .gl-v{animation:m-float 2.4s ease-in-out infinite}
/* La destination pulse doucement : c'est elle qui reçoit. */
.mock-flow > .mock-node:last-child{animation:m-pop .7s cubic-bezier(.22,.7,.3,1) .25s both}

/* =====================================================================
 * 4. PAGE D'ACCUEIL (data-page="landing") — traitement renforcé.
 *
 * Scopé par l'attribut posé sur <body> : rien de ce qui suit ne fuit
 * sur les autres pages. Deux intentions, jamais la décoration seule :
 *   - le hero raconte la réplication (un paquet part de la source,
 *     arrive à destination, la destination réagit) ;
 *   - chaque section se compose de gauche à droite quand on l'atteint,
 *     ce qui donne un ordre de lecture au lieu d'un mur de cartes.
 * ===================================================================== */

@keyframes m-packet{0%{transform:translateX(0);opacity:0}
  12%{opacity:1}80%{opacity:1}100%{transform:translateX(38px);opacity:0}}
@keyframes m-packet-v{0%{transform:translateY(0);opacity:0}
  12%{opacity:1}80%{opacity:1}100%{transform:translateY(15px);opacity:0}}
@keyframes m-recv{0%,62%{border-color:#2C2745;box-shadow:none}
  78%{border-color:#4FD2E8;box-shadow:0 0 0 3px rgba(79,210,232,.13)}
  100%{border-color:#2C2745;box-shadow:none}}
@keyframes m-aura{50%{transform:translate3d(3%,-4%,0) scale(1.08)}}
@keyframes m-badge{50%{box-shadow:0 0 0 6px rgba(84,74,227,0);background:#6A60F0}}

/* ------------------------------------------------------------ le hero */

/* Halo animé derrière le hero : deux nappes de couleur qui dérivent très
   lentement. Décoratif, sous le contenu, sans coût de mise en page. */
[data-page=landing] .g-hero{position:relative}
[data-page=landing] .g-hero::before{content:'';position:absolute;z-index:-1;
  inset:-18% -12% -12% -18%;pointer-events:none;
  background:
    radial-gradient(38% 46% at 22% 30%,rgba(84,74,227,.16),transparent 70%),
    radial-gradient(34% 40% at 78% 66%,rgba(63,193,217,.14),transparent 70%);
  filter:blur(6px);animation:m-aura 14s ease-in-out infinite alternate}

/* La colonne de texte se compose ligne à ligne (le bloc entier ne bouge
   plus d'un coup : c'est le stagger interne qui porte le mouvement). */
[data-page=landing] .g-hero > div:first-child{animation:none}
[data-page=landing] .g-hero > div:first-child > *{
  animation:m-up .5s cubic-bezier(.22,.7,.3,1) both}
[data-page=landing] .g-hero > div:first-child > :nth-child(1){animation-delay:.02s}
[data-page=landing] .g-hero > div:first-child > :nth-child(2){animation-delay:.09s}
[data-page=landing] .g-hero > div:first-child > :nth-child(3){animation-delay:.17s}
[data-page=landing] .g-hero > div:first-child > :nth-child(4){animation-delay:.25s}
[data-page=landing] .g-hero > div:first-child > :nth-child(5){animation-delay:.32s}

/* Flèches des boutons : elles avancent au survol, le chevron « voir
   comment ça marche » invite à descendre. */
.btn svg,.btn2 svg{transition:transform .18s cubic-bezier(.22,.7,.3,1)}
.btn:hover svg{transform:translateX(3px)}
[data-page=landing] .g-hero .btn2 svg{animation:m-float 2.4s ease-in-out infinite}
[data-page=landing] .g-hero .btn2:hover svg{animation:none;transform:translateY(3px)}

/* --------------------------------------- la maquette : flux source → cible */

/* Un paquet part de la source et file vers la destination, en boucle. */
.mock-arrow .bar-h,.mock-arrow .bar-v{position:relative}
.mock-arrow .bar-h::after,.mock-arrow .bar-v::after{content:'';position:absolute;
  width:6px;height:6px;border-radius:50%;background:#4FD2E8;
  box-shadow:0 0 9px rgba(79,210,232,.9)}
.mock-arrow .bar-h::after{top:-2px;left:0;animation:m-packet 1.9s ease-in-out infinite}
.mock-arrow .bar-v::after{left:-2px;top:0;animation:m-packet-v 1.9s ease-in-out infinite}

/* La destination s'illumine à l'instant où le paquet arrive (retard calé
   sur la fin du trajet) : on voit ce qui reçoit. */
.mock-flow > .mock-node:last-child{
  animation:m-pop .7s cubic-bezier(.22,.7,.3,1) .25s both,
            m-recv 1.9s ease-in-out infinite}

/* Balayage unique sur le journal, une fois les lignes écrites. */
.mock .log{position:relative}
.mock .log::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 35%,rgba(155,148,255,.13) 50%,transparent 65%);
  transform:translateX(-100%);animation:m-sweep 1.4s ease-out 1.15s 1 both}
@keyframes m-sweep{to{transform:translateX(100%)}}

/* Cascade au défilement : DÉSACTIVÉE avec le bloc plus haut (même raison —
   contenu resté à faible opacité chez un vrai utilisateur). Les cartes de
   tarifs et les grilles de la page d'accueil s'affichent maintenant à pleine
   opacité par défaut. */

/* --------------------------------------------- réactions propres à l'accueil */
.scard .badge,.fcard .ibadge{transition:transform .25s cubic-bezier(.34,1.4,.5,1)}
.scard:hover .badge,.fcard:hover .ibadge{transform:scale(1.1) rotate(-4deg)}
.dcard{transition:transform .2s cubic-bezier(.22,.7,.3,1),box-shadow .2s ease}
.dcard:hover{transform:translateY(-4px)}
/* L'offre mise en avant respire — sur le badge, pas sur la carte, pour ne
   pas entrer en conflit avec son animation de révélation. */
.tag-pop{animation:m-badge 2.8s ease-in-out infinite;box-shadow:0 0 0 0 rgba(84,74,227,.55)}
.pcta{transition:transform .16s cubic-bezier(.22,.7,.3,1),background .16s ease,border-color .16s ease}
.pcta:hover{transform:translateY(-2px)}

/* Impression : aucune animation, et surtout aucun état initial masqué —
   une page imprimée ne défile pas. */
@media print{*,*::before,*::after{animation:none !important;transition:none !important}}

/* =====================================================================
 * Accessibilité — mouvement réduit : tout se fige, rien ne disparaît.
 * (Doit rester en dernier : ce fichier est chargé après les autres.)
 * ===================================================================== */
@media (prefers-reduced-motion:reduce){
  /* On coupe le mouvement, pas la mise en forme : neutraliser opacity ou
     transform ici casserait les coches, chevrons et éléments grisés qui
     s'appuient dessus. Sans animation, les états `both` ne s'appliquent
     plus : le contenu s'affiche à son état naturel. */
  *,*::before,*::after{
    animation:none !important;transition:none !important;scroll-behavior:auto !important}
}
