/* ===================================================
   home.css — page d'accueil et page mandat de direkol.fr
   Complète design-tokens.css + landing-base.css.
   =================================================== */

:root {
  --ok: #10A16A;
  --alert: #DC2626;
  --sh-window: 0 30px 70px -24px rgba(76, 46, 160, .30), 0 6px 20px -8px rgba(76, 46, 160, .16);
  --ease: cubic-bezier(.22, .61, .36, 1);

  /* Trois courbes, trois usages, et rien d'autre — c'est ce qui fait qu'une
     page animée par plusieurs blocs garde une seule main.
       --e-out    : arrivée franche puis pose lente. Toutes les entrées.
       --e-spring : dépasse légèrement la cible. Ce qui « claque » : boutons,
                    pastilles, cases de l'appel.
       --e-io     : symétrique. Les boucles qui respirent. */
  --e-out: cubic-bezier(.16, 1, .3, 1);
  --e-spring: cubic-bezier(.34, 1.42, .5, 1);
  --e-io: cubic-bezier(.45, 0, .55, 1);

  /* Couleurs de l'appel en classe — reprises telles quelles de
     server/appel/index.php. La maquette de cette page doit ressembler à
     l'écran que l'enseignant a sous les yeux, pas à une interprétation. */
  --ap-violet: #6d3bec;
  --ap-violet-50: #f4f1fe;
  --ap-violet-100: #e6e0f7;
  --ap-ink: #16131f;
  --ap-muted: #79737f;
  --ap-line-2: #e1ddec;
  --ap-bg: #f6f4fb;
  --ap-red: #db362a; --ap-red-50: #fdeeed; --ap-red-100: #f9d7d3;
  --ap-amber: #a9600b; --ap-amber-50: #fbf2e3; --ap-amber-100: #f5e0bf;
  --ap-green: #199a51; --ap-green-50: #eaf7ef; --ap-green-100: #cbead7;
  --ap-sh-sm: 0 1px 2px rgba(30, 20, 55, .05), 0 1px 3px rgba(30, 20, 55, .05);
}

/* ===================================================
   1. Primitives
   =================================================== */

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; }
.wrap--narrow { max-width: 820px; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: .74rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--v);
}
.eyebrow::before { content: ""; width: 22px; height: 2px; background: currentColor; border-radius: 2px; }

h2.title {
  font-family: var(--fd);
  font-size: clamp(1.9rem, 4.2vw, 3rem);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.08;
  margin: 14px 0 0; color: var(--g);
}

/* Le titre se DÉCOUVRE, il n'apparaît pas. Un rideau qui remonte donne à un
   titre de deux lignes une entrée que ni le fondu ni le glissement seuls ne
   lui donnent — et sans découper le texte en <span>, qui abîmerait la
   sélection, la recherche dans la page et la lecture d'écran.
   Les retraits négatifs de fin laissent passer jambages et accents. */
@keyframes titleWipe {
  from { clip-path: inset(0 0 104% 0); transform: translate3d(0, 20px, 0); }
  to { clip-path: inset(-32% -6% -16% -6%); transform: none; }
}
[data-reveal].is-in .title { animation: titleWipe 1s var(--e-out) .06s backwards; }
.lede { font-size: 1.06rem; color: var(--g2); margin-top: 16px; max-width: 62ch; }

.sec { padding: 92px 0; }
.sec--soft { background: var(--bg-soft); }
.sec--tint {
  background:
    radial-gradient(ellipse 70% 60% at 15% 0%, rgba(169, 139, 247, .16) 0%, transparent 60%),
    linear-gradient(180deg, #FBF9FF 0%, #F5F0FF 100%);
}

/* Boutons — valeurs issues de la cascade des deux anciennes définitions
   (le socle ci-dessous, partiellement écrasé par le bloc héros disparu) :
   le rendu ne change pas. */
.btn-primary {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-weight: 700; font-size: 1rem; white-space: nowrap;
  padding: 14px 26px; border-radius: 11px;
  transition: transform .15s, box-shadow .2s, background .2s;
  background: var(--v); color: #fff;
  box-shadow: 0 4px 14px rgba(123, 91, 240, .3);
}
.btn-primary:hover { background: var(--vd); transform: translateY(-2px); box-shadow: 0 8px 22px rgba(123, 91, 240, .4); }
.btn-primary:active { transform: translateY(-1px) scale(.985); transition-duration: .09s; }
.btn-primary svg { width: 16px; height: 16px; transition: transform .3s var(--e-spring); }
.btn-primary:hover svg { transform: translateY(2px); }

/* Reflet qui balaie le bouton au survol. Il vit dans un pseudo-élément posé
   sur son propre calque : rien ne bouge autour, seule une transformation
   traverse le bouton. */
.btn-primary::after, .nav-cta::after {
  content: ""; position: absolute; inset: 0 auto 0 -60%; width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .45), transparent);
  transform: translateX(0) skewX(-18deg); opacity: 0;
  pointer-events: none;
}
.btn-primary:hover::after, .nav-cta:hover::after { opacity: 1; animation: sheen .75s var(--e-out); }
@keyframes sheen { from { transform: translateX(0) skewX(-18deg); } to { transform: translateX(430%) skewX(-18deg); } }
/* Le CTA blanc sur fond violet : un reflet blanc n'y verrait rien. */
.cta .btn-primary::after { background: linear-gradient(100deg, transparent, rgba(123, 91, 240, .22), transparent); }

/* ---------------------------------------------------------------
   Révélation au scroll — pilotée par motion.js (data-reveal → .is-in)
   --------------------------------------------------------------- */

[data-reveal] {
  opacity: 0; transform: translate3d(0, 44px, 0);
  transition: opacity .9s var(--e-out), transform .9s var(--e-out);
}
[data-reveal].is-in { opacity: 1; transform: none; }
[data-reveal-delay="1"] { transition-delay: .13s; }
[data-reveal-delay="2"] { transition-delay: .26s; }
[data-reveal-delay="3"] { transition-delay: .39s; }

/* Cascade : le conteneur est observé, chaque enfant porte son propre retard,
   posé une fois par motion.js dans --d. Une liste qui arrive d'un bloc se lit
   comme un pavé ; échelonnée, on lit les éléments un par un. */
[data-stagger] > * {
  opacity: 0; transform: translate3d(0, 26px, 0);
  transition: opacity .75s var(--e-out) var(--d, 0ms), transform .75s var(--e-out) var(--d, 0ms);
}
[data-stagger].is-in > * { opacity: 1; transform: none; }

/* Déplacement lié au défilement — motion.js écrit --py et --pr, le CSS ne fait
   que composer. Aucun de ces éléments n'a d'autre source de transformation.
   `rotateX(0deg)` est neutre : la perspective ne sert qu'aux éléments qui
   déclarent `data-parallax-spin`. */
[data-parallax] {
  transform: perspective(1600px) translate3d(0, var(--py, 0px), 0) rotateX(var(--pr, 0deg));
  will-change: transform;
}

/* Le trait de l'œilleton se trace au lieu d'apparaître. */
.eyebrow::before { transform: scaleX(0); transform-origin: left center; transition: transform .7s var(--e-out) .15s; }
[data-reveal].is-in .eyebrow::before, [data-stagger].is-in .eyebrow::before { transform: scaleX(1); }

/* Un visiteur SANS JavaScript doit voir la même page COMPLÈTE. `motion-on` est
   posée sur <html> par index.html dès le <head>, et retirée si motion.js ne se
   charge pas ; motion.js la pose aussi lui-même, pour le cas où un index.html
   plus ancien est encore en cache. Sans elle, personne ne viendra jamais
   ajouter `.is-in` : les blocs masqués le resteraient pour toujours.
   Mesuré avant ce repli, à 1280 × 800 : 76 éléments à opacity 0, 9 039 px sur
   9 867 px de hauteur de page — 92 % de la page invisible, hors héros.

   Ces règles S'AJOUTENT, elles ne modifient rien au-dessus : elles ne
   s'appliquent qu'en l'absence de la classe, où aucune règle `.is-in`
   ne peut de toute façon entrer en concurrence. */
html:not(.motion-on) [data-reveal],
html:not(.motion-on) [data-stagger] > * { opacity: 1; transform: none; }
html:not(.motion-on) .eyebrow::before { transform: scaleX(1); }

/* Les scènes composées au pixel, quand le navigateur n'a pas `zoom`.
   main.js les réduit pour qu'elles tiennent dans un écran de téléphone, mais
   il sort d'emblée si la propriété n'existe pas — Firefox ne l'a que depuis la
   version 126 (mai 2024), et un parc scolaire figé sur un ESR plus ancien
   passe par cette sortie. Les scènes gardent alors leur largeur composée,
   jusqu'à 540 px dans un écran de 375 : mesuré, scrollWidth 564 pour 375 de
   fenêtre, 189 px de débordement, et c'est la PAGE ENTIÈRE qui défile
   latéralement — le directeur lit des blocs coupés et balaie de côté à chaque
   section.

   `min-width: 0` est ce qui débloque : sans lui, la taille minimale
   automatique d'un élément de grille vaut son min-content, que les textes sans
   retour à la ligne rendent plus large que la colonne. Les scènes se
   recomposent alors dans la place disponible. Mesuré avec ce repli appliqué à
   la main : scrollWidth 375 pour 375, plus un pixel de débordement, et
   `overflow` reste `visible` — les décors qui dépassent volontairement ne sont
   ni rognés ni dotés d'une barre de défilement intérieure.

   Le garde `@supports` rend ce bloc inerte partout où `zoom` existe : sur ces
   navigateurs, c'est main.js qui décide, exactement comme avant. */
@supports not (zoom: 1) {
  [data-fit-scene] { min-width: 0; max-width: 100%; }
}

/* Un visiteur qui refuse les animations doit voir la page COMPLÈTE, pas une
   suite de blocs restés invisibles faute d'observateur. Les décors qui ne
   portent aucune information s'effacent, le reste se fige à l'état final. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal], [data-stagger] > * { opacity: 1; transform: none; }
  [data-parallax] { transform: none; }
  .eyebrow::before { transform: scaleX(1); }
  /* design-tokens.css ramène déjà les durées à zéro ; sans cette ligne, un
     retard d'animation laisserait l'élément sur son image de départ. */
  *, *::before, *::after { animation-delay: 0s !important; }
}

/* ===================================================
   2. Barre de navigation
   =================================================== */

.nav {
  position: fixed; inset: 0 0 auto; z-index: 100;
  background: rgba(255, 255, 255, .72);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: background .3s, border-color .3s, box-shadow .3s;
}
.nav.scrolled {
  background: rgba(255, 255, 255, .93);
  border-bottom-color: rgba(31, 41, 55, .07);
  box-shadow: 0 1px 14px rgba(31, 41, 55, .05);
}
.nav-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  height: 68px; max-width: 1180px; margin: 0 auto; padding: 0 24px;
}
.nav-logo { display: flex; align-items: center; gap: 10px; }
.nav-logo svg { width: 32px; height: 32px; flex-shrink: 0; }
.nav-logo .wm { font-family: var(--fd); font-weight: 800; font-size: 1.4rem; letter-spacing: -.025em; line-height: 1; }
.nav-logo .dir { color: var(--v); }
.nav-logo .eko { color: #1A0F3D; }

.nav-links { display: flex; align-items: center; gap: 26px; }
.nav-links a { font-size: .9rem; font-weight: 500; color: #4b5563; transition: color .2s; }
.nav-links a:hover { color: var(--v); }

.nav-cta {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--v); color: #fff !important;
  font-size: .875rem; font-weight: 700; white-space: nowrap;
  padding: 10px 18px; border-radius: 10px;
  transition: background .2s, transform .22s var(--e-spring), box-shadow .25s;
}
.nav-cta svg { width: 14px; height: 14px; transition: transform .3s var(--e-spring); }
.nav-cta:hover { background: var(--vd); transform: translateY(-2px) scale(1.04); box-shadow: 0 10px 24px rgba(123, 91, 240, .45); }
.nav-cta:hover svg { transform: translateY(2px); }

/* Avancement dans la page. Une mise à l'échelle sur un trait de 2 px : le
   navigateur la compose, elle ne provoque ni calcul de mise en page ni
   repeinte. */
.nav-progress {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  overflow: hidden; pointer-events: none;
}
.nav-progress i {
  display: block; height: 100%; width: 100%;
  background: linear-gradient(90deg, var(--vl), var(--v));
  transform: scaleX(var(--sp, 0)); transform-origin: 0 50%;
}

/* Les liens se soulignent en glissant plutôt qu'en changeant de couleur. */
.nav-links > a:not(.nav-cta) { position: relative; }
.nav-links > a:not(.nav-cta)::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -5px; height: 2px;
  border-radius: 2px; background: var(--v);
  transform: scaleX(0); transform-origin: right center;
  transition: transform .32s var(--e-out);
}
.nav-links > a:not(.nav-cta):hover::after { transform: scaleX(1); transform-origin: left center; }

/* Padding 9px pour une cible tactile de 44px ; la marge négative rend la
   place que le padding prend, l'icône ne bouge pas. */
.nav-mobile { display: none; background: none; padding: 9px; margin: -5px; color: var(--g); }
.nav-mobile svg { width: 26px; height: 26px; }

@media (max-width: 980px) {
  .nav-links {
    display: none; flex-direction: column; gap: 0; align-items: stretch;
    position: absolute; top: 68px; left: 0; right: 0;
    background: #fff; border-bottom: 1px solid var(--brd);
    box-shadow: 0 12px 30px rgba(31, 41, 55, .12);
  }
  .nav-links.open { display: flex; }
  .nav-links a { padding: 15px 24px; border-bottom: 1px solid #f3f4f6; }
  /* Le soulignement glissant se superposerait au filet de séparation. */
  .nav-links > a:not(.nav-cta)::after { display: none; }
  .nav-links .nav-cta { margin: 14px 24px; justify-content: center; }
  .nav-mobile { display: block; }
}

/* ===================================================
   3. Héros — repris tel quel de la version d'origine
   =================================================== */

@keyframes float-logo{
  0%,100%{transform:translateY(0) rotate(-2deg)}
  50%{transform:translateY(-8px) rotate(2deg)}
}
/* ===== HERO ===== */
.hero{
  position:relative;
  background:
    radial-gradient(ellipse at 20% 0%,rgba(169,139,247,.18) 0%,transparent 50%),
    radial-gradient(ellipse at 80% 100%,rgba(123,91,240,.12) 0%,transparent 50%),
    linear-gradient(180deg,#faf8ff 0%,#f5f0ff 100%);
  padding:120px 0 80px;
  overflow:hidden;
}
.hero-inner{
  max-width:1200px;margin:0 auto;padding:0 24px;
  display:grid;grid-template-columns:minmax(380px,450px) 1fr;gap:56px;align-items:center;
}
.hero-text{max-width:560px}
.hero-eyebrow{
  display:inline-flex;align-items:center;gap:8px;
  background:rgba(123,91,240,.1);
  color:var(--vd);
  padding:7px 14px;border-radius:100px;
  font-size:.78rem;font-weight:600;letter-spacing:.01em;
  margin-bottom:20px;
}
.hero-eyebrow svg{width:13px;height:13px}
.hero h1{
  font-size:clamp(1.9rem,2.8vw,2.7rem);
  font-weight:800;line-height:1.1;letter-spacing:-.02em;
  margin-bottom:20px;color:var(--g);
}
.hero h1 em{font-style:normal;color:var(--v);background:linear-gradient(120deg,transparent 0%,transparent 50%,rgba(169,139,247,.25) 50%,rgba(169,139,247,.25) 100%);padding:0 4px}
.hero-features{list-style:none;margin:0 0 28px;padding:0;display:inline-flex;flex-direction:column;gap:10px;text-align:left}
.hero-features li{display:flex;align-items:center;gap:10px;font-size:1rem;color:#374151}
.hero-features li svg{width:18px;height:18px;flex-shrink:0;color:var(--v)}
.hero-features li.hero-features-last{font-size:1.05rem;color:var(--g)}
.hero-features li.hero-features-last svg{color:#059669}
.hero-ctas{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:28px}
.hero-price{font-size:.85rem;color:var(--g2)}
.hero-price strong{color:var(--g);font-weight:600}

/* LE PRODUIT DANS LE HERO. La capture est ENTIÈRE au-dessus du pli — jamais
   coupée par le bas de l'écran. Pour rester lisible malgré ça, le cadre
   déborde du gabarit vers le bord droit de la fenêtre quand la place existe. */
.hero-app{
  position:relative;
  /* min-width:0 : sans lui, la piste de grille s'élargit à la largeur
     min-content de la scène (le bandeau du jour, incompressible) et la page
     déborde sur téléphone au lieu de laisser les textes s'ellipser. */
  min-width:0;
  margin-right:clamp(-220px,calc((1200px - 100vw)/2 + 24px),0px);
}
/* Le cadre de capture du héros a disparu : la scène animée (section 24) porte
   son propre cadre, et parle sans légende. */

@media(max-width:900px){
  .hero{padding:100px 0 60px}
  .hero-inner{grid-template-columns:1fr;gap:40px;text-align:center}
  .hero-text{margin:0 auto}
  .hero-ctas{justify-content:center}
  .hero-app{margin-right:0}
}
@media(max-width:600px){
  .hero{padding:88px 0 48px}
  .hero h1{font-size:1.75rem;margin-bottom:16px}
  .hero-features{margin-bottom:20px}
  .hero-inner{gap:32px}
}
/* Shield pill — posée sur le coin du cadre, hors de son overflow */
.hero-shield{
  position:absolute;
  /* Posée sur la barre de fenêtre (décorative) : plus bas, elle recouvrirait
     la barre d'onglets de la scène. Deux lignes à 250px, et float-logo ne
     descend jamais sous la position de repos. */
  top:-30px;right:-10px;z-index:2;
  background:#fff;
  border-radius:12px;
  padding:10px 16px;
  display:flex;align-items:center;gap:9px;
  font-size:.78rem;font-weight:600;color:#059669;
  box-shadow:0 8px 24px rgba(16,185,129,.18);
  border:1px solid rgba(16,185,129,.2);
  animation:float-logo 5s ease-in-out infinite .5s;
  max-width:250px;line-height:1.35;text-align:left;
}
.hero-shield svg{width:18px;height:18px;flex-shrink:0}
@media(max-width:600px){
  .hero-shield{display:none}
}

/* ===== SECTION COMMONS ===== */
section{padding:80px 0}

/* --- Bandeau de confiance, repris de la version d'origine --- */
.trust {
  background: #faf8ff; color: #4b5563;
  padding: 20px 0;
  border-top: 1px solid rgba(123, 91, 240, .08);
  border-bottom: 1px solid rgba(123, 91, 240, .08);
}
.trust-inner { display: flex; align-items: center; justify-content: center; gap: 36px; flex-wrap: wrap; }
.trust-item { display: flex; align-items: center; gap: 8px; font-size: .82rem; font-weight: 500; white-space: nowrap; }
.trust-item svg { width: 16px; height: 16px; color: var(--v); flex-shrink: 0; }
@media (max-width: 600px) {
  .trust-inner { gap: 14px; }
  .trust-item { font-size: .75rem; }
}

/* --- Avant / après ------------------------------------------------------- */
/* Le bandeau de confiance vit DANS la section (première pièce de la slide en
   scrollytelling) : la section lui rend son padding haut, le .wrap reprend
   l'écart — au pixel, le flux normal ne change pas. */
#avant-apres { padding-top: 0; }
#avant-apres > .wrap { margin-top: 92px; }
/* Avant/après masqué (08/09/2026) : la section ne porte plus que le bandeau
   de confiance, elle ne doit donc plus rendre l'écart bas de .sec. */
.avap--off { padding-bottom: 0; }
.avap-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; margin-top: 44px; }
.avap-col { border-radius: var(--rad-lg); padding: 30px 30px 34px; border: 1px solid var(--brd); }
.avap-col--before { background: var(--bg-soft); }
.avap-col--after {
  background:
    radial-gradient(ellipse 80% 60% at 20% 0%, rgba(169, 139, 247, .14) 0%, transparent 60%),
    linear-gradient(180deg, #FBF9FF 0%, #F5F0FF 100%);
  border-color: rgba(123, 91, 240, .28);
  box-shadow: var(--sh);
}
.avap-head {
  display: block;
  font-size: .78rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--g2);
}
.avap-col--after .avap-head { color: var(--vd); }
.avap-col ul { list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.avap-col li { display: flex; gap: 12px; font-size: .96rem; line-height: 1.5; color: #4b5563; }
.avap-col--after li { color: var(--g); }
.avap-col li svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 3px; }
.avap-col--before li svg { color: var(--g3); }
.avap-col--after li svg { color: #059669; }
@media (max-width: 820px) {
  .avap-grid { grid-template-columns: 1fr; gap: 18px; margin-top: 32px; }
  .avap-col { padding: 24px 22px 28px; }
}

/* ===================================================
   5. L'appel en classe — maquette fidèle de l'écran enseignant
   =================================================== */

/* La section se veut COMPACTE : l'en-tête vit dans la colonne de gauche, face
   au téléphone — pas d'étage de titre au-dessus de la grille. */
#appel.sec { padding: 64px 0; }
.appel-grid {
  display: grid; grid-template-columns: .8fr 1.2fr; gap: 44px; align-items: center;
}
/* Dans une colonne de ~420 px, le titre pleine taille ferait quatre lignes :
   on le borne pour qu'il en garde deux. */
.appel-grid .title { font-size: clamp(1.7rem, 2.6vw, 2.2rem); }
.appel-points { list-style: none; margin: 26px 0 0; display: grid; gap: 13px; }
.appel-points li { display: flex; gap: 12px; font-size: .96rem; color: var(--g2); }
.appel-points svg { width: 19px; height: 19px; color: var(--v); flex-shrink: 0; margin-top: 2px; }
.appel-points b { color: var(--g); font-weight: 700; }

.appel-scene { display: grid; grid-template-columns: 272px 1fr; gap: 24px; align-items: center; }
/* Les couleurs de l'appel et les trois courbes, redéclarées sous la scène.
   Le :root de home.css les pose déjà pour la page d'accueil, mais le blog ne
   sert que cette section à ses articles : sans cette redéclaration, chaque
   var(--ap-*) y devient invalide et le téléphone sort sans écran (relevé le
   05/09/2026 sur l'article J29). Mêmes valeurs qu'en tête de fichier, à tenir
   à l'identique. */
#appelScene {
  --ease: cubic-bezier(.22, .61, .36, 1);
  --e-out: cubic-bezier(.16, 1, .3, 1);
  --e-spring: cubic-bezier(.34, 1.42, .5, 1);
  --ap-violet: #6d3bec;
  --ap-violet-50: #f4f1fe;
  --ap-violet-100: #e6e0f7;
  --ap-ink: #16131f;
  --ap-muted: #79737f;
  --ap-line-2: #e1ddec;
  --ap-bg: #f6f4fb;
  --ap-red: #db362a; --ap-red-50: #fdeeed; --ap-red-100: #f9d7d3;
  --ap-amber: #a9600b; --ap-amber-50: #fbf2e3; --ap-amber-100: #f5e0bf;
  --ap-green: #199a51; --ap-green-50: #eaf7ef; --ap-green-100: #cbead7;
  --ap-sh-sm: 0 1px 2px rgba(30, 20, 55, .05), 0 1px 3px rgba(30, 20, 55, .05);
}

/* --- Le téléphone de l'enseignant --- */
.phone {
  position: relative;
  border-radius: 42px; padding: 11px;
  background: linear-gradient(160deg, #2E2440, #16131f);
  box-shadow: 0 26px 54px -18px rgba(30, 20, 55, .45);
}
/* L'encoche. Elle chevauche le haut de l'écran, d'où le z-index : c'est elle
   qui dit « téléphone » en un coup d'œil, bien avant les coins arrondis. */
.phone::before {
  content: ""; position: absolute; z-index: 5;
  top: 11px; left: 50%; transform: translateX(-50%);
  width: 46%; height: 22px;
  background: #16131f; border-radius: 0 0 14px 14px;
}
/* Le téléphone arrive de trois quarts et se remet d'aplomb. Il finit à plat,
   sans quoi le texte de la maquette serait flou en permanence — c'est un
   écran qu'on doit pouvoir lire, pas une photo de produit. */
.appel-scene.is-in .phone { animation: phoneIn 1.15s var(--e-out) .1s backwards; }
@keyframes phoneIn {
  from { opacity: 0; transform: perspective(1200px) rotateY(-17deg) rotateX(7deg) translate3d(-24px, 34px, 0) scale(.93); }
  to { opacity: 1; transform: perspective(1200px) rotateY(0) rotateX(0) translate3d(0, 0, 0) scale(1); }
}
/* Les cases se posent en balayage, comme une classe qui s'affiche. Le retard
   par case est posé par motion.js dans --d.
   `backwards` et non `both` : sitôt l'entrée finie, la transformation
   redevient celle du CSS, sans quoi le creux du tap (.is-tapped) resterait
   sans effet, écrasé à vie par l'animation. */
.appel-scene.is-in .ap-num { animation: tileIn .42s var(--e-spring) var(--d, 0ms) backwards; }
@keyframes tileIn {
  from { opacity: 0; transform: scale(.5); }
  to { opacity: 1; transform: none; }
}
/* `padding-top` généreux : l'en-tête de l'écran doit commencer SOUS l'encoche,
   sinon « CE2 A » passerait dessous. */
.phone-screen {
  background: var(--ap-bg); border-radius: 32px; overflow: hidden;
  padding: 30px 14px 0;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  color: var(--ap-ink);
}
.ap-hdr { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.ap-hdr .ap-class { font-size: 1.28rem; font-weight: 700; letter-spacing: -.02em; }
.ap-dir {
  padding: 6px 10px; border-radius: 999px;
  background: var(--ap-violet-50); border: 1px solid var(--ap-violet-100);
  color: var(--ap-violet); font-size: .68rem; font-weight: 650; white-space: nowrap;
}
.ap-howto { margin: 8px 0 10px; font-size: .72rem; color: var(--ap-muted); }

.ap-datebar {
  display: flex; align-items: center; gap: 6px; margin-bottom: 6px;
  background: var(--ap-violet-50); border: 1px solid var(--ap-violet-100);
  border-radius: 12px; padding: 3px;
}
.ap-datebar .arw {
  width: 30px; height: 30px; flex: none; border-radius: 9px;
  color: var(--ap-violet); font-size: 1.1rem; font-weight: 700; line-height: 1;
  display: grid; place-items: center;
}
.ap-datebar .lbl { flex: 1; text-align: center; font-size: .8rem; font-weight: 700; letter-spacing: -.01em; }

.ap-seg {
  display: flex; width: max-content; gap: 3px; margin: 0 auto 10px; padding: 3px;
  background: var(--ap-violet-50); border: 1px solid var(--ap-violet-100); border-radius: 999px;
}
.ap-seg span {
  padding: 4px 12px; border-radius: 999px; font-size: .68rem; font-weight: 600; color: var(--ap-muted);
}
.ap-seg span.on { background: #fff; color: var(--ap-violet); box-shadow: var(--ap-sh-sm); font-weight: 650; }

/* La grille de NUMÉROS — aucun nom d'élève n'existe sur cet écran : le serveur
   ne reçoit que des numéros d'ordre. Une maquette qui afficherait des noms
   dirait le contraire de ce que la page promet juste à côté. */
.ap-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px;
  padding-bottom: 10px;
}
.ap-num {
  position: relative; aspect-ratio: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  border: 1px solid var(--ap-line-2); border-radius: 14px; background: #fff;
  font-size: 1.1rem; font-weight: 750; color: var(--ap-ink);
  box-shadow: var(--ap-sh-sm);
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease), transform .12s var(--ease);
}
.ap-num .tag {
  font-size: .38em; font-weight: 750; text-transform: uppercase; letter-spacing: .04em; line-height: 1;
  min-height: .38em;
}
.ap-num.is-abs { border-color: var(--ap-red-100); background: var(--ap-red-50); color: var(--ap-red); }
.ap-num.is-prev { border-color: var(--ap-amber-100); background: var(--ap-amber-50); color: var(--ap-amber); }
.ap-num.is-just { border-color: var(--ap-green-100); background: var(--ap-green-50); color: var(--ap-green); }
/* Le creux du doigt, puis le rebond : une case qui change d'état sans reculer
   d'abord ressemble à une couleur qui se substitue, pas à un appui. */
.ap-num.is-tapped { transform: scale(.86); }
.ap-num.is-pop { animation: numPop .46s var(--e-spring); }
@keyframes numPop {
  0% { transform: scale(.86); }
  45% { transform: scale(1.13); }
  100% { transform: scale(1); }
}
.ap-num .tag { transition: opacity .2s; }
.ap-num.is-pop .tag { animation: tagIn .4s var(--e-out) .1s backwards; }
@keyframes tagIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

.ap-foot { margin: 0 -14px; padding: 10px 14px 14px; background: var(--ap-bg); }
.ap-go {
  display: block; width: 100%; padding: 12px; border-radius: 15px;
  background: var(--ap-violet); color: #fff; font-size: .86rem; font-weight: 700;
  letter-spacing: -.01em; text-align: center;
  box-shadow: 0 8px 22px rgba(74, 30, 176, .26);
  transition: transform .12s var(--ease), background .18s var(--ease);
}
.ap-go.is-pressed { transform: translateY(1px) scale(.96); background: #5b28d8; }
.ap-go.is-done { background: var(--ap-green); box-shadow: 0 8px 22px rgba(25, 154, 81, .26); animation: goDone .6s var(--e-spring); }
@keyframes goDone {
  0% { transform: scale(.96); }
  40% { transform: scale(1.05); }
  100% { transform: scale(1); }
}

/* Aucun repère de doigt : la case qui change de couleur DIT déjà le geste.
   Un cercle qui la survole ajoute un curseur factice là où un vrai doigt ne
   laisse aucune trace. */

/* --- La console de direction (onglet Appel, vue Jour) --- */
.console {
  position: relative;
  background: #fff; border: 1px solid var(--brd); border-radius: var(--rad-lg);
  padding: 20px 22px 22px; box-shadow: var(--sh);
  transition: box-shadow .4s var(--e-out), border-color .4s;
}
/* L'appel arrive. La console encaisse : c'est le seul moment où le regard doit
   passer du téléphone à l'écran de direction. */
.console.is-hit { border-color: var(--vl); box-shadow: 0 0 0 4px rgba(123, 91, 240, .12), 0 18px 40px -16px rgba(76, 46, 160, .4); }
.console-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  white-space: nowrap;
}
.console-head b { font-size: .98rem; font-weight: 700; color: var(--g); }
.console-head .cnt { font-size: .84rem; color: var(--g2); font-variant-numeric: tabular-nums; }
.console-head .cnt em { font-style: normal; color: var(--v); font-weight: 800; display: inline-block; }
.console-head .cnt em.is-up { animation: countUp .5s var(--e-spring); }
@keyframes countUp {
  0% { transform: translateY(6px) scale(.8); opacity: .3; }
  100% { transform: none; opacity: 1; }
}
.console-bar { height: 6px; border-radius: 100px; background: #EFECF9; margin: 12px 0 16px; overflow: hidden; }
.console-bar i { display: block; height: 100%; border-radius: 100px; background: var(--v); width: 0; transition: width .7s var(--e-out); }
.console-chips { display: flex; flex-wrap: wrap; gap: 8px; }
/* Ces valeurs — pas de bordure, .78rem / 650, couleur héritée, blanc
   insécable, aucune transition, coche à l'échelle .5 — sont celles que la
   page CALCULAIT avant la dépose de la scène certificats (05/09/2026) : son
   préfixe « cs- » (certificat scène) entrait en collision avec celui-ci
   (classe statut), et sa règle .cs-chip, plus bas dans le fichier, écrasait
   la nôtre — avec des var(--cs-*) définies seulement sous #certScene, donc
   bordure et transition remises à zéro ici. On les écrit noir sur blanc pour
   que la dépose ne change aucune valeur calculée. L'intention d'origine
   (bordure --brd, .8rem / 600, --g2, fondu .35s, coche pleine) est un choix
   visuel à part : à réhabiliter si Jérémy le décide, pas en passant. */
.cs-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: 100px;
  border: 0; background: #fff;
  font-size: .78rem; font-weight: 650; color: inherit; white-space: nowrap;
  transition: none;
}
.cs-chip .ring {
  width: 11px; height: 11px; border-radius: 50%;
  border: 1.6px solid var(--g3); flex-shrink: 0;
  display: grid; place-items: center;
  transition: background .3s, border-color .3s, transform .45s var(--e-spring);
}
.cs-chip .ring svg { width: 8px; height: 8px; flex: none; opacity: 0; transform: scale(.5); transition: opacity .25s; }
.cs-chip[data-done="1"] {
  background: #EAF7EF; border-color: var(--ap-green-100); color: var(--ap-green);
}
.cs-chip[data-done="1"] .ring { border-color: var(--ap-green); background: var(--ap-green); color: #fff; }
.cs-chip[data-done="1"] .ring svg { opacity: 1; }
/* La classe qui vient de remonter, et elle seule, fait un bond. */
.cs-chip.is-fresh { animation: chipPop .55s var(--e-spring); }
@keyframes chipPop {
  0% { transform: scale(.9); }
  45% { transform: scale(1.09); }
  100% { transform: scale(1); }
}
@media (max-width: 600px) {
  .cs-chip { font-size: .74rem; padding: 6px 11px; }
}
.console-note { margin-top: 16px; font-size: .8rem; color: var(--g3); }

@media (max-width: 1000px) {
  .appel-grid { grid-template-columns: 1fr; gap: 40px; }
}
@media (max-width: 700px) {
  .appel-scene { grid-template-columns: 1fr; justify-items: center; gap: 24px; }
  .phone { width: 280px; }
  .console { width: 100%; }
}

/* ===================================================
   8. Vos données
   =================================================== */

.data-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 44px; }
.data-card {
  background: #fff; border: 1px solid var(--brd); border-radius: var(--rad-lg);
  padding: 26px 24px 28px;
}
/* La carte s'incline vers le pointeur. motion.js n'écrit que --tx et --ty ;
   la règle ne s'applique qu'au survol, ce qui laisse la révélation au scroll
   maîtresse de la transformation tant que la carte n'est pas montrée du doigt.
   Cette règle doit rester APRÈS [data-reveal].is-in : à spécificité égale,
   c'est l'ordre du fichier qui tranche. */
/* Ces cartes ne sont PAS des actions : rien ne se passe si on les touche.
   Un survol qui les soulève promet un clic qui n'existe pas — le curseur
   cherche la cible, ne la trouve pas, et l'interface a menti. Elles restent
   donc plates. Le relief est réservé à ce qui se clique : boutons, liens,
   cartes d'articles. */
.data-card, .price-note {
  transition: opacity .9s var(--e-out), transform .5s var(--e-out);
}
.data-card .ic {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
  background: var(--v-soft); color: var(--v); margin-bottom: 16px;
}
.data-card .ic svg { width: 21px; height: 21px; }
.data-card b { display: block; font-size: 1.02rem; font-weight: 700; letter-spacing: -.01em; margin-bottom: 7px; }
.data-card p { font-size: .91rem; color: var(--g2); margin: 0; }
/* Le lien « Plus de détails » de la carte RGPD : sans cette règle, le reset
   global (color:inherit, pas de soulignement) le rendrait invisible. */
.data-card p a { color: var(--v); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 860px) { .data-grid { grid-template-columns: 1fr; } }

/* ===================================================
   9. Le grand appel à l'action
   =================================================== */

.cta {
  position: relative; overflow: hidden;
  background:
    radial-gradient(ellipse 60% 70% at 20% 10%, rgba(255, 255, 255, .22) 0%, transparent 55%),
    linear-gradient(135deg, var(--v) 0%, #5B37C9 100%);
  color: #fff; text-align: center;
  padding: 88px 0;
}
/* La trame défile en continu. Le pas de la grille est de 54 px : en la
   déplaçant d'exactement 54 px, la boucle est invisible. Le débord de 60 px
   sert à ce déplacement — sans lui, la trame découvrirait un bord vide.
   C'est une simple transformation : elle n'est peinte qu'une fois. */
.cta::before {
  content: ""; position: absolute; inset: -60px; pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, .06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .06) 1px, transparent 1px);
  background-size: 54px 54px;
  mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 10%, transparent 70%);
  -webkit-mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 10%, transparent 70%);
  animation: gridPan 26s linear infinite;
  will-change: transform;
}
@keyframes gridPan { from { transform: none; } to { transform: translate3d(-54px, -54px, 0); } }

.cta > * { position: relative; }
.cta h2 {
  font-family: var(--fd); font-size: clamp(2rem, 4.6vw, 3.2rem);
  font-weight: 800; letter-spacing: -.035em; line-height: 1.05; color: #fff;
}
[data-reveal].is-in .cta h2, .cta [data-reveal].is-in h2 { animation: titleWipe 1s var(--e-out) .06s backwards; }
.cta p { margin: 20px auto 0; max-width: 54ch; font-size: 1.06rem; color: rgba(255, 255, 255, .84); }
.cta .btn-primary {
  background: #fff; color: var(--vd); font-size: 1.04rem; padding: 17px 32px;
  box-shadow: 0 14px 34px -10px rgba(20, 8, 60, .5);
}
.cta .btn-primary:hover { background: #fff; color: #4E2CB8; }
.cta-ctas { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin-top: 34px; }
.cta-fine { margin-top: 20px; font-size: .86rem; color: rgba(255, 255, 255, .72); }
.cta-steps {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 26px;
  margin-top: 34px; font-size: .87rem; color: rgba(255, 255, 255, .82);
}
.cta-step { display: inline-flex; align-items: center; gap: 9px; }
.cta-step .n {
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  background: rgba(255, 255, 255, .18); color: #fff;
  display: grid; place-items: center; font-size: .72rem; font-weight: 800;
  transition: transform .45s var(--e-spring), background .3s;
}
.cta-steps.is-in .cta-step .n { animation: stepPop .6s var(--e-spring) var(--d, 0ms) backwards; }
@keyframes stepPop {
  from { transform: scale(0); }
  to { transform: none; }
}
.cta-step:hover .n { transform: scale(1.22); background: rgba(255, 255, 255, .34); }

/* ===================================================
   10. Tarif
   =================================================== */

.price { text-align: center; }
.price-hero {
  display: flex; align-items: flex-start; justify-content: center; gap: 6px;
  margin-top: 26px;
}
.price-hero .amount {
  font-family: var(--fd); font-size: clamp(4rem, 11vw, 7rem);
  font-weight: 800; letter-spacing: -.05em; line-height: .9;
  background: linear-gradient(180deg, var(--v) 0%, #5B37C9 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Le prix est l'argument : il arrive de plus loin que le reste. */
[data-reveal].is-in .price-hero .amount { animation: priceIn .95s var(--e-spring) .12s backwards; }
@keyframes priceIn {
  from { opacity: 0; transform: translate3d(0, 26px, 0) scale(.68); }
  to { opacity: 1; transform: none; }
}
[data-reveal].is-in .price-hero .per { animation: introUp .8s var(--e-out) .42s backwards; }
.price-hero .per { font-size: 1.05rem; font-weight: 650; color: var(--g2); margin-top: 12px; }
/* « Sans engagement » se lit avant le prix : à sa gauche, centré sur la
   hauteur du chiffre. Sur un écran étroit il passe au-dessus, sur sa ligne. */
.price-hero .engagement {
  align-self: center; margin-right: 14px;
  font-size: 1.06rem; font-weight: 600; color: var(--g); white-space: nowrap;
}
[data-reveal].is-in .price-hero .engagement { animation: introUp .8s var(--e-out) .42s backwards; }
@media (max-width: 640px) {
  .price-hero { flex-wrap: wrap; }
  .price-hero .engagement { flex-basis: 100%; text-align: center; margin: 0 0 10px; }
}
.price-line { margin-top: 14px; font-size: 1.06rem; color: var(--g); font-weight: 600; }
.price-line em { font-style: normal; color: var(--v); }

/* Deux colonnes, pas trois : à trois, chaque ligne repassait sur deux niveaux
   et la liste se lisait en zigzag. À deux, une entrée tient sur une ligne. */
.price-list {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 40px;
  max-width: 820px; margin: 36px auto 0; text-align: left;
}
.price-list li {
  display: flex; gap: 11px; align-items: center;
  padding: 9px 2px; font-size: .96rem; color: var(--g);
}
.price-list svg { width: 18px; height: 18px; color: var(--ok); flex-shrink: 0; }

@media (max-width: 720px) { .price-list { grid-template-columns: 1fr; } }
/* Chaque coche se pose d'un coup après sa ligne : on lit une liste qui se
   valide, pas un pavé qui apparaît. */
.price-list.is-in svg { animation: tickIn .45s var(--e-spring) calc(var(--d, 0ms) + 160ms) backwards; }
@keyframes tickIn {
  from { opacity: 0; transform: scale(.2) rotate(-45deg); }
  to { opacity: 1; transform: none; }
}

/* Une seule carte depuis le 23/08 (mairie, mandat et pause fusionnés) :
   centrée, pas plus large que la liste des fonctionnalités au-dessus. */
.price-notes { max-width: 820px; margin: 44px auto 0; text-align: left; }
.price-note { background: #fff; border: 1px solid var(--brd); border-radius: var(--rad); padding: 20px 22px; }
.price-note b { display: block; margin-bottom: 7px; font-size: .95rem; }
.price-note p { font-size: .88rem; color: var(--g2); margin: 0; }
.price-note a { color: var(--v); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }


/* ===================================================
   11. Mandat administratif (page dédiée)
   =================================================== */

.page-head { padding: 132px 0 0; background: linear-gradient(180deg, #FBF9FF 0%, #fff 100%); }
.page-head .back { display: inline-flex; align-items: center; gap: 6px; font-size: .88rem; font-weight: 600; color: var(--v); margin-bottom: 22px; }
.page-head .back:hover { text-decoration: underline; }
.page-head h1 {
  font-family: var(--fd); font-size: clamp(2rem, 5vw, 3rem);
  font-weight: 800; letter-spacing: -.035em; line-height: 1.06; color: var(--g);
}

.mandat-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin: 40px 0 34px; }
.mandat-step { background: #fff; border: 1px solid var(--brd); border-radius: var(--rad); padding: 22px; }
.mandat-step .n {
  width: 27px; height: 27px; border-radius: 50%; background: var(--v); color: #fff;
  display: grid; place-items: center; font-size: .82rem; font-weight: 800; margin-bottom: 12px;
}
.mandat-step b { display: block; font-size: .95rem; margin-bottom: 5px; }
.mandat-step p { font-size: .86rem; color: var(--g2); margin: 0; }
.mandat-price { text-align: center; font-size: 1.05rem; font-weight: 700; margin-bottom: 6px; }
.mandat-price span { color: var(--g2); font-weight: 500; font-size: .88rem; display: block; margin-top: 3px; }

.mandat-form { background: #fff; border: 1px solid var(--brd); border-radius: var(--rad-lg); padding: 28px; margin-top: 30px; }
.mandat-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.mandat-field { margin-bottom: 16px; }
.mandat-field label { display: block; font-size: .84rem; font-weight: 600; margin-bottom: 6px; }
.mandat-field input, .mandat-field textarea {
  width: 100%; padding: 11px 13px; border: 1.5px solid var(--brd); border-radius: var(--rad-sm);
  font: inherit; font-size: .92rem; transition: border-color .18s;
}
.mandat-field input:focus, .mandat-field textarea:focus { outline: none; border-color: var(--v); }
.mandat-field .hint { font-size: .78rem; color: var(--g3); margin-top: 5px; }
.mandat-hp { position: absolute; left: -9999px; }
.mandat-msg { margin-top: 12px; font-size: .88rem; }
.mandat-msg.ok { color: var(--ok); }
.mandat-msg.err { color: var(--alert); }

@media (max-width: 760px) {
  .mandat-steps { grid-template-columns: 1fr; }
  .mandat-row { grid-template-columns: 1fr; }
}

/* ===================================================
   12. FAQ
   =================================================== */

.faq-list { max-width: 780px; margin: 40px auto 0; }
.faq-item { border-bottom: 1px solid var(--brd); }
.faq-q {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 18px;
  background: none; padding: 20px 0; text-align: left;
  font-size: 1rem; font-weight: 650; color: var(--g);
}
/* Repère de survol : un trait qui se déroule dans la marge. Une indentation
   au survol aurait recalculé la mise en page de la ligne à chaque passage. */
.faq-q { position: relative; transition: color .22s; }
.faq-q::before {
  content: ""; position: absolute; left: -16px; top: 18px; bottom: 18px; width: 3px;
  border-radius: 3px; background: var(--v);
  transform: scaleY(0); transition: transform .38s var(--e-out);
}
.faq-q:hover::before { transform: scaleY(1); }
.faq-q:hover { color: var(--v); }
.faq-q svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--v); transition: transform .4s var(--e-spring); }
.faq-q:hover svg { transform: scale(1.2); }
.faq-item.open .faq-q svg { transform: rotate(45deg); }
.faq-a { max-height: 0; overflow: hidden; transition: max-height .45s var(--e-out); }
.faq-item.open .faq-a { max-height: 620px; }
.faq-a p {
  font-size: .92rem; color: var(--g2); padding-bottom: 20px; margin: 0;
  opacity: 0; transform: translate3d(0, -8px, 0);
  transition: opacity .35s var(--e-out), transform .45s var(--e-out);
}
.faq-item.open .faq-a p { opacity: 1; transform: none; transition-delay: .1s; }
.faq-a a { color: var(--v); text-decoration: underline; text-underline-offset: 3px; }

/* ===================================================
   13. Le mot du fondateur
   =================================================== */

.about-card {
  display: grid; grid-template-columns: 140px 1fr; gap: 30px; align-items: start;
  max-width: 860px; margin: 40px auto 0;
  background: #fff; border: 1px solid var(--brd); border-radius: var(--rad-lg); padding: 32px;
}
.about-card img { width: 140px; height: 140px; border-radius: 18px; object-fit: cover; }
.about-card h3 { font-family: var(--fd); font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; }
.about-card .role { font-size: .86rem; color: var(--g2); margin-bottom: 14px; }
.about-card p { font-size: .94rem; color: #4b5563; margin-bottom: 12px; }
.about-quote { border-left: 3px solid var(--v); padding-left: 16px; font-style: italic; color: var(--g); }
.about-mail { font-size: .88rem; color: var(--g2); margin-top: 14px; }
.about-mail a { color: var(--v); font-weight: 600; }

@media (max-width: 700px) {
  .about-card { grid-template-columns: 1fr; text-align: center; justify-items: center; }
}

/* ===================================================
   14. Blog
   =================================================== */

.blog-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 22px; margin-top: 40px; }
.blog-home-card {
  display: block; background: #fff; border: 1px solid var(--brd); border-radius: var(--rad); overflow: hidden;
  transition: transform .25s var(--ease), box-shadow .25s;
}
.blog-home-card:hover { transform: translate3d(0, -7px, 0); box-shadow: 0 26px 46px -18px rgba(31, 41, 55, .3); }
.bh-media { position: relative; aspect-ratio: 16 / 9; background: var(--v-soft); overflow: hidden; }
.bh-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--e-out); }
.blog-home-card:hover .bh-media img { transform: scale(1.06); }
.bh-read { transition: transform .35s var(--e-out); display: inline-block; }
.blog-home-card:hover .bh-read { transform: translateX(5px); }
.bh-ph { width: 100%; height: 100%; background: linear-gradient(135deg, var(--vl), var(--v)); }
.bh-tag {
  position: absolute; left: 12px; bottom: 12px;
  background: rgba(255, 255, 255, .95); color: var(--vd);
  font-size: .7rem; font-weight: 700; padding: 4px 10px; border-radius: 100px;
}
.bh-body { padding: 18px 20px 22px; }
.bh-meta { display: flex; align-items: center; gap: 8px; font-size: .76rem; color: var(--g3); margin-bottom: 8px; }
.bh-dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; }
.bh-body h3 { font-size: 1.02rem; font-weight: 700; letter-spacing: -.015em; line-height: 1.3; margin-bottom: 7px; }
.bh-body p { font-size: .88rem; color: var(--g2); margin: 0 0 12px; }
.bh-read { font-size: .84rem; font-weight: 700; color: var(--v); }

/* ===================================================
   15. CTA collant mobile
   =================================================== */

.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  background: rgba(255, 255, 255, .96); backdrop-filter: blur(12px);
  border-top: 1px solid var(--brd); padding: 12px 16px;
  transform: translateY(110%); transition: transform .3s var(--ease);
  display: none;
}
.sticky-cta.visible { transform: none; }
.sticky-cta .btn-primary { width: 100%; }
@media (max-width: 780px) {
  .sticky-cta { display: block; }
  body { padding-bottom: 74px; }
}

/* ===================================================
   17. Scène : l'onglet Mails, le mail du parent ouvert et traité
   ===================================================
   Remplace la capture de la section #geste-mails ; animée par motion.js §3.
   Complète design-tokens.css (--v, --brd, --rad-lg…) et les courbes du haut
   de ce fichier (--e-out, --e-spring ; pastille hero-shield pour la parenté
   de la pill de confirmation).

   La scène est une MAQUETTE FIDÈLE de l'onglet Mails de l'application, en
   trois volets comme à l'écran : le rail des dossiers (raccourcis, bascule
   Nominative / École, dossiers de classement), la liste (recherche,
   Rédiger, filtres, lignes avec « Mère de … (CP A) » et pastilles À traiter
   / Prioritaire), le volet de lecture (Répondre, Supprimer, Saisir absence,
   en-tête avec le parent reconnu, « Classé dans »). Le mail de Claire
   Roussel arrive, le parent est reconnu, la ligne s'ouvre à droite, « Saisir
   absence » est pressé, la confirmation se pose.

   Tenue héritée de motion.js : seuls transform, opacity et couleurs bougent.
   La ligne qui arrive a sa place réservée (rien n'est poussé) ; les deux
   contenus du volet de lecture sont rendus en permanence, l'un sur l'autre,
   en fondu croisé ; aucune hauteur, aucune marge n'est animée. La pill
   déborde du bas du cadre : pas d'overflow:hidden sur .ms-scene (celui de
   .ms-win, nécessaire aux coins de la barre, ne la concerne pas). Les var()
   portent un repli pour que la scène survive hors de la page d'accueil. */

.ms-scene {
  position: relative;
  width: 100%; max-width: 620px; margin: 0 auto;
  font-size: 10.5px; /* l'unité de la maquette : tout le reste est en em */
}

.ms-win {
  background: var(--card, #fff);
  border: 1px solid var(--brd, #e5e7eb);
  border-radius: var(--rad-lg, 20px);
  box-shadow: var(--sh-lg, 0 12px 40px rgba(123, 91, 240, .18));
  overflow: hidden;
  transition: opacity .45s var(--e-out, cubic-bezier(.16, 1, .3, 1));
}
.ms-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 11px 16px;
  background: var(--bg-soft, #fafafa);
  border-bottom: 1px solid var(--brd, #e5e7eb);
}
.ms-dot { width: 9px; height: 9px; border-radius: 50%; background: #e3ddf3; }
.ms-bartitle {
  margin-left: 8px; font-size: .74rem; font-weight: 650; color: var(--g2, #6b7280);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ms-badge {
  margin-left: auto; flex: none;
  padding: 3px 10px; border-radius: 100px;
  background: var(--v-soft, #f3efff); border: 1px solid rgba(123, 91, 240, .28);
  color: var(--v, #7B5BF0); font-size: .7rem; font-weight: 700;
}

/* --- Les trois volets --------------------------------------------------- */
.ms-panes {
  display: grid; grid-template-columns: 12.4em 20em 1fr;
  min-height: 34em;
  background: var(--bg-soft, #fafafa);
}

/* Volet 1 : le rail. Une liste de raccourcis, la bascule, les dossiers. */
.ms-rail { padding: 1.1em .8em; border-right: 1px solid var(--brd, #e5e7eb); }
.ms-rail-h {
  font-size: .68em; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--g3, #9ca3af); padding: 0 .6em .5em;
}
.ms-rail-i {
  display: flex; align-items: center; gap: .55em;
  padding: .45em .6em; border-radius: .7em;
  font-size: .96em; font-weight: 600; color: var(--g, #1f2937);
  white-space: nowrap; overflow: hidden;
}
.ms-rail-i i { width: 1em; height: .85em; flex: none; border: 1.4px solid var(--g3, #9ca3af); border-radius: .2em; opacity: .75; }
.ms-rail-i b { margin-left: auto; font-size: .78em; font-weight: 700; color: #dc2626; }
.ms-rail-i.is-on { background: var(--v-soft, #f3efff); color: var(--vd, #6A4AE0); }
.ms-rail-i.is-on i { border-color: var(--v, #7B5BF0); opacity: 1; }
.ms-rail-f { font-weight: 500; color: var(--g2, #6b7280); }
.ms-box {
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px;
  margin: .8em 0 .6em; padding: 2px; border-radius: .8em;
  background: #fff; border: 1px solid var(--brd, #e5e7eb);
}
.ms-box span { text-align: center; padding: .35em 0; border-radius: .6em; font-size: .82em; font-weight: 600; color: var(--g2, #6b7280); }
.ms-box span.is-on { background: var(--v-soft, #f3efff); color: var(--vd, #6A4AE0); }

/* Volet 2 : la liste. */
.ms-list { padding: 1.1em .9em; border-right: 1px solid var(--brd, #e5e7eb); background: #fff; }
.ms-tools { display: flex; gap: .5em; }
.ms-search {
  flex: 1; min-width: 0; padding: .55em .9em; border-radius: .8em;
  border: 1px solid var(--brd, #e5e7eb); background: #fff;
  font-size: .86em; color: var(--g3, #9ca3af); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ms-write {
  flex: none; padding: .55em .9em; border-radius: .8em;
  background: var(--v, #7B5BF0); color: #fff; font-size: .86em; font-weight: 700;
}
.ms-filters { display: flex; gap: .4em; margin: .7em 0 .9em; }
.ms-filters span {
  flex: 1; text-align: center; padding: .4em 0; border-radius: .7em;
  border: 1px solid var(--brd, #e5e7eb); font-size: .78em; font-weight: 600; color: var(--g2, #6b7280);
}
.ms-row {
  padding: .7em .8em; border-radius: .9em; margin-bottom: .2em;
  transition: opacity .5s var(--e-out, cubic-bezier(.16, 1, .3, 1)),
              transform .6s var(--e-out, cubic-bezier(.16, 1, .3, 1)),
              background-color .35s;
}
.ms-row-top { display: flex; align-items: center; gap: .5em; }
.ms-row-top b { font-size: .98em; font-weight: 700; color: var(--g, #1f2937); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ms-row-top span { margin-left: auto; flex: none; font-size: .78em; color: var(--g3, #9ca3af); }
.ms-unread { flex: none; width: .7em; height: .7em; border-radius: 50%; background: var(--v, #7B5BF0); }
/* « Mère de Léon Roussel (CP A) » : la reconnaissance du parent — la ligne
   a toujours sa place, seul son dévoilement est animé (is-known). */
.ms-who {
  font-size: .82em; font-weight: 600; color: var(--v, #7B5BF0);
  margin-top: .15em;
  transition: opacity .35s var(--e-out, ease-out), transform .45s var(--e-out, ease-out);
}
.ms-subj { display: flex; align-items: center; gap: .3em; font-size: .88em; color: var(--g2, #6b7280); margin-top: .15em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ms-subj svg { width: .9em; height: .9em; flex: none; color: var(--g3, #9ca3af); }
.ms-pills { display: flex; gap: .4em; margin-top: .45em; }
.ms-pill-s {
  padding: .2em .7em; border-radius: 100px;
  border: 1px solid var(--brd, #e5e7eb); background: var(--bg-soft, #fafafa);
  font-size: .72em; font-weight: 700; color: var(--g2, #6b7280);
}
.ms-pill-s--todo { background: #fef2f2; border-color: #fecaca; color: #dc2626; }
.ms-pill-s--prio { background: #ecfdf5; border-color: #a7f3d0; color: #059669; }

/* La ligne sélectionnée : fond violet pâle, comme à l'écran. Au repos c'est
   le mail de Karim Hamon (déjà ouvert) ; is-open passe la sélection au mail
   qui vient d'arriver. */
.ms-row--prev { background: var(--v-soft, #f3efff); }
.ms-scene.is-open .ms-row--prev { background: transparent; }
.ms-scene.is-open .ms-row--new { background: var(--v-soft, #f3efff); }

/* Le mail qui arrive : visible d'office (état sans JS) ; pendant la boucle,
   is-play le retire et is-in le fait glisser en place, la ligne « Mère de »
   arrivant avec is-known. */
.ms-scene.is-play .ms-row--new { opacity: 0; transform: translateY(-1em); }
.ms-scene.is-play.is-in .ms-row--new { opacity: 1; transform: none; }
.ms-scene.is-play .ms-row--new .ms-who { opacity: 0; transform: translateX(-.4em); }
.ms-scene.is-play.is-known .ms-row--new .ms-who { opacity: 1; transform: none; }

/* Volet 3 : la lecture. */
.ms-read { position: relative; padding: 1.1em 1.1em; background: #fff; }
.ms-actions { display: flex; gap: .45em; }
.ms-act {
  padding: .55em .9em; border-radius: .8em; white-space: nowrap;
  border: 1px solid var(--brd, #e5e7eb); background: #fff;
  font-size: .84em; font-weight: 700; color: var(--g, #1f2937);
  transition: transform .12s ease-out, background-color .3s, border-color .3s, color .3s;
}
.ms-act--main { background: var(--v, #7B5BF0); border-color: var(--v, #7B5BF0); color: #fff; }
.ms-act--del { color: #dc2626; border-color: #fecaca; }
.ms-act--abs { position: relative; background: var(--v-soft, #f3efff); border-color: rgba(123, 91, 240, .35); color: var(--vd, #6A4AE0); }
.ms-act--abs .ms-act-l2 { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; transition: opacity .3s; }
.ms-act--abs .ms-act-l1 { transition: opacity .3s; }
.ms-act.is-pressed { transform: translateY(1px) scale(.96); }
.ms-scene.is-done .ms-act--abs { background: #EAF7EF; border-color: rgba(16, 185, 129, .35); color: #059669; }
.ms-scene.is-done .ms-act--abs .ms-act-l1 { opacity: 0; }
.ms-scene.is-done .ms-act--abs .ms-act-l2 { opacity: 1; }

/* Les deux messages, l'un sur l'autre : celui de Karim Hamon au repos, celui
   de Claire Roussel sous is-open — fondu croisé, la place est la même. */
.ms-msg {
  position: absolute; left: 1.1em; right: 1.1em; top: 4.4em;
  transition: opacity .4s var(--e-out, cubic-bezier(.16, 1, .3, 1)),
              transform .5s var(--e-out, cubic-bezier(.16, 1, .3, 1));
}
.ms-msg--new { opacity: 0; transform: translateY(.6em); }
.ms-scene.is-open .ms-msg--prev { opacity: 0; }
.ms-scene.is-open .ms-msg--new { opacity: 1; transform: none; }
.ms-msg-head { display: flex; align-items: center; gap: .6em; flex-wrap: wrap; }
.ms-msg-head b { font-size: 1.25em; font-weight: 800; color: var(--g, #1f2937); letter-spacing: -.01em; }
.ms-chip {
  display: inline-flex; align-items: center; gap: .4em;
  padding: .25em .8em; border-radius: 100px;
  background: var(--v-soft, #f3efff); border: 1px solid rgba(123, 91, 240, .28);
  color: var(--v, #7B5BF0); font-size: .78em; font-weight: 650; white-space: nowrap;
}
.ms-chip::before { content: ""; width: .6em; height: .6em; border-radius: 50%; background: var(--v, #7B5BF0); }
.ms-msg-meta {
  margin-top: .7em; padding: .4em .8em; border-radius: .7em; display: inline-block;
  background: var(--bg-soft, #fafafa); border: 1px solid var(--brd, #e5e7eb);
  font-size: .78em; font-weight: 600; color: var(--g2, #6b7280);
}
.ms-msg-subj { margin-top: 1em; font-size: 1.1em; font-weight: 800; color: var(--g, #1f2937); }
.ms-msg-att {
  display: flex; align-items: center; gap: .5em; margin-top: .6em; padding: .45em .7em;
  border-radius: .7em; background: var(--bg-soft, #fafafa); border: 1px solid var(--brd, #e5e7eb);
  font-size: .76em; font-weight: 650; color: var(--g2, #6b7280);
}
.ms-msg-att i { width: 1.2em; height: 1.5em; border-radius: .25em; background: #ef4444; opacity: .85; }
.ms-msg p { margin: .8em 0 0; font-size: .9em; line-height: 1.55; color: var(--g2, #6b7280); }

.ms-pill {
  position: absolute; left: 50%; bottom: -20px; z-index: 3;
  transform: translate(-50%, 10px); opacity: 0;
  background: #fff;
  border: 1px solid rgba(16, 185, 129, .25);
  border-radius: 12px; padding: 10px 16px;
  color: #059669; font-size: .82rem; font-weight: 700;
  white-space: nowrap;
  box-shadow: 0 8px 24px rgba(16, 185, 129, .2);
  transition: opacity .35s var(--e-out, ease-out),
              transform .5s var(--e-spring, cubic-bezier(.34, 1.42, .5, 1));
}
.ms-scene.is-done .ms-pill { opacity: 1; transform: translate(-50%, 0); }

/* --- Remise à zéro en fondu ------------------------------------------------
   Posées en fin de feuille : à spécificité égale, elles doivent gagner sur
   is-done pour que la pill s'éteigne avec la fenêtre. */
.ms-scene.is-fade .ms-win { opacity: 0; }
.ms-scene.is-fade .ms-pill { opacity: 0; }

/* Sur une colonne (fenêtre ≤ 900 px), la scène reprend sa composition de
   620 px, entière : quand la fenêtre est plus étroite, fit-scene (main.js)
   la zoome en bloc, comme les autres scènes. */
@media (max-width: 900px) {
  .ms-scene { width: 620px; max-width: none; }
}

/* Reduced motion : design-tokens.css écrase déjà toutes les durées, mais on
   coupe explicitement — l'état final posé par motion.js §3 doit apparaître
   d'un bloc, sans micro-fondu. */
@media (prefers-reduced-motion: reduce) {
  .ms-win, .ms-row, .ms-who, .ms-act, .ms-act span, .ms-msg, .ms-pill { transition: none; }
}

/* ===================================================
   18. Scène : les documents filtrés par le nom
   ===================================================
   Remplace la capture de la section #geste-documents ; animée par motion.js §4.
   Tout est préfixé ds- et rattaché à #docScene : aucun sélecteur global.
   Le JS ne touche qu'à des classes dont l'effet est transform / opacity —
   la seule exception (order en mode reduced-motion) est posée une fois,
   à froid, jamais animée. */

#docScene {
  /* Variables locales : elles retombent sur les tokens du site quand la scène
     est collée dans index.html, et sur les valeurs en dur quand elle est
     regardée seule — la préview ne doit dépendre de rien. */
  --ds-v: var(--v, #7B5BF0);
  --ds-v-soft: var(--v-soft, #f3efff);
  --ds-ink: var(--g, #1f2937);
  --ds-mut: var(--g2, #6b7280);
  --ds-mut2: var(--g3, #9ca3af);
  --ds-brd: var(--brd, #e5e7eb);
  --ds-e: cubic-bezier(.22, .61, .36, 1);
  --ds-eo: cubic-bezier(.16, 1, .3, 1);
  --ds-spring: cubic-bezier(.34, 1.42, .5, 1);
  /* Même largeur et même centrage que les autres scènes : les trois cadres
     doivent tomber sur le même axe d'une section à l'autre. */
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  font-family: var(--fd, 'Inter Tight', -apple-system, BlinkMacSystemFont, system-ui, sans-serif);
  color: var(--ds-ink);
}

/* --- La fenêtre de l'explorateur --- */
.ds-win {
  background: var(--card, #fff);
  border: 1px solid var(--ds-brd);
  border-radius: var(--rad-lg, 20px);
  box-shadow: var(--sh-lg, 0 12px 40px rgba(123, 91, 240, .18));
  overflow: hidden;
}
.ds-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 11px 16px;
  background: var(--bg-soft, #fafafa);
  border-bottom: 1px solid var(--ds-brd);
}
.ds-dot { width: 9px; height: 9px; border-radius: 50%; background: #e3ddf3; }
.ds-bartitle { margin-left: 8px; font-size: .74rem; font-weight: 650; color: var(--ds-mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* --- Le champ de recherche --- */
.ds-search {
  position: relative;
  display: flex; align-items: center; gap: 8px;
  margin: 14px 14px 0;
  padding: 10px 12px;
  border: 1.5px solid var(--ds-brd);
  border-radius: 12px;
  background: #fff;
  transition: border-color .3s var(--ds-e), box-shadow .3s var(--ds-e);
}
#docScene.is-typing .ds-search {
  border-color: var(--ds-v);
  box-shadow: 0 0 0 3px rgba(123, 91, 240, .12);
}
.ds-loupe { width: 15px; height: 15px; flex: none; color: var(--ds-mut2); }
/* Le placeholder est hors flux : le masquer ne déplace ni la frappe ni le
   curseur — seule son opacité change. */
.ds-ph {
  position: absolute; left: 35px;
  font-size: .85rem; color: var(--ds-mut2);
  transition: opacity .2s;
}
#docScene.is-typing .ds-ph { opacity: 0; }
.ds-q { font-size: .85rem; font-weight: 650; letter-spacing: .01em; white-space: pre; }
.ds-caret {
  width: 1.5px; height: 16px; flex: none;
  background: var(--ds-v); border-radius: 1px;
  animation: ds-blink 1.06s step-end infinite;
}
@keyframes ds-blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }
/* En reduced-motion la règle globale fige l'animation caret à l'opacité 1 :
   un curseur allumé en permanence sur un état final ferait « écran gelé »,
   on l'éteint. */
#docScene.is-reduced .ds-caret { opacity: 0; }

/* --- Le chip de résultat --- */
/* La rangée garde sa hauteur même chip masqué : l'apparition ne pousse rien. */
.ds-chiprow { display: flex; align-items: center; height: 34px; margin: 8px 14px 0; }
.ds-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px 5px 6px;
  border-radius: 100px;
  background: var(--ds-v-soft);
  border: 1px solid #e4dbfc;
  color: var(--ds-v);
  font-size: .76rem; font-weight: 700; letter-spacing: -.01em;
  opacity: 0; transform: translateY(6px) scale(.95);
  transition: opacity .4s var(--ds-e), transform .5s var(--ds-spring);
}
.ds-chip.is-on { opacity: 1; transform: none; }
.ds-chip-av {
  width: 19px; height: 19px; border-radius: 50%; flex: none;
  background: var(--ds-v); color: #fff;
  font-size: .52rem; font-weight: 800; letter-spacing: .02em;
  display: grid; place-items: center;
}

/* --- La grille de fichiers --- */
.ds-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  padding: 12px 14px 16px;
}
.ds-tile {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 8px 10px;
  border: 1px solid var(--ds-brd);
  border-radius: var(--rad-sm, 10px);
  background: #fff;
  box-shadow: 0 1px 2px rgba(30, 20, 55, .05);
  transition: opacity .5s var(--ds-e), transform .55s var(--ds-eo),
              box-shadow .4s var(--ds-e), border-color .4s;
}
/* Estompées, pas supprimées : les tuiles gardent leur cellule, la grille ne
   se recalcule jamais — le filtrage est un jeu d'opacités et d'échelles. */
.ds-tile.ds-out { opacity: .1; transform: scale(.96); }
/* Les deux tuiles de l'élève : leur glissement (transform posé par le JS,
   distances mesurées une fois par cycle) est plus lent que le fondu. */
.ds-tile.ds-match { transition: opacity .5s var(--ds-e), transform .7s var(--ds-eo), box-shadow .4s var(--ds-e), border-color .4s; }
.ds-tile.is-lift {
  position: relative; z-index: 2;
  border-color: #e4dbfc;
  box-shadow: 0 0 0 3px rgba(123, 91, 240, .14), 0 14px 30px -14px rgba(76, 46, 160, .4);
}
.ds-name {
  font-size: .62rem; font-weight: 600; line-height: 1.3;
  color: var(--ds-mut); text-align: center; word-break: break-word;
  min-height: 2.6em; /* égalise les rangées : 1 ou 2 lignes, même hauteur */
}

/* --- Les pictos de fichiers, en pur CSS --- */
.ds-ico {
  position: relative;
  width: 32px; height: 40px; border-radius: 7px;
  display: grid; place-items: end center; padding-bottom: 4px;
  font-size: .48rem; font-weight: 800; color: #fff; letter-spacing: .04em;
}
/* Le coin corné qui dit « fichier » : une découpe couleur de tuile, puis un
   rabat translucide dans la découpe. */
.ds-ico::before {
  content: ""; position: absolute; top: 0; right: 0;
  border-top: 10px solid #fff; border-left: 10px solid transparent;
}
.ds-ico::after {
  content: ""; position: absolute; top: 0; right: 0;
  border-bottom: 10px solid rgba(255, 255, 255, .45); border-right: 10px solid transparent;
}
.ds-ico--pdf { background: #dd4a3d; }
.ds-ico--xls { background: #25a05c; }
.ds-ico--doc { background: #3b74e8; }
.ds-ico--img { background: var(--ds-v); }

/* --- État figé (prefers-reduced-motion) --- */
/* Le JS pose is-reduced puis l'état final : champ « Verd », deux fichiers en
   tête de grille, chip visible. `order` n'est utilisé qu'ici, à froid — ce
   n'est jamais une propriété animée. */
#docScene.is-reduced .ds-tile.ds-match { order: -1; }

@media (max-width: 600px) {
  .ds-grid { gap: 8px; padding: 10px 12px 14px; }
  .ds-bartitle { font-size: .7rem; }
}
/* ===================================================
   23. Les sections de gestes
   ===================================================
   Le gabarit de la section Appel, généralisé : en-tête + puces à coches dans
   une colonne, la scène animée dans l'autre, alignées au centre. Compact
   (64 px de respiration), côtés alternés par is-flipped, fonds par sec--soft.
   minmax(0, …) : les scènes à piste (translateX) ont une largeur min-content
   de deux volets — sans plancher, elles gonfleraient leur colonne. */

.feat.sec { padding: 64px 0; }
.feat-grid {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: 52px;
  align-items: center;
}
.feat-grid.is-flipped { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
.feat-grid.is-flipped > .feat-txt { order: 2; }
/* Même borne que le titre de l'appel : deux lignes, pas cinq. */
.feat-grid .title { font-size: clamp(1.7rem, 2.6vw, 2.2rem); }

.feat-points { list-style: none; margin: 24px 0 0; display: grid; gap: 13px; }
.feat-points li { display: flex; gap: 12px; font-size: .96rem; color: var(--g2); }
.feat-points svg { width: 19px; height: 19px; color: var(--v); flex-shrink: 0; margin-top: 2px; }
.feat-points b { color: var(--g); font-weight: 700; }

@media (max-width: 900px) {
  .feat-grid, .feat-grid.is-flipped { grid-template-columns: 1fr; gap: 34px; }
  .feat-grid.is-flipped > .feat-txt { order: 0; }
}

/* ===================================================
   24. Scène : l'accueil du héros
   ===================================================
   Remplace la capture d'écran du héros (le .hero-app-frame et son <picture>) ;
   animée par motion.js §11. Tout est préfixé hs- et rattaché à #heroScene :
   aucun sélecteur global. Complète design-tokens.css (--v, --brd, --rad-lg…)
   et les courbes du haut de ce fichier (--e-out, --e-spring, --e-io) ; les
   var() portent un repli pour que la scène survive hors de la page d'accueil
   (préview, tests).

   Tenue héritée de motion.js : seuls transform, opacity et couleurs bougent.
   La scène vit en deux temps — une entrée en cascade jouée UNE fois, puis une
   vie ambiante en boucle qui ne repasse JAMAIS par un état vide. Les
   compteurs de mails (16 non lus, 9 à traiter, boîte nominative) et l'état
   de l'appel sont des fondus croisés sur une même cellule de grille : la
   carte ne change donc jamais de hauteur. Sans JS ou en reduced-motion,
   is-play n'existe pas et le tableau complet est lisible d'office — c'est
   l'état par défaut du HTML. La structure suit le vrai accueil de l'app :
   barre d'onglets, bandeau du jour avec météo, Mails + Appel du jour en
   haut, En retard (liseré rouge) + Livrets + Semaine en bas, un lien de
   pied par carte. */

#heroScene {
  --hs-v: var(--v, #7B5BF0);
  --hs-v-soft: var(--v-soft, #f3efff);
  --hs-ink: var(--g, #1f2937);
  --hs-mut: var(--g2, #6b7280);
  --hs-mut2: var(--g3, #9ca3af);
  --hs-brd: var(--brd, #e5e7eb);
  --hs-ok: #059669;
  --hs-warn: #a9600b;
  --hs-alert: var(--alert, #DC2626);
  --hs-eo: var(--e-out, cubic-bezier(.16, 1, .3, 1));
  --hs-spring: var(--e-spring, cubic-bezier(.34, 1.42, .5, 1));
  --hs-io: var(--e-io, cubic-bezier(.45, 0, .55, 1));
  position: relative;
  width: 100%; max-width: 720px; margin: 0 auto;
  font-family: var(--fd, 'Inter Tight', -apple-system, BlinkMacSystemFont, system-ui, sans-serif);
  color: var(--hs-ink);
  /* Une fenêtre d'app s'aligne à gauche — même quand le héros mobile centre
     son texte (.hero-inner passe en text-align:center sous 980px). */
  text-align: left;
}

/* --- Le cadre : la fenêtre de l'application -------------------------------- */
.hs-frame {
  display: flex; flex-direction: column; gap: 10px;
  padding: 10px 14px 14px;
  background: linear-gradient(180deg, #fbfaff 0%, #f5f2fd 100%);
  border: 1px solid var(--hs-brd);
  border-radius: var(--rad-lg, 20px);
  box-shadow: var(--sh-window, 0 30px 70px -24px rgba(76, 46, 160, .30), 0 6px 20px -8px rgba(76, 46, 160, .16));
  transition: opacity .5s var(--hs-eo), transform .6s var(--hs-eo);
}
/* Pendant l'entrée, le cadre attend son tour ; sans JS ou en reduced-motion,
   is-play n'existe pas et le tableau est visible d'office. */
#heroScene.is-play .hs-frame { opacity: 0; transform: translateY(14px); }
#heroScene.is-in .hs-frame { opacity: 1; transform: none; }

/* La barre de fenêtre : trois pastilles et le titre, purement décoratifs. */
.hs-chrome { position: relative; display: flex; align-items: center; gap: 6px; padding: 2px 2px 0; }
.hs-chrome i { width: 9px; height: 9px; border-radius: 50%; background: #e6e0f7; }
.hs-chrome span {
  position: absolute; left: 0; right: 0; text-align: center;
  font-size: .66rem; font-weight: 650; letter-spacing: .02em; color: var(--hs-mut2);
}

/* --- La barre de l'application ---------------------------------------------
   Logo, onglet actif, onglets, présence : décorative et muette, elle arrive
   avec le cadre (is-in) — c'est elle qui fait « vrai logiciel ». */
.hs-nav {
  display: flex; align-items: center; gap: 12px;
  padding: 6px 12px;
  background: #fff; border: 1px solid var(--hs-brd); border-radius: 10px;
}
.hs-nav-brand {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: .7rem; font-weight: 800; letter-spacing: -.01em;
}
.hs-nav-brand svg { width: 15px; height: 15px; }
.hs-nav-home {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 21px; height: 21px; border-radius: 6px;
  background: var(--hs-v-soft); color: var(--hs-v);
}
.hs-nav-home svg { width: 12px; height: 12px; }
.hs-nav-tabs { display: flex; gap: 13px; margin: 0 auto; min-width: 0; overflow: hidden; }
.hs-nav-tabs i { font-style: normal; font-size: .64rem; font-weight: 600; color: var(--hs-mut); white-space: nowrap; }
.hs-nav-right {
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  font-size: .64rem; font-weight: 600; color: var(--hs-mut); white-space: nowrap;
}
.hs-nav-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--hs-ok); }

/* --- Le bandeau du jour ----------------------------------------------------
   Tuile maison, date sur deux étages, échéances, météo : il arrive avec le
   cadre (is-in), c'est lui qui ouvre la cascade. */
.hs-band {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 14px;
  background: #fff; border: 1px solid var(--hs-brd); border-radius: 12px;
}
.hs-home {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 10px;
  background: var(--hs-v-soft); color: var(--hs-v);
}
.hs-home svg { width: 17px; height: 17px; }
.hs-band-txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.hs-band-txt b { font-size: .88rem; font-weight: 750; letter-spacing: -.01em; white-space: nowrap; }
.hs-band-txt span { font-size: .68rem; color: var(--hs-mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-badge {
  margin-left: auto; flex: none;
  padding: 4px 10px; border-radius: 100px;
  background: #fdeeed; border: 1px solid rgba(220, 38, 38, .22);
  color: var(--hs-alert); font-size: .68rem; font-weight: 700; white-space: nowrap;
}
.hs-meteo { flex: none; display: inline-flex; align-items: center; gap: 7px; padding-left: 12px; border-left: 1px solid #f1eefb; }
.hs-meteo svg { width: 20px; height: 20px; }
.hs-meteo > span { display: flex; flex-direction: column; }
.hs-meteo b { font-size: .8rem; font-weight: 800; line-height: 1.1; }
.hs-meteo em { font-style: normal; font-size: .58rem; color: var(--hs-mut2); white-space: nowrap; }

/* --- Les cartes ------------------------------------------------------------
   Deux rangs, comme le vrai accueil : Mails + Appel du jour en haut,
   En retard + Livrets + Semaine en bas. */
.hs-tiles { display: grid; grid-template-columns: 1.25fr 1fr; gap: 12px; }
.hs-tile {
  min-width: 0;
  display: flex; flex-direction: column; gap: 7px;
  padding: 11px 14px 11px;
  background: #fff; border: 1px solid var(--hs-brd); border-radius: 12px;
  box-shadow: var(--sh, 0 4px 14px rgba(123, 91, 240, .10));
  transition: opacity .45s var(--hs-eo), transform .55s var(--hs-eo);
}
.hs-k {
  font-size: .62rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--hs-mut2); white-space: nowrap;
}
.hs-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; min-width: 0; }
.hs-count { font-size: .62rem; font-weight: 650; color: var(--hs-mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Le lien de pied, comme dans l'app : l'intitulé à gauche, le chevron au
   bord droit. margin-top:auto le colle en bas — les cartes d'un même rang
   finissent à la même ligne. */
.hs-link {
  margin-top: auto; padding-top: 7px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  border-top: 1px solid #f1eefb;
  font-size: .64rem; font-weight: 650; color: var(--hs-v); white-space: nowrap;
}
.hs-link svg { width: 11px; height: 11px; flex: none; color: var(--hs-mut2); }
.hs-tile .hs-link, .hs-late .hs-link, .hs-week .hs-link { margin-top: auto; }

/* Carte Mails. Chaque paire de chiffres (.hs-num, .hs-cd) partage une même
   cellule de grille : les passages 15 → 16, 8 → 9 et 12 → 13 sont des fondus
   croisés, la carte ne bouge jamais. */
.hs-mail { display: flex; align-items: baseline; gap: 8px; }
.hs-num { display: grid; font-size: 2.1rem; font-weight: 800; line-height: 1; letter-spacing: -.02em; color: var(--hs-v); }
.hs-num i { grid-area: 1 / 1; font-style: normal; transition: opacity .3s var(--hs-eo), transform .5s var(--hs-spring); }
.hs-n0 { opacity: 0; transform: scale(.72); }
/* La pose du 15 (début de boucle) est un fondu discret ; c'est le RETOUR du
   16 (le mail qui arrive) qui rebondit, via la transition à ressort de base. */
#heroScene.is-mail .hs-n1 { opacity: 0; transform: scale(.72); transition: opacity .3s var(--hs-eo), transform .3s var(--hs-eo); }
#heroScene.is-mail .hs-n0 { opacity: 1; transform: none; transition: opacity .3s var(--hs-eo), transform .3s var(--hs-eo); }
.hs-mail-lbl { font-size: .74rem; font-weight: 650; color: var(--hs-mut); }
.hs-cd { display: inline-grid; }
.hs-cd i { grid-area: 1 / 1; font-style: normal; transition: opacity .3s var(--hs-eo); }
.hs-cd i + i { opacity: 0; }
#heroScene.is-mail .hs-cd i { opacity: 0; }
#heroScene.is-mail .hs-cd i + i { opacity: 1; }
.hs-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.hs-chip {
  display: inline-flex; align-items: center;
  padding: 3px 9px; border-radius: 100px;
  font-size: .64rem; font-weight: 700; white-space: nowrap;
}
.hs-chip .hs-cd { margin-right: 3px; }
.hs-chip--todo { background: #fdeeed; border: 1px solid rgba(220, 38, 38, .18); color: var(--hs-alert); }
.hs-chip--doing { background: #eef1fe; border: 1px solid rgba(61, 91, 217, .2); color: #3d5bd9; }
.hs-boxes { display: flex; flex-direction: column; gap: 5px; }
.hs-box {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 5px 9px; border-radius: 8px;
  background: #f7f5fe;
  font-size: .68rem;
}
.hs-box > span { min-width: 0; color: var(--hs-mut); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-box b { font-size: .7rem; font-weight: 750; color: var(--hs-ink); }

/* Carte Appel du jour. Les deux états partagent la même cellule de grille :
   « Aucune absence signalée » et « Léon Roussel » se fondent l'un dans
   l'autre, la cellule garde la hauteur du plus grand des deux. */
.hs-abs { display: grid; align-items: center; }
.hs-abs > span {
  grid-area: 1 / 1; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  transition: opacity .35s var(--hs-eo), transform .45s var(--hs-eo);
}
.hs-abs > span > i { width: 8px; height: 8px; flex: none; border-radius: 50%; }
.hs-abs-a { font-size: .8rem; font-weight: 750; letter-spacing: -.01em; color: var(--hs-ink); }
.hs-abs-a i { background: var(--hs-ok); }
.hs-abs-b { opacity: 0; transform: translateY(6px); }
.hs-abs-b i { background: var(--hs-warn); }
.hs-abs-b > span { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.hs-abs-b b { font-size: .78rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-abs-b em { font-style: normal; font-size: .68rem; color: var(--hs-mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#heroScene.is-abs .hs-abs-a { opacity: 0; transform: translateY(-6px); }
#heroScene.is-abs .hs-abs-b { opacity: 1; transform: none; }
.hs-appel { display: flex; flex-direction: column; gap: 4px; }
.hs-ap-ok { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: .68rem; font-weight: 650; color: var(--hs-ok); white-space: nowrap; }
.hs-ap-ok svg { width: 11px; height: 11px; flex: none; }
.hs-ap-nx { padding-left: 17px; font-size: .66rem; color: var(--hs-mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Carte Livrets de suivi : avatar aux initiales, le nom et l'étiquette sur
   la première ligne, la description en dessous sur toute la largeur — c'est
   elle qui manque de place, pas le nom. */
.hs-el {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: 7px; row-gap: 1px; align-items: center;
  padding: 6px 0 5px;
  border-top: 1px solid #f1eefb;
  font-size: .7rem;
}
.hs-ava {
  grid-row: 1 / span 2;
  width: 20px; height: 20px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--hs-v-soft); color: var(--hs-v);
  font-size: .52rem; font-weight: 800; letter-spacing: .01em;
}
.hs-el b { min-width: 0; font-weight: 700; font-size: .7rem; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-el em { grid-column: 2 / -1; min-width: 0; font-style: normal; font-size: .62rem; color: var(--hs-mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-tag {
  flex: none; font-style: normal;
  padding: 2px 7px; border-radius: 100px;
  font-size: .62rem; font-weight: 700; white-space: nowrap;
}
.hs-tag--warn { background: #fbf2e3; border: 1px solid rgba(169, 96, 11, .22); color: var(--hs-warn); }
.hs-tag--due { background: #fdeeed; border: 1px solid rgba(220, 38, 38, .22); color: var(--hs-alert); }

/* La vie ambiante : la relance RASED bat deux fois, doucement, puis se
   repose. Transform et couleur seulement — la ligne ne bouge pas. */
@keyframes hsPulse {
  0%, 100% { transform: none; }
  35% { transform: scale(1.1); background: #f5e0bf; }
}
#heroScene.is-pulse .hs-tag--pulse { animation: hsPulse 1.15s var(--hs-io) 2; }

/* --- Le bas : les retards, les livrets et la semaine ----------------------- */
.hs-low { display: grid; grid-template-columns: 1.35fr 1.12fr .9fr; gap: 12px; }
.hs-late, .hs-week {
  min-width: 0;
  display: flex; flex-direction: column;
  padding: 11px 14px 11px;
  background: #fff; border: 1px solid var(--hs-brd); border-radius: 12px;
  box-shadow: var(--sh, 0 4px 14px rgba(123, 91, 240, .10));
  transition: opacity .45s var(--hs-eo), transform .55s var(--hs-eo);
}
/* Le liseré rouge du vrai accueil : c'est lui qui dit « ça presse ». */
.hs-late { border-left: 3px solid rgba(220, 38, 38, .45); }
.hs-late .hs-head { margin-bottom: 2px; }
.hs-li {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 0 6px;
  font-size: .7rem;
  transition: opacity .4s var(--hs-eo), transform .45s var(--hs-eo);
}
.hs-li + .hs-li { border-top: 1px solid #f1eefb; }
/* Le retard s'affiche à gauche de l'intitulé, comme dans l'app : un chiffre
   rouge nu, pas une pastille. */
.hs-li b { flex: none; width: 30px; font-size: .66rem; font-weight: 750; color: var(--hs-alert); white-space: nowrap; }
.hs-li .hs-li-warn { color: var(--hs-warn); }
.hs-li > span { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hs-more { display: block; padding: 6px 0 7px; font-size: .62rem; color: var(--hs-mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.hs-week { gap: 8px; }
.hs-ev {
  min-width: 0; margin: auto 0;
  display: flex; flex-direction: column; gap: 2px;
  padding-left: 10px; border-left: 3px solid var(--hs-v);
}
.hs-ev b { font-size: .72rem; font-weight: 700; letter-spacing: -.01em; }
.hs-ev span { font-size: .68rem; color: var(--hs-mut); }

/* --- L'entrée en cascade ---------------------------------------------------
   is-play cache, chaque état d'entrée rallume : à spécificité égale, l'ordre
   des règles fait gagner la révélation. Une classe d'entrée re-posée à un
   tour suivant de la boucle ne produit rien — l'entrée ne se rejoue pas. */
#heroScene.is-play .hs-tile,
#heroScene.is-play .hs-late,
#heroScene.is-play .hs-week { opacity: 0; transform: translateY(10px); }
#heroScene.is-play .hs-li { opacity: 0; transform: translateX(-8px); }

#heroScene.is-t1 .hs-t1,
#heroScene.is-t2 .hs-t2,
#heroScene.is-t3 .hs-t3,
#heroScene.is-l1 .hs-late,
#heroScene.is-wk .hs-week { opacity: 1; transform: none; }
#heroScene.is-l1 .hs-l1,
#heroScene.is-l2 .hs-l2,
#heroScene.is-l3 .hs-l3 { opacity: 1; transform: none; }

/* Reduced motion : design-tokens.css écrase déjà toutes les durées, mais on
   coupe explicitement — le tableau complet (l'état par défaut du HTML) doit
   se poser d'un bloc, sans micro-fondu ni vie ambiante. */
@media (prefers-reduced-motion: reduce) {
  .hs-frame, .hs-tile, .hs-late, .hs-week, .hs-li,
  .hs-num i, .hs-cd i, .hs-abs > span { transition: none; }
  #heroScene.is-pulse .hs-tag--pulse { animation: none; }
}

@media (max-width: 640px) {
  .hs-tiles { grid-template-columns: 1fr; }
  .hs-low { grid-template-columns: 1fr; }
  .hs-nav-tabs { display: none; }
  .hs-meteo { display: none; }
  .hs-home { display: none; }
  .hs-band-txt span { font-size: .66rem; }
  .hs-chrome span { display: none; }
}

/* ===================================================
   26. Le catalogue : tout le reste de l'école
   ===================================================
   Une seule section pour tout ce qui n'a pas de scène. L'argument est la
   quantité, et elle se lit à la densité : petits corps, colonnes serrées,
   groupes par domaine. Colonnes CSS plutôt qu'une grille — les groupes ont
   des hauteurs très inégales (16 lignes pour les mails, 3 pour le journal),
   une grille laisserait des trous ; en colonnes, chaque groupe reste entier
   (break-inside) et le mur se remplit sans creux. Les groupes sont rangés
   dans l'ordre qui équilibre les trois colonnes (le navigateur remplit dans
   l'ordre du flux, il ne réordonne pas) : Élèves, Absences, Équipe | Mails,
   Documents | Pilotage, Journal, Poste. Chaque puce tient sur une ligne à
   ces largeurs ; rien n'est tronqué, une puce longue passe à la ligne. */

.catalog-head { max-width: 820px; }
.catalog-head .title em { font-style: normal; color: var(--v); }

.catalog-groups {
  margin-top: 52px;
  columns: 3;
  column-gap: 44px;
}
.catalog-group {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  padding-bottom: 32px;
}
.catalog-group h3 {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 12px;
  font-size: .76rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--vd);
}
/* Le filet après l'intitulé ferme la colonne : chaque groupe se lit comme
   une rubrique de sommaire, pas comme un paragraphe de plus. */
.catalog-group h3::after { content: ""; flex: 1; height: 1px; background: var(--brd); }
/* Le rythme entre les puces vient du padding et non d'un gap : c'est ce
   padding qui donne sa hauteur au surlignage du projecteur, ci-dessous. */
.catalog-group ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.catalog-group li {
  position: relative;
  padding: 4px 0 4px 24px;
  /* Pilule : l'arrondi épouse la coche, qui se pose à gauche du surlignage
     sans qu'un coin carré la fasse paraître coupée. */
  border-radius: 999px;
  font-size: .88rem; line-height: 1.45; color: var(--g);
}
.catalog-group li::before {
  content: "";
  position: absolute; left: 0; top: calc(.22em + 4px);
  width: 15px; height: 15px; border-radius: 50%;
  /* La coche grossit vers la DROITE, dans les 24 px libres avant le texte :
     depuis son centre, elle sortait de 1,4 px à gauche de la colonne, qui
     coupe tout ce qui dépasse — la pastille y perdait son bord. */
  transform-origin: left center;
  background: var(--v-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%237B5BF0'%3E%3Cpath fill-rule='evenodd' d='M16.7 5.3a1 1 0 010 1.4l-8 8a1 1 0 01-1.4 0l-4-4a1 1 0 011.4-1.4L8 12.6l7.3-7.3a1 1 0 011.4 0z'/%3E%3C/svg%3E") center / 10px no-repeat;
}
/* --- Le projecteur --------------------------------------------------------
   motion.js §18 allume une puce au hasard toutes les 1,5 s (is-spot) : elle
   se surligne, passe au violet, sa coche se remplit et grossit ; la
   précédente relâche au même instant. Rien n'agrandit le texte : les puces
   coulent dans des colonnes (columns: 3), et une colonne COUPE ce qui
   dépasse de sa largeur — une puce longue mise à l'échelle perdait sa fin
   (mesuré : 23 px hors colonne à scale 1.07). Le surlignage, lui, s'arrête
   au bord de la colonne, à toutes les largeurs. La transition est posée sur
   ul.is-live (ajoutée par le script une fois la cascade d'entrée jouée) :
   avant, c'est celle de [data-stagger] > *, avec son retard --d, qui doit
   rester maîtresse. */
.catalog-group ul.is-live > li {
  transition: background-color .45s, color .35s;
}
.catalog-group ul.is-live > li::before {
  transition: background-color .35s, transform .55s var(--e-spring);
}
.catalog-group li.is-spot { background: var(--v-soft); color: var(--vd); }
.catalog-group li.is-spot::before {
  transform: scale(1.18);
  background: var(--v) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23ffffff'%3E%3Cpath fill-rule='evenodd' d='M16.7 5.3a1 1 0 010 1.4l-8 8a1 1 0 01-1.4 0l-4-4a1 1 0 011.4-1.4L8 12.6l7.3-7.3a1 1 0 011.4 0z'/%3E%3C/svg%3E") center / 10px no-repeat;
}
@media (prefers-reduced-motion: reduce) {
  .catalog-group ul.is-live > li, .catalog-group ul.is-live > li::before { transition: none; }
}

.catalog-foot {
  margin: 12px 0 0;
  padding-top: 26px;
  border-top: 1px solid var(--brd);
  font-size: .96rem; color: var(--g2);
}

@media (max-width: 1000px) {
  .catalog-groups { columns: 2; column-gap: 36px; }
}
@media (max-width: 600px) {
  .catalog-groups { columns: 1; margin-top: 36px; }
  .catalog-group { padding-bottom: 26px; }
  .catalog-group li { font-size: .92rem; }
}
