/* ------------------------------------------------------------------ polices
   Servies par le site (src/assets/fonts/), pas par Google : plus de DNS ni de
   TLS vers un tiers avant de pouvoir écrire une lettre. Deux fichiers
   variables par langue de caractères, latin et latin étendu.

   ⚠️ Et surtout une POLICE DE SECOURS AUX MÊMES MESURES (07/09). Le temps que
   la vraie arrive, le navigateur composait en Arial — 12,6 % plus large que
   Jakarta en gras (mesuré au canvas) — d'où des titres qui paraissaient
   énormes et se coupaient autrement, puis sautaient en place : « au début les
   écritures sont très très grosses, ce n'est pas du tout harmonieux »
   (Anthony). `size-adjust` ramène Arial à la largeur de Jakarta, poids par
   poids ; il ne se passe plus rien à l'arrivée de la vraie police. */
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('/assets/fonts/jetbrains-latin-ext.woff2') format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('/assets/fonts/jetbrains-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Plus Jakarta Sans'; font-style: italic; font-weight: 400 800; font-display: swap;
  src: url('/assets/fonts/jakarta-italique-latin-ext.woff2') format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Plus Jakarta Sans'; font-style: italic; font-weight: 400 800; font-display: swap;
  src: url('/assets/fonts/jakarta-italique-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Plus Jakarta Sans'; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url('/assets/fonts/jakarta-latin-ext.woff2') format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Plus Jakarta Sans'; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url('/assets/fonts/jakarta-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Jakarta secours"; font-weight: 400 600; size-adjust: 105.4%;
  src: local("Arial"), local("Helvetica Neue"), local("Liberation Sans"), local("Arimo"); }
@font-face { font-family: "Jakarta secours"; font-weight: 700 800; size-adjust: 102.1%;
  src: local("Arial Bold"), local("Arial"), local("Helvetica Neue"), local("Liberation Sans"); }
@font-face { font-family: "JetBrains secours"; font-weight: 400 600; size-adjust: 109.1%;
  src: local("Consolas"), local("SFMono-Regular"), local("Menlo"), local("DejaVu Sans Mono"); }

/* ============================================================================
   AG Algo Lab — DA « Isométrique » (choisie par Anthony le 4 septembre 2026)
   Fond menthe, encre vert profond, un accent vert, chiffres en bandes vert
   nuit. Plus Jakarta Sans partout, JetBrains Mono pour les étiquettes.
   Le héros pose les écrans en dalles flottantes sur un sol quadrillé en 3D ;
   chaque cadre change de page.

   Règles de production qui ne se discutent pas :
   - l'état statique du HTML est l'état final ; aucune visibilité ne dépend
     d'une transition ni d'une animation ;
   - le chemin riche (survol, révélations, lévitation, fondus) n'existe qu'à
     partir de 1024 px avec une souris ; en dessous, les écrans changent de
     page par simple remplacement, sans transition ;
   - grilles en minmax(0, …), padding-top / padding-bottom séparés sur tout
     ce qui porte .wrap, menu téléphone en display:none ↔ flex.
   ========================================================================== */

:root {
  --papier: #EAF7F1;
  --papier-2: #DDF0E6;
  --carte: #FFFFFF;
  --encre: #0B2E1F;
  --encre-2: #3F5F50;
  --filet: #BFE0CF;
  --filet-2: #D8EBE0;
  --accent: #047857;      /* boutons verts (blanc dessus : 5,4:1), mot fort des titres */
  --accent-txt: #046C4E;  /* petit texte vert sur menthe : 5,9:1 */
  --accent-vif: #10B981;  /* décor seulement */
  --accent-pale: #D1FAE5;
  --vert: #047857;
  --vert-pale: #D1FAE5;
  --nuit: #0B2E1F;
  --nuit-2: #124A33;
  --nuit-txt: #ECFDF5;
  --nuit-txt-2: #8FC7A9;
  --ambre: #FCD34D;
  --menthe: #6EE7B7;
  --grille: rgba(4, 120, 87, .18);
  --serif: "Plus Jakarta Sans", "Jakarta secours", "Helvetica Neue", Arial, sans-serif;
  --sans: "Plus Jakarta Sans", "Jakarta secours", "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", "JetBrains secours", "SFMono-Regular", Consolas, monospace;
  --wrap: 1180px;
  --rayon: 18px;
  --ombre: 0 30px 60px -36px rgba(11, 46, 31, .5);
}

*, *::before, *::after { box-sizing: border-box; }
/* 17 px de base : « des textes très lisibles, assez gros » (Anthony, 06/09) */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; font-size: 106.25%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0;
  font: 400 17px/1.6 var(--sans);
  color: var(--encre);
  background: var(--papier);
  -webkit-font-smoothing: antialiased;
}
img, svg { max-width: 100%; height: auto; display: block; }
a { color: var(--accent-txt); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--encre); }
p { margin: 0 0 1em; }
ul, ol { margin: 0 0 1em; padding-left: 1.2em; }
strong, b { font-weight: 700; }
:focus-visible { outline: 3px solid var(--accent-vif); outline-offset: 3px; }
::selection { background: var(--accent-pale); }

.saut { position: absolute; left: -999px; top: 8px; background: var(--encre); color: #fff; padding: 10px 14px; border-radius: 8px; z-index: 100; }
.saut:focus { left: 8px; }

/* ------------------------------------------------------------ typographie */
h1, h2, h3, h4 { font-family: var(--serif); font-weight: 800; letter-spacing: -.035em; line-height: 1.1; margin: 0 0 .5em; }
.h1 { font-size: clamp(2.3rem, 4.6vw, 3.9rem); max-width: 20ch; }
.h2 { font-size: clamp(1.8rem, 3.2vw, 2.7rem); max-width: 28ch; }
.h3 { font-size: clamp(1.35rem, 2vw, 1.7rem); letter-spacing: -.025em; }
.h4 { font-size: 1.15rem; font-weight: 700; letter-spacing: -.015em; margin-bottom: .35em; }
h1 em, h2 em, h3 em { font-style: normal; color: var(--accent); }
.chapo { font-size: clamp(1.1rem, 1.5vw, 1.28rem); line-height: 1.55; color: var(--encre-2); max-width: 62ch; }
.chapo b, p b { color: var(--encre); font-weight: 700; }
.max-70 { max-width: 70ch; }
.mono { font-family: var(--mono); }
.vert { color: var(--vert); }
.sourdine { color: var(--encre-2); }
.petit { font-size: .92rem; }

.etq {
  display: inline-flex; align-items: center; gap: .65em;
  font: 600 .72rem/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-txt); margin: 0 0 14px;
}
.etq::before { content: ""; flex: none; width: 9px; height: 9px; background: var(--accent-vif); transform: rotate(45deg); }

.note {
  font: 500 .8rem/1.55 var(--mono); color: var(--encre-2);
  padding-left: 14px; border-left: 2px solid var(--accent-vif); margin: 0 0 1em;
}

/* --------------------------------------------------------------- layout */
.wrap { max-width: var(--wrap); margin-left: auto; margin-right: auto; padding-left: 22px; padding-right: 22px; }
.sec { padding-top: clamp(56px, 8vw, 104px); padding-bottom: clamp(56px, 8vw, 104px); }
.sec-filet { border-top: 1px solid var(--filet); }
.sec-papier { background: var(--papier-2); }
.sec-tete { max-width: 760px; margin-bottom: clamp(28px, 4vw, 48px); }
.sec-tete .h2 { margin-bottom: .35em; }
.sec-tete .chapo { margin-bottom: 0; }
.g2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 18px; }
.g3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); gap: 18px; }
.g4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: 18px; }
.actions { display: flex; flex-wrap: wrap; gap: 12px 14px; align-items: center; margin-top: 22px; }

/* ------------------------------------------------------------- boutons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  min-height: 48px; padding: .7em 1.35em; border-radius: 12px;
  font: 700 1rem/1.2 var(--sans); text-decoration: none; cursor: pointer;
  border: 1.5px solid transparent; background: transparent; color: var(--encre);
}
.btn-p { background: var(--nuit); color: #fff; }
.btn-p:hover { background: var(--accent); color: #fff; }
.btn-s { background: var(--carte); border-color: var(--filet); color: var(--encre); }
.btn-s:hover { border-color: var(--encre); }
.btn-nav { min-height: 44px; padding: .5em 1.1em; font-size: .95rem; }
.lien { font-weight: 600; }
.actions .lien { display: inline-flex; align-items: center; min-height: 44px; }

/* -------------------------------------------------------------- en-tête */
.entete { position: sticky; top: 0; z-index: 50; background: var(--papier); background: color-mix(in srgb, var(--papier) 94%, transparent); border-bottom: 1px solid var(--filet); }
@supports (backdrop-filter: blur(8px)) { .entete { backdrop-filter: blur(8px); } }
.entete-in { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 66px; position: relative; }
.marque { display: inline-flex; align-items: center; gap: 10px; font: 800 1.15rem/1 var(--sans); letter-spacing: -.03em; color: var(--encre); text-decoration: none; min-height: 44px; white-space: nowrap; }
.marque img { height: 32px; width: auto; flex: none; }
.marque-carre { width: 12px; height: 12px; background: var(--accent); transform: rotate(45deg); flex: none; }
/* la nav se range à droite, collée aux outils : au milieu elle flottait (Anthony, 06/09) */
.nav { display: flex; align-items: center; gap: 4px 22px; margin-left: auto; margin-right: 8px; }
.nav a { color: var(--encre); text-decoration: none; font-size: .96rem; font-weight: 600; min-height: 44px; display: inline-flex; align-items: center; }
.nav a:hover { color: var(--accent-txt); }
.nav .btn-nav { color: #fff; }
.nav .btn-nav:hover { color: #fff; }
.entete-outils { display: flex; align-items: center; gap: 10px; }
.langue {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 12px;
  border: 1px solid var(--filet); border-radius: 10px; background: var(--carte);
  font: 700 .76rem/1 var(--mono); letter-spacing: .08em; color: var(--encre); text-decoration: none;
}
.langue:hover { border-color: var(--encre); }
.langue-actuelle { color: var(--encre-2); }
.langue-sep { color: var(--filet); }
.burger { display: none; width: 46px; height: 44px; padding: 8px; border: 1px solid var(--filet); border-radius: 10px; background: var(--carte); cursor: pointer; flex-direction: column; justify-content: center; gap: 5px; }
.burger span { display: block; height: 2px; background: var(--encre); border-radius: 2px; }
/* Sous 380 px (320 et 360 px : iPhone SE, Galaxy A, Redmi), le nom se cassait
   en deux ou trois lignes et sortait de l'en-tête. Il ne se casse plus, et il
   rétrécit juste assez pour tenir à côté du sélecteur de langue (07/09). */
@media (max-width: 380px) {
  /* Mesuré : entre la marge et les outils il reste 138 px, et la marque en
     demandait 158 (logo 50 + espace 7 + nom 101). On rend 10 px sur le logo,
     8 px sur les entrelettres du sélecteur de langue et 8 px sur l'espace qui
     les sépare — la marque tient alors dans 148 px, et rien n'est coupé. */
  .marque { font-size: .95rem; gap: 6px; }
  .marque img { height: 24px; }
  .entete-outils { gap: 8px; }
  .langue { padding: 0 8px; gap: 4px; letter-spacing: .04em; }
  /* ⚠️ pas de `.burger { width: 42px }` ici : c'était mon premier réflexe pour
     gagner deux pixels, et ça faisait tomber le bouton sous les 44 px que le
     site affiche comme un gage. La place se prend sur le logo et sur les
     entrelettres, jamais sur une cible tactile (07/09). */
}


@media (max-width: 960px) {
  .nav {
    display: none; position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--papier); border-bottom: 1px solid var(--filet);
    padding: 6px 22px 18px; box-shadow: var(--ombre);
  }
  .nav.ouvert { display: flex; }
  .nav a { min-height: 50px; border-bottom: 1px solid var(--filet-2); font-size: 1.05rem; }
  .nav .btn-nav { margin-top: 12px; border-bottom: 0; }
  .burger { display: flex; }
}

/* ----------------------------------------------------------------- héros */
.hero { position: relative; padding-top: clamp(40px, 6vw, 84px); padding-bottom: clamp(40px, 6vw, 72px); overflow: hidden; }
.hero-txt { position: relative; z-index: 2; }
.hero .h1 { margin-bottom: .4em; }
.hero .chapo { margin-bottom: .4em; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; align-items: center; }
@media (min-width: 960px) { .hero-grid { grid-template-columns: minmax(0, 6fr) minmax(0, 6fr); gap: 30px; } }
.hero-grid .h1 { font-size: clamp(2.1rem, 3.5vw, 2.95rem); max-width: none; }

/* --------------------------------------------------- la scène isométrique
   Une scène de 760 × 760 dessinée à l'échelle 1, puis réduite par --k selon
   la largeur. Le sol est tourné en 3D ; les écrans sont ses ENFANTS : ils
   héritent de son orientation, donc ils sont posés sur la grille, alignés
   sur ses lignes — les uns dans un sens, les autres à angle droit (.travers).
   Chacun lévite à sa hauteur (--z) et à son rythme (--d, --r). Positions au
   centre (left/top + translate(-50%,-50%)) : la hauteur réelle du cadre ne
   déplace rien. Rien ne se superpose, vérifié par mesure des quadrilatères. */
.iso { --k: .84; position: relative; z-index: 0; width: 100%; height: calc(760px * var(--k)); margin: 0 auto; }
.iso-scene { position: absolute; left: 50%; top: 0; width: 760px; height: 760px; margin-left: -380px; transform: scale(var(--k)); transform-origin: 50% 0; perspective: 2400px; }
.iso-sol {
  position: absolute; left: 50%; top: 50%; width: 1100px; height: 1100px;
  transform: translate(-50%, -50%) rotateX(58deg) rotateZ(-45deg); transform-style: preserve-3d;
}
/* la grille est peinte dans un enfant : un masque sur le sol lui-même
   aplatirait la scène (mask force transform-style: flat) et les écrans
   retomberaient à plat sur le sol au lieu de léviter. */
.iso-grille {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(4, 120, 87, .34) 1px, transparent 1px),
    linear-gradient(90deg, rgba(4, 120, 87, .34) 1px, transparent 1px),
    linear-gradient(rgba(4, 120, 87, .17) 1px, transparent 1px),
    linear-gradient(90deg, rgba(4, 120, 87, .17) 1px, transparent 1px);
  background-size: 300px 300px, 300px 300px, 60px 60px, 60px 60px;
  -webkit-mask-image: radial-gradient(closest-side, #000 46%, rgba(0, 0, 0, .45) 76%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 46%, rgba(0, 0, 0, .45) 76%, transparent 100%);
}
.iso-dalle { position: absolute; width: var(--w, 232px); transform: translate(-50%, -50%); transform-style: preserve-3d; }
.iso-dalle.travers { transform: translate(-50%, -50%) rotateZ(90deg); }
.iso-dalle .flotte { position: relative; transform: translateZ(var(--z, 16px)); transform-style: preserve-3d; will-change: transform; }
/* l'ombre reste SUR la grille : elle est portée par la dalle (z = 0) pendant
   que l'écran, lui, s'élève — c'est ce décalage qui fait la lévitation. */
.iso-dalle::before {
  content: ""; position: absolute; left: 6%; right: 6%; top: 10%; bottom: 10%;
  background: rgba(11, 46, 31, .34); border-radius: 42%; filter: blur(15px);
}
.iso-dalle .ordi { box-shadow: 0 2px 7px rgba(11, 46, 31, .16); }
/* à deux colonnes, la scène est décalée de 16 px vers la droite : son coin
   gauche frôlait sinon la colonne de texte. */
@media (min-width: 960px) { .iso-scene { margin-left: -364px; } }
/* --k suit la largeur disponible : à deux colonnes, les écrans doivent
   rester entre la colonne de texte (16 px de marge) et le bord de la
   fenêtre — mesuré à 1440, 1280, 1200, 1024, 960, 640, 390 et 320 px. */
@media (max-width: 1279px) { .iso { --k: .78; } }
@media (max-width: 1199px) { .iso { --k: .71; } }
@media (max-width: 1039px) { .iso { --k: .65; } }
@media (max-width: 959px) { .iso { --k: .8; } }
@media (max-width: 640px) { .iso { --k: .62; } }
@media (max-width: 440px) { .iso { --k: .46; } }

/* ------------------------------------------------------------ chiffres */
.stats {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: 1fr 1fr;   /* téléphone : deux carreaux en haut */
  grid-auto-rows: auto;
  background: var(--nuit); border: 1px solid var(--nuit); border-radius: var(--rayon); overflow: hidden;
  margin-top: clamp(28px, 4vw, 44px); box-shadow: var(--ombre);
}
/* Les trois cases partagent les mêmes rangées (grille imbriquée) : le grand
   mot, le libellé et la ligne mono s'alignent d'une case à l'autre, et chaque
   rangée ne fait que la hauteur qu'il lui faut. J'avais d'abord réservé deux
   lignes au libellé : ça alignait, mais ça creusait un trou sous les libellés
   d'une seule ligne (07/09). */
.stat { padding: 18px 18px 16px; min-width: 0; border-right: 1px solid var(--nuit-2); border-bottom: 1px solid var(--nuit-2);
  display: grid; grid-template-rows: subgrid; grid-row: span 3; align-content: start; row-gap: 0; }
.stat b { display: block; font: 800 clamp(1.9rem, 3vw, 2.6rem)/1.15 var(--serif); color: var(--menthe); letter-spacing: -.035em; }
.stat b .vert, .stat b span { color: var(--menthe); }
/* > span : le libellé du chiffre. Sans le >, la règle attrapait aussi le
   <span data-compte> DANS le <b>, et « 71 » sortait en 15 px. */
.stat > span { display: block; font-size: .95rem; line-height: 1.35; color: var(--nuit-txt); }
.stat small { display: block; margin-top: 8px; font: 500 .66rem/1.45 var(--mono); color: var(--nuit-txt-2); letter-spacing: .01em; }
/* La case large (07/09) : l'hébergement européen vaut pour tout ce qui est
   construit, il ne tient donc pas dans une case de chiffre. Elle occupe deux
   carreaux, et garde EXACTEMENT les mêmes tailles de texte que les autres —
   c'est ce qui fait un bandeau rangé plutôt que brouillon. */
.stat-large { grid-column: 1 / -1; }          /* sur téléphone : un seul cadre, toute la largeur */
/* Pas de `max-width` ici (07/09) : je l'avais posé à 52ch pour éviter une
   ligne trop longue, mais la case mesure 524 px et sa ligne mono n'en demande
   que 427 — la bride cassait donc la ligne pour rien, et le bandeau français
   restait plus haut que l'anglais d'une ligne entière. C'est la colonne qui
   fixe la largeur, elle est déjà taillée sur la mesure. */
/* Sur ordinateur, trois colonnes taillées sur ce qu'elles ont à dire : la
   première assez large pour tenir « personne, de la maquette au déploiement »
   sur UNE ligne, et la case UE moins large — elle avait un grand vide à sa
   droite (07/09). */
@media (min-width: 760px) {
  /* Largeurs prises sur la MESURE, pas sur une estimation (07/09) : les deux
     premières lignes mono demandent 326 et 272 px marges comprises, mesurées
     au canvas dans leur vraie police. Le reste va à la case européenne, dont
     la ligne tient sur deux lignes sans gêner personne. J'avais d'abord
     estimé 310 et 258 : huit pixels de moins, et les deux lignes passaient
     à la ligne. */
  /* La ligne mono du bandeau reste à 11,2 px, en connaissance de cause : la
     monter à 12,1 px rallonge le bandeau de 21 px, or Anthony venait de
     demander l'inverse (« réduis la taille du bandeau », 06/09). Le zoom du
     téléphone reste libre, c'est ce qui compte pour la lisibilité. */
  .stats { grid-template-columns: 1.17fr .98fr 1.85fr; }
  .stat-large { grid-column: auto; }
}

.chiffres { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 8px 22px; }
.chiffres li { font-size: .95rem; color: var(--encre-2); }
.chiffres b { font: 800 1.5rem/1.15 var(--serif); color: var(--encre); letter-spacing: -.03em; margin-right: .25em; }

/* -------------------------------------------------------------- cartes */
.carte { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: clamp(20px, 3vw, 32px); min-width: 0; box-shadow: 0 20px 50px -40px rgba(11, 46, 31, .4); }
.carte > :last-child { margin-bottom: 0; }
.carte .h3 { margin-bottom: .4em; }
.carte-accent { border-top: 4px solid var(--accent-vif); }
.carte-nuit { background: var(--nuit); color: var(--nuit-txt); border-color: var(--nuit); }
.carte-nuit a { color: var(--menthe); }
.carte-nuit .etq { color: var(--menthe); }
.carte-nuit .sourdine { color: var(--nuit-txt-2); }

.liste { list-style: none; padding: 0; margin: 0 0 1em; display: grid; gap: 10px; }
.liste li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: start; }
.liste li::before { content: ""; width: 8px; height: 8px; margin-top: calc((1.6em - 8px) / 2); background: var(--accent-vif); transform: rotate(45deg); }
.liste-vert li::before { background: var(--vert); }
.liste li b { font-weight: 700; }

/* ------------------------------------------------- cas (réalisations) */
.cas { display: grid; grid-template-columns: minmax(0, 1fr); gap: 28px; margin-bottom: 22px; }
.cas-txt > :last-child { margin-bottom: 0; }
.cas-vis { position: relative; min-width: 0; }
@media (min-width: 860px) {
  .cas { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: center; gap: 40px; }
  .cas-inv .cas-txt { order: 2; }
  .cas-inv .cas-vis { order: 1; }
}

/* -------------------------------------------------------------- planches */
/* Les cadres se voient. Blanc sur blanc, on devinait un navigateur ou un
   téléphone à sa forme, sans le lire (Anthony, 05/09) : la barre du
   navigateur est teintée, le téléphone et la tablette ont un corps sombre. */
.ordi { border: 1px solid rgba(11, 46, 31, .3); background: var(--carte); border-radius: 12px; box-shadow: var(--ombre); overflow: hidden; }
.ordi-barre { display: flex; align-items: center; gap: 6px; padding: 8px 12px; border-bottom: 1px solid rgba(11, 46, 31, .18); background: #CBE3D7; }
/* les trois pastilles d'une fenêtre : rouge, ambre, verte. Grises, elles ne
   disaient rien ; colorées, on reconnaît un navigateur au premier coup d'œil
   (Anthony, 05/09 : « le petit bouton vert et le petit bouton rouge »). */
.ordi-barre i { width: 8px; height: 8px; border-radius: 50%; background: rgba(11, 46, 31, .3); }
.ordi-barre i:nth-of-type(1) { background: #EE6A5F; }
.ordi-barre i:nth-of-type(2) { background: #F5BD4F; }
.ordi-barre i:nth-of-type(3) { background: #61C454; }
.ordi-barre .url { margin-left: 8px; font: 500 .64rem/1 var(--mono); color: var(--encre-2); letter-spacing: .02em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.ordi-ecran { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: #fff; }
.ordi-ecran img { position: absolute; left: 0; top: 0; width: 100%; height: auto; }
.tel { width: 160px; border: 1px solid var(--nuit); border-radius: 28px; padding: 7px; background: var(--nuit); box-shadow: 0 22px 40px -24px rgba(11, 46, 31, .7); }
.tel-ecran { position: relative; aspect-ratio: 9 / 19; border-radius: 20px; overflow: hidden; background: #fff; }
.tel-ecran img { position: absolute; left: 0; top: 0; width: 100%; height: auto; }
.tel-encoche { position: absolute; left: 50%; top: 6px; width: 34%; height: 6px; transform: translateX(-50%); background: var(--encre); border-radius: 999px; opacity: .85; z-index: 3; }
.tel-grand { width: min(100%, 300px); }
/* la tablette : le même corps sombre que le téléphone, en plus large — le
   formulaire d'inscription y est lisible, ce qu'il n'était pas sur un
   téléphone (Anthony, 05/09). L'écran garde les proportions de la capture. */
.tablette { width: 100%; border: 1px solid var(--nuit); border-radius: 20px; padding: 12px 10px; background: var(--nuit); box-shadow: 0 24px 44px -26px rgba(11, 46, 31, .7); }
.tablette-ecran { position: relative; aspect-ratio: 41 / 59; border-radius: 8px; overflow: hidden; background: #fff; }
.tablette-ecran img { position: absolute; left: 0; top: 0; width: 100%; height: auto; }
.tel-grand .tel-ecran { border-radius: 24px; }
/* les écrans qui changent de page : l'image suivante se pose par-dessus */
.ecran-rot .img-suivant { opacity: 0; z-index: 2; }
.ecran-rot .img-suivant.in { opacity: 1; }

.planche-duo { position: relative; padding-bottom: 54px; }
.planche-duo .ordi { margin-right: 44px; }
.planche-duo .tel { position: absolute; right: 0; bottom: 0; }
@media (max-width: 480px) {
  .planche-duo { padding-bottom: 0; }
  .planche-duo .ordi { margin-right: 0; }
  .planche-duo .tel { position: static; margin: -30px 0 0 auto; width: 140px; }
}
.planches { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 24px; align-items: start; }
.planche-leg { margin-top: 10px; font: 500 .76rem/1.5 var(--mono); color: var(--encre-2); }
.planche-tel { display: grid; justify-items: center; }

/* --------------------------------------------------------- frise / dates */
.frise { list-style: none; margin: 0 0 1em; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 14px; }
.frise li { border-top: 2px solid var(--encre); padding-top: 12px; min-width: 0; }
.frise time, .frise .date { display: block; font: 700 .7rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); margin-bottom: 6px; }
.frise b { display: block; font: 800 1.15rem/1.25 var(--serif); letter-spacing: -.025em; margin-bottom: 4px; }
.frise p { margin: 0; font-size: .95rem; color: var(--encre-2); }

/* ------------------------------------------------------------- espaces */
.espaces { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr)); gap: 18px; }
.espace { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: clamp(18px, 2.4vw, 26px); min-width: 0; }
.espace .etq { margin-bottom: 8px; }
.espace .h3 { margin-bottom: .2em; }
.espace .route { font: 500 .72rem/1.3 var(--mono); color: var(--encre-2); margin: 0 0 12px; }
.espace .liste { gap: 7px; font-size: .95rem; }

/* --------------------------------------------------------- automatismes */
.auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 14px; }
.auto-item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; align-items: start; background: var(--carte); border: 1px solid var(--filet); border-radius: 14px; padding: 16px 18px; }
.auto-item .tag { font: 700 .62rem/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--vert); background: var(--vert-pale); border-radius: 6px; padding: 7px 8px; margin-top: 2px; white-space: nowrap; }
.auto-item p { margin: 0; font-size: .97rem; }
.auto-item b { display: block; font-weight: 700; margin-bottom: 2px; }

/* --------------------------------------------------------------- pile */
.pile { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 10px; }
.pile li { background: var(--carte); border: 1px solid var(--filet); border-radius: 12px; padding: 12px 14px; font-size: .93rem; min-width: 0; }
.pile li b { display: block; font-family: var(--mono); font-weight: 600; font-size: .78rem; letter-spacing: .02em; margin-bottom: 2px; }

/* --------------------------------------------------------------- offres */
.offre { display: grid; grid-template-rows: auto auto 1fr auto; }
.offre .prix { border-top: 1px solid var(--filet); padding-top: 16px; margin-top: 18px; }
.offre .prix b { display: block; font: 800 clamp(1.4rem, 2.2vw, 1.9rem)/1.2 var(--serif); letter-spacing: -.03em; margin-bottom: 4px; }
.offre .prix p { margin: 0; color: var(--encre-2); font-size: .95rem; }

/* -------------------------------------------------------------- méthode */
.methode { counter-reset: etape; }
.methode .carte { position: relative; padding-top: 26px; }
.methode .carte::before { counter-increment: etape; content: counter(etape, decimal-leading-zero); position: absolute; top: 18px; right: 20px; font: 600 .72rem/1 var(--mono); letter-spacing: .08em; color: var(--accent-txt); }

/* ------------------------------------------------------- qui construit
   Le portrait est le soleil ; cinq repères (Dauphine, FIDE, ASU, ML,
   YouTube) tournent autour, en remplacement des puces (Anthony, 07/09 :
   « des icônes en orbite comme des planètes, on clique, ça zoome, et un
   résumé avec quelques lignes et des liens »).

   Mécanique, tout en CSS : chaque astre est posé en haut d'un porte-astre
   (un carré invisible centré sur le soleil) que l'on tourne de --a ; l'astre
   contre-tourne du même angle pour rester droit. L'animation ajoute 360° aux
   deux, à la même vitesse --t. Sans script, les astres tournent quand même
   et la fiche du premier est ouverte ; le script ne fait que changer de
   fiche au clic (les autres fiches sont dépliées par <noscript>). Avec
   « réduire le mouvement », les astres sont posés sur leur orbite, immobiles.

   Le « zoom » du clic est un scale() sur .astre-corps, jamais sur .astre :
   le transform de l'astre appartient à l'animation. Sous 600 px, une seule
   orbite (deux se touchaient sur un 320 px) ; les cinq astres s'y répartissent. */
.qui { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; align-items: center; }
.qui-texte .chapo { margin-bottom: 0; }
.systeme { --d: 46px; --ico: 22px; position: relative; width: min(100%, 520px); aspect-ratio: 1; margin: 0 auto 26px; }
.orbite, .porte-astre { position: absolute; border-radius: 50%; }
.orbite { border: 1px dashed rgba(4, 120, 87, .34); pointer-events: none; }
.porte-astre { pointer-events: none; transform: rotate(var(--a)); }
/* Les cinq astres partagent UN SEUL cercle (à 9 % du bord : rayon 205 px sur
   un système de 500, soit 235 px entre deux voisins — les noms font 70 px) ;
   l'anneau intérieur, à 22 %, ne porte plus rien : c'est du décor qui dérive.
   Deux anneaux à des vitesses différentes se croisaient : 45 chevauchements
   mesurés sur 48 instants de la ronde (07/09). */
.orbite-1 { inset: 22%; }
.orbite-2, .porte-astre { inset: 9%; }
.porte-astre { --t: 52s; }
.p-dauphine { --a: -90deg; }
.p-fide { --a: -18deg; }
.p-asu { --a: 54deg; }
.p-ml { --a: 126deg; }
.p-youtube { --a: 198deg; }
.soleil { position: absolute; inset: 27%; border-radius: 50%; overflow: hidden; background: var(--carte); box-shadow: 0 0 0 4px var(--carte), var(--ombre); }
.soleil img { width: 100%; height: 100%; object-fit: cover; }
.astre {
  position: absolute; left: 50%; top: 0; width: var(--d); height: var(--d);
  margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2); padding: 0; border: 0; border-radius: 50%;
  background: none; font: inherit; color: inherit; cursor: pointer; pointer-events: auto;
  transform: rotate(calc(-1 * var(--a)));
}
.astre-corps {
  position: relative; display: grid; place-items: center; width: 100%; height: 100%; border-radius: 50%;
  background: var(--carte); border: 1px solid var(--filet); color: var(--encre);
  box-shadow: 0 10px 24px -14px rgba(11, 46, 31, .45);
  transition: transform .45s cubic-bezier(.2, .8, .2, 1.25), background-color .3s ease, color .3s ease, border-color .3s ease;
}
.astre-corps svg { width: var(--ico); height: var(--ico); fill: currentColor; }
.astre-nom {
  position: absolute; top: calc(100% + 8px); left: 50%; transform: translateX(-50%); white-space: nowrap;
  font: 600 .72rem/1.2 var(--mono); letter-spacing: .06em; text-transform: uppercase; color: var(--encre-2);
}
.astre:hover .astre-corps { transform: scale(1.1); border-color: var(--encre); }
.astre.actif .astre-corps { transform: scale(1.28); background: var(--nuit); border-color: var(--nuit); color: #fff; box-shadow: 0 0 0 4px var(--papier-2), 0 0 0 6px var(--accent-vif); }
.astre.actif .astre-nom { top: calc(100% + 16px); color: var(--encre); }
/* la fiche : le résumé de l'astre choisi, quelques lignes et des liens */
/* les fiches sont empilées dans la même case : la hauteur est celle de la plus
   haute, rien ne saute en dessous quand on change d'astre ; une fiche cachée
   reste `hidden` (le script, l'accessibilité) mais tient sa place */
/* `align-items: start` : chaque fiche garde SA hauteur (sans quoi la grille
   les étire toutes à celle de la plus haute) ; `site.js` pose la hauteur de
   la fiche visible sur le conteneur, et elle glisse d'une fiche à l'autre. */
.fiches { display: grid; align-items: start; margin-top: 4px;
  transition: height .4s cubic-bezier(.2, .8, .2, 1); }
.fiche { grid-area: 1 / 1; position: relative; background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: 22px 24px 16px;
  transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
.fiche[hidden] { display: block; visibility: hidden; pointer-events: none; }
.fiche-tete { display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 16px; align-items: center; margin-bottom: 14px; }
.fiche-icone { position: relative; width: 52px; height: 52px; border-radius: 50%; background: var(--nuit); color: #fff; display: grid; place-items: center; }
/* le disque de la fiche a sa propre orbite et un satellite : la carte vit */
.fiche-icone::before { content: ""; position: absolute; inset: -9px; border-radius: 50%; border: 1px dashed rgba(4, 120, 87, .35); }
.fiche-icone::after { content: ""; position: absolute; top: 50%; left: 50%; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%;
  background: var(--accent-vif); box-shadow: 0 0 0 2px var(--carte); transform: rotate(-50deg) translateX(35px); }
.fiche-icone svg { width: 26px; height: 26px; fill: currentColor; }
.fiche-sur { font: 600 .72rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); margin: 0 0 4px; }
.fiche h3 { margin: 0; }
.fiche p { margin: 0 0 .5em; }
.fiche code { font: 600 .85rem/1.6 var(--mono); background: var(--papier); padding: 2px 8px; border-radius: 6px; white-space: nowrap; }
.fiche-liens { display: flex; flex-wrap: wrap; gap: 0 22px; }
.fiche-liens .lien { display: inline-flex; align-items: center; min-height: 44px; }
@media (max-width: 599px) {
  .orbite-1 { display: none; }
  /* 12 % et non 8 % : l'onde de l'astre choisi grandit jusqu'à 155 %, et à
     8 % elle sortait de la fenêtre quand l'astre passait à droite — 1 px de
     débordement horizontal, intermittent (il suit la ronde), attrapé par
     verifier_rendu.py le 07/09. Un pseudo-élément ne se voit pas dans le DOM :
     il a fallu le déduire, aucun élément listé ne dépassait. */
  .orbite-2, .porte-astre { inset: 12%; }
  .soleil { inset: 30%; }
  .porte-astre { --t: 44s; }
}
@media (min-width: 600px) { .systeme { --d: 56px; --ico: 26px; } }
@media (min-width: 1000px) {
  .qui { grid-template-columns: minmax(0, 500px) minmax(0, 1fr); grid-template-areas: "systeme texte" "systeme suite"; gap: 8px 56px; }
  .qui-texte { grid-area: texte; align-self: end; }
  .systeme { grid-area: systeme; --d: 60px; --ico: 26px; margin: 0; }
  .qui-suite { grid-area: suite; align-self: start; }
}
@media (prefers-reduced-motion: no-preference) {
  .porte-astre { animation: orbite var(--t) linear infinite; }
  .astre { animation: contre-orbite var(--t) linear infinite; }
  .fiche-icone { animation: fiche-pop .4s cubic-bezier(.2, .8, .2, 1.2) both; }
  /* vivant (07/09) : le soleil respire, les orbites dérivent, une queue de
     comète derrière chaque astre (un cône de 48° masqué en anneau, porté par
     le porte-astre qui tourne avec lui), l'astre choisi ondule, la fiche
     arrive d'un ressort et son satellite tourne ; tout dort hors écran */
  .soleil { animation: respire 7s ease-in-out infinite; }
  .orbite { animation: derive 120s linear infinite; }
  .porte-astre::before { content: ""; position: absolute; inset: 0; border-radius: 50%;
    background: conic-gradient(from 312deg, rgba(4, 120, 87, 0) 0deg, rgba(4, 120, 87, .55) 48deg, rgba(4, 120, 87, 0) 48.5deg);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px), #000 calc(100% - 1px), transparent 100%);
    mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px), #000 calc(100% - 1px), transparent 100%); }
  .astre.actif .astre-corps::after { content: ""; position: absolute; inset: -7px; border-radius: 50%; border: 2px solid var(--accent-vif); animation: onde 1.8s ease-out infinite; }
  /* pas de scale() ici : a .98, le lien de la fiche mesurait 43 px au lieu de 44 (verifier_rendu, 07/09) */
  .fiche.vient { animation: fiche-vient .55s cubic-bezier(.2, .8, .2, 1) backwards; }
  .fiche-icone::after { animation: satellite 3.4s linear infinite; }
  .dort .porte-astre, .dort .astre, .dort .orbite, .dort .soleil, .dort .fiche-icone::after, .dort .astre-corps::after, .dort .fiche.vient { animation-play-state: paused; }
  @keyframes respire { 50% { transform: scale(1.035); } }
  @keyframes derive { to { transform: rotate(360deg); } }
  @keyframes onde { from { transform: scale(.92); opacity: .9; } to { transform: scale(1.55); opacity: 0; } }
  @keyframes fiche-vient { from { transform: translateY(14px); } }
  @keyframes satellite { from { transform: rotate(-50deg) translateX(35px); } to { transform: rotate(310deg) translateX(35px); } }
  @keyframes orbite { to { transform: rotate(calc(var(--a) + 360deg)); } }
  @keyframes contre-orbite { to { transform: rotate(calc(-1 * var(--a) - 360deg)); } }
  @keyframes fiche-pop { from { transform: scale(.6); } to { transform: scale(1); } }
}
/* La ronde ne s'arrête PAS sous la souris (07/09 : « fais en sorte que ça
   continue à tourner même quand on a la souris dessus »). À 52 s le tour sur
   un rayon de 205 px, un astre avance de 25 px/s : il met 2,4 s à se décaler
   de sa propre largeur, il se clique sans effort. Elle s'arrête au CLAVIER,
   où l'on doit pouvoir lire le nom de l'astre qu'on vient d'atteindre. */
@media (prefers-reduced-motion: no-preference) {
  .systeme:has(.astre:focus-visible) .porte-astre, .systeme:has(.astre:focus-visible) .astre { animation-play-state: paused; }
}
.badges { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 8px; }
.badges li { font: 600 .72rem/1.3 var(--mono); letter-spacing: .04em; border: 1px solid var(--filet); border-radius: 8px; padding: 7px 12px; background: var(--carte); }

/* -------------------------------------------------------------- contact */
.contact-bloc { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 18px; align-items: start; }
.adresse { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.adresse a { display: inline-flex; align-items: center; min-height: 44px; font: 800 clamp(1.05rem, 1.8vw, 1.35rem)/1.3 var(--serif); letter-spacing: -.02em; color: var(--encre); word-break: break-all; }
.copier { min-height: 44px; padding: .4em 1em; border: 1px solid var(--filet); border-radius: 10px; background: var(--carte); font: 700 .74rem/1 var(--mono); letter-spacing: .06em; cursor: pointer; color: var(--encre); }
.copier:hover { border-color: var(--encre); }

/* ---------------------------------------------------------------- pied */
.pied { border-top: 1px solid var(--filet); padding-top: 44px; padding-bottom: 40px; font-size: .93rem; color: var(--encre-2); background: var(--papier-2); }
.pied-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 26px; }
.pied-col > :last-child { margin-bottom: 0; }
.pied-col .marque { margin-bottom: 10px; }
.pied-titre { font: 700 .72rem/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--encre); margin-bottom: 10px; }
.pied-liste { list-style: none; padding: 0; margin: 0; display: grid; gap: 0; line-height: 1.9; }
.pied a { color: var(--encre); }
/* cibles tactiles ≥ 44 px, mesurées par verifier_rendu.py à 390 px : les liens du pied
   faisaient 32 px, le courriel 20, le lien du dépôt 41 (06/09) */
.pied-liste a, .pied-col > p > a, .pied-bas a { display: inline-flex; align-items: center; min-height: 44px; line-height: 1.5; }
.pied a:hover { color: var(--accent-txt); }
.pied-bas { margin: 30px 0 0; padding-top: 18px; border-top: 1px solid var(--filet); font-size: .85rem; }

/* ------------------------------------------------------------ tableaux */
.tbl { width: 100%; border-collapse: collapse; font-size: .96rem; }
.tbl th, .tbl td { padding: 12px 10px; border-bottom: 1px solid var(--filet); text-align: left; vertical-align: top; }
.tbl th { font: 600 .7rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--encre-2); }
@media (max-width: 640px) {
  .tbl thead { display: none; }
  .tbl tr { display: block; border: 1px solid var(--filet); border-radius: 10px; padding: 8px 12px; margin-bottom: 10px; background: var(--carte); }
  .tbl td { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 10px; border: 0; padding: 6px 0; }
  .tbl td::before { content: attr(data-label); font: 600 .66rem/1.5 var(--mono); text-transform: uppercase; letter-spacing: .06em; color: var(--encre-2); }
}

/* ------------------------------------------------------------- schéma */
.schema { display: grid; gap: 12px; }
.schema-ligne { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); gap: 12px; }
.schema-boite { background: var(--carte); border: 1px solid var(--filet); border-radius: 12px; padding: 12px 14px; text-align: center; min-width: 0; }
.schema-boite b { display: block; font: 800 1.05rem/1.2 var(--serif); letter-spacing: -.02em; }
.schema-boite span { display: block; font: 500 .7rem/1.4 var(--mono); color: var(--encre-2); margin-top: 3px; }
.schema-base { background: var(--nuit); color: var(--nuit-txt); border-color: var(--nuit); }
.schema-base span { color: var(--nuit-txt-2); }
.schema-lien { text-align: center; font: 500 .7rem/1.3 var(--mono); color: var(--encre-2); letter-spacing: .06em; }

/* --------------------------------------------------- graphique (nuit) */
.graph { background: var(--nuit); color: var(--nuit-txt); border-radius: var(--rayon); padding: clamp(16px, 2.5vw, 28px); overflow: hidden; }
.graph-tete { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 16px; font: 600 .7rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--nuit-txt-2); margin-bottom: 12px; }
.graph svg { width: 100%; height: auto; }
.graph .balayage { display: none; }
.signaux { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; font: 500 .8rem/1.5 var(--mono); }
.signaux li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: baseline; border-top: 1px solid var(--nuit-2); padding-top: 8px; }
.signaux .tag-bas { color: var(--menthe); }
.signaux .tag-haut { color: var(--ambre); }

/* ------------------------------------------------------- pages légales */
.legal { max-width: 74ch; }
.legal h2 { font-size: 1.4rem; margin-top: 1.8em; }
.legal p, .legal li { font-size: 1rem; }

/* ---------------------------------------------------------- chemin riche
   Tout ce qui bouge n'existe qu'ici : grand écran, souris, pas de demande de
   réduction du mouvement. Les classes .rv / .in sont posées par le script,
   uniquement sur ce chemin, puis retirées après la révélation.
   ------------------------------------------------------------------------ */
@media (min-width: 1024px) and (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .btn, .carte, .langue, .espace, .auto-item { transition: transform .35s cubic-bezier(.2, .7, .2, 1), box-shadow .35s cubic-bezier(.2, .7, .2, 1), background-color .2s ease, color .2s ease, border-color .2s ease; }
  .btn:hover { transform: translateY(-2px); }
  .carte:hover, .espace:hover, .auto-item:hover { transform: translateY(-3px); box-shadow: 0 28px 50px -30px rgba(11, 46, 31, .45); }
  .planche-duo .ordi-ecran img, .planche-duo .tel-ecran img, .planches .ordi-ecran img, .planche-tel .tel-ecran img { transition: transform 6s linear, opacity .9s ease; }
  .ecran-rot .img-suivant { transition: opacity .9s ease; }
  .rv { opacity: .55; transform: translateY(14px); transition: opacity .7s cubic-bezier(.2, .7, .2, 1), transform .7s cubic-bezier(.2, .7, .2, 1); }
  .rv.in { opacity: 1; transform: none; }
  .iso-dalle .flotte { animation: flotte var(--d, 9s) cubic-bezier(.45, 0, .55, 1) infinite alternate; animation-delay: var(--r, 0s); }
  @keyframes flotte { from { transform: translateZ(var(--z, 16px)); } to { transform: translateZ(calc(var(--z, 16px) + var(--l, 26px))); } }
  .serpentin-trace .trait { transition: stroke-dashoffset .25s linear; }
  .serp-titre::before { transition: background-color .4s ease, border-color .4s ease, box-shadow .4s ease; }
}

/* --------------------------------------------- la méthode, en serpentin
   Un chemin en S descend au centre ; les titres sont posés dessus, les
   contenus alternent à gauche et à droite. Le chemin se dessine au
   défilement sur grand écran ; ailleurs il est dessiné d'un bloc. */
.serpentin { position: relative; margin-top: 10px; }
.serpentin-trace { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.serpentin-trace path { fill: none; stroke-width: 3; stroke-linecap: round; }
.serpentin-trace .fond { stroke: var(--filet); }
.serpentin-trace .trait { stroke: var(--accent); }
/* textes raccourcis le 06/09 : les étapes se resserrent, sinon le chemin traverse du vide */
.serp-etapes { list-style: none; margin: 0; padding: 20px 0; display: grid; gap: clamp(40px, 6vw, 80px); }
.serp-etape { display: grid; grid-template-columns: minmax(0, 1fr) 180px minmax(0, 1fr); gap: 0 20px; align-items: center; min-height: 300px; }
.serp-titre { grid-column: 2; text-align: center; position: relative; }
.serp-titre::before { content: ""; display: block; width: 18px; height: 18px; border-radius: 50%; background: var(--carte); border: 3px solid var(--filet); margin: 0 auto 12px; position: relative; z-index: 1; }
.serp-etape.passe .serp-titre::before { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 0 7px var(--accent-pale); }
.serp-titre .num { display: block; font: 600 .71rem/1.5 var(--mono); letter-spacing: .12em; color: var(--accent-txt); }
.serp-titre h3 { font-size: 1.1rem; letter-spacing: -.02em; margin: 4px 0 0; }
/* le titre est posé SUR le chemin : son fond masque le trait derrière lui, comme un panneau */
.serp-titre .num, .serp-titre h3 { position: relative; z-index: 1; background: var(--papier); display: table; margin: 0 auto; padding: 0 10px; }
.serp-titre h3 { margin-top: 4px; }
.serp-contenu { grid-column: 1; min-width: 0; }
.serp-etape.droite .serp-contenu { grid-column: 3; }
.serp-contenu p { margin: 0 0 14px; }
.serp-contenu > .v-carte { margin-top: 4px; }
@media (max-width: 899px) {
  .serp-etapes { gap: 44px; padding-left: 0; }
  .serp-etape { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .serp-titre { grid-column: 1; text-align: left; padding-left: 44px; }
  .serp-titre::before { position: absolute; left: 13px; top: 6px; margin: 0; }
  .serp-contenu, .serp-etape.droite .serp-contenu { grid-column: 1; padding-left: 44px; margin-top: 12px; }
  .serp-titre .num, .serp-titre h3 { display: inline-block; margin: 0; background: none; padding: 0 8px 0 0; }
}

/* ------------------------------------------------------ téléphone */
@media (max-width: 480px) {
  .hero { padding-top: 28px; }
  .actions .btn { flex: 1 1 100%; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat { padding: 14px 14px 12px; }
  .stat b { font-size: 1.7rem; }
  .chiffres { gap: 6px 16px; }
}

/* ------------------------------------------------------------- vitrines
   Un ordinateur qui déroule la page ENTIÈRE au survol (ou tout seul sur
   téléphone), un téléphone posé à côté, toujours devant — sauf quand la
   souris est sur l'ordinateur, où l'ordinateur passe devant. */
.vitrine { position: relative; padding-right: 96px; padding-bottom: 36px; }
.vitrine-ordi { position: relative; z-index: 1; }
.vitrine-tel { position: absolute; right: 0; bottom: 0; width: 176px; z-index: 2; }
.vitrine-tel .tel { width: 176px; }
.vitrine.ordi-devant .vitrine-ordi { z-index: 3; }
.vitrine-leg { margin: 10px 0 0; font: 500 .74rem/1.5 var(--mono); color: var(--encre-2); }
.defile.auto > img, .defile.auto .defile-piste { animation: defile var(--duree, 24s) linear infinite alternate; }
@keyframes defile { from { transform: translateY(0); } to { transform: translateY(var(--d, 0px)); } }
@media (max-width: 640px) {
  .vitrine { padding-right: 62px; padding-bottom: 28px; }
  .vitrine-tel { width: 124px; }
  .vitrine-tel .tel { width: 124px; padding: 6px; border-radius: 22px; }
  .vitrine-tel .tel-ecran { border-radius: 16px; }
}

/* ------------------------------------------------------------ carrousel */
.carrousel-tete { display: flex; justify-content: space-between; align-items: end; gap: 16px; margin-bottom: 14px; }
.carrousel-nav { display: flex; gap: 8px; flex: none; }
.carrousel-nav button { width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--filet); background: var(--carte); cursor: pointer; font-size: 1.1rem; color: var(--encre); }
.carrousel-nav button:hover { border-color: var(--encre); }
.carrousel { display: flex; align-items: end; gap: 18px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 4px 4px 18px; scrollbar-width: thin; scrollbar-color: var(--filet) transparent; }
.carrousel > * { flex: 0 0 min(84%, 600px); scroll-snap-align: start; min-width: 0; }
.carrousel > .carte-tel { flex-basis: min(58%, 250px); }
.carrousel > .carte-tab { flex-basis: min(72%, 400px); }
.carte-ecran { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: 14px; }
/* dans un carrousel, la carte blanche autour de l'appareil ne sert à rien :
   on montre l'appareil, et son nom dessous, centré (Anthony, 05/09). */
.carrousel .carte-ecran { background: none; border: 0; padding: 0; }
.carrousel .carte-ecran .leg { text-align: center; }
.carte-ecran .ordi-ecran { aspect-ratio: 16 / 10; }
.carte-ecran .tel { width: 100%; }
.carte-ecran .leg { margin: 12px 0 0; font-size: .92rem; line-height: 1.45; }
.carte-ecran .leg b { display: block; font-weight: 700; }
.carte-ecran .leg span { color: var(--encre-2); font-size: .86rem; }
.carte-ecran .leg .etq { margin: 0 0 6px; }

/* Quatre écrans en table de deux sur deux, chacun centré dans sa case et
   posé sur la ligne du bas (Anthony, 05/09 : « une table de quatre, deux en
   haut, deux en bas, centré »). En damier : la tablette et le téléphone sont
   hauts, les deux fenêtres sont larges. */
.ecrans-4 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px 30px; align-items: end; justify-items: center; margin-top: 26px; }
.ecrans-4 .carte-ecran { width: 100%; max-width: 520px; background: none; border: 0; padding: 0; }
.ecrans-4 .carte-ecran .leg { text-align: center; }
.ecrans-4 .carte-tel { max-width: 250px; }
.ecrans-4 .carte-tab { max-width: 400px; }
@media (max-width: 720px) { .ecrans-4 { grid-template-columns: minmax(0, 1fr); gap: 30px; } }

/* -------------------------------------------------------------- onglets */
.onglets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.onglet { min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--filet); background: var(--carte); font: 600 .92rem var(--sans); color: var(--encre); cursor: pointer; }
.onglet[aria-selected="true"] { background: var(--nuit); color: #fff; border-color: var(--nuit); }
.panneau { display: none; }
.panneau.actif { display: grid; grid-template-columns: minmax(0, 1fr); gap: 22px; align-items: start; }
@media (min-width: 860px) { .panneau.actif { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); } }
.panneau .liste { margin: 0; }
.panneau .ordi-ecran { aspect-ratio: 16 / 11; }

/* --------------------------------------------------- méthode interactive */
.etapes { display: grid; grid-template-columns: minmax(0, 1fr); gap: 22px; }
@media (min-width: 900px) { .etapes { grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); align-items: start; } }
.etape-liste { display: grid; gap: 8px; }
.etape { text-align: left; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; align-items: start; padding: 14px 16px 16px; border-radius: 14px; border: 1px solid var(--filet); background: var(--carte); cursor: pointer; font: inherit; color: inherit; position: relative; overflow: hidden; }
.etape .num { font: 600 .7rem/1.6 var(--mono); color: var(--accent-txt); }
.etape b { display: block; font-weight: 700; }
.etape span { display: block; color: var(--encre-2); font-size: .92rem; }
.etape[aria-selected="true"] { border-color: var(--nuit); box-shadow: 0 16px 34px -24px rgba(11, 46, 31, .5); }
.etape .barre { position: absolute; left: 0; bottom: 0; height: 3px; width: 0; background: var(--accent-vif); }
.etape[aria-selected="true"] .barre { width: 100%; transition: width var(--tempo, 5s) linear; }
.etape-visuel { position: relative; }
.visuel { display: none; }
.visuel.actif { display: block; }
.v-carte { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: 22px; box-shadow: var(--ombre); }
.v-nuit { background: #06130D; color: var(--nuit-txt); border-color: #06130D; font: 500 .82rem/1.8 var(--mono); }
.v-nuit .p { color: var(--menthe); }
.v-nuit .c { color: #8FC7A9; }
.v-nuit .curseur { display: inline-block; width: 8px; height: 14px; background: var(--menthe); vertical-align: -2px; }
.bulle { max-width: 86%; padding: 12px 14px; border-radius: 16px; background: var(--papier-2); margin: 0 0 10px; font-size: .95rem; }
.bulle.moi { margin-left: auto; background: var(--nuit); color: #fff; border-bottom-right-radius: 4px; }
.bulle.lui { border-bottom-left-radius: 4px; }
.bulle small { display: block; font: 500 .64rem var(--mono); letter-spacing: .06em; text-transform: uppercase; opacity: .7; margin-bottom: 4px; }
.frise-deploy { display: grid; gap: 10px; }
.frise-deploy div { display: grid; grid-template-columns: 92px minmax(0, 1fr) auto; gap: 12px; align-items: center; font-size: .92rem; }
.frise-deploy time { font: 600 .7rem var(--mono); color: var(--accent-txt); letter-spacing: .06em; }
.frise-deploy i { display: block; height: 10px; border-radius: 5px; background: var(--vert-pale); position: relative; overflow: hidden; }
.frise-deploy i::after { content: ""; position: absolute; inset: 0; width: var(--w, 40%); background: var(--accent); border-radius: 5px; }
.frise-deploy b { font: 600 .74rem var(--mono); color: var(--encre-2); }
.coche { list-style: none; padding: 0; margin: 0; display: grid; gap: 10px; }
.coche li { display: grid; grid-template-columns: 26px minmax(0, 1fr) auto; gap: 12px; align-items: center; font-size: .95rem; }
/* À 320 px, les trois colonnes ne tenaient pas : le libellé se cassait en
   quatre lignes et la valeur se posait par-dessus. La valeur passe alors sous
   le libellé, dans sa colonne (07/09). */
@media (max-width: 380px) {
  .coche li { grid-template-columns: 26px minmax(0, 1fr); row-gap: 2px; }
  .coche li b { grid-column: 2; }
}
.coche li i { width: 26px; height: 26px; border-radius: 50%; background: var(--vert-pale); color: var(--vert); display: grid; place-items: center; font-style: normal; font-weight: 800; font-size: .8rem; }
.coche li b { font: 600 .74rem var(--mono); color: var(--vert); }
.doc { font: 500 .84rem/1.9 var(--mono); color: var(--encre-2); }
.doc b { color: var(--encre); font-weight: 700; display: block; }
.doc .t { color: var(--accent-txt); }

/* -------------------------------------------- le moteur du Reversal Engine */
.moteur { position: relative; background: #06130D; color: var(--nuit-txt); border-radius: var(--rayon); overflow: hidden; box-shadow: var(--ombre); display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .moteur { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); } }
.moteur-graph { position: relative; min-height: 340px; }
.moteur canvas { display: block; width: 100%; height: 100%; }
.moteur-tete { position: absolute; left: 16px; top: 14px; right: 16px; display: flex; justify-content: space-between; gap: 10px; font: 600 .64rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: #8FC7A9; pointer-events: none; }
.moteur-tete b { color: var(--menthe); }
.flux { border-top: 1px solid #12352A; padding: 16px; }
@media (min-width: 900px) { .flux { border-top: 0; border-left: 1px solid #12352A; } }
.flux h3 { font: 600 .64rem/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: #8FC7A9; margin: 0 0 12px; }
.signal { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: start; background: #0B1F17; border: 1px solid #12352A; border-radius: 12px; padding: 12px 14px; margin-bottom: 10px; font: 500 .76rem/1.5 var(--mono); }
.signal i { font-style: normal; font-weight: 700; padding: 4px 8px; border-radius: 6px; background: #12352A; color: var(--menthe); }
.signal.haut i { color: var(--ambre); }
.signal.muet i { color: #8FC7A9; }
.signal b { display: block; color: var(--nuit-txt); font-weight: 600; }
.signal span { color: #8FC7A9; }
.signal.neuf { animation: signal-entre .6s cubic-bezier(.2, .7, .2, 1); }
@keyframes signal-entre { from { transform: translateY(-10px); } to { transform: none; } }
.moteur-note { margin: 4px 0 0; font: 500 .64rem/1.5 var(--mono); color: #8FC7A9; }

/* très petits téléphones : la scène entière tient dans 320 px */
@media (max-width: 360px) { .iso { --k: .42; } }

/* ------------------------------------------------ le manège des écrans
   Sur la page Molière, les quatre écrans sont posés sur un cercle vu de
   trois quarts : celui de devant est grand et net, les trois autres
   s'éloignent, rapetissent et s'assombrissent (Anthony, 05/09 : « quatre
   écrans qui pivotent en 3D… autour d'un cercle posé en 3D… le principal
   au milieu et les trois autres qui bougent un peu sur les côtés »). La
   projection — position, échelle, ordre de peinture, voile — est calculée
   par site.js en 2D ; ce CSS ne fait que poser les pièces et le sol.
   Sans script, ou si le visiteur demande moins de mouvement, la table de
   deux sur deux (.ecrans-4) reste telle quelle. */
.ecrans-4.anneau { display: block; position: relative; overflow: hidden; touch-action: pan-y; user-select: none; -webkit-user-select: none; margin-top: 10px; }
.ecrans-4.anneau .carte-ecran { position: absolute; left: 50%; top: 0; margin: 0; max-width: none; transform-origin: 50% 100%; cursor: grab; }
.ecrans-4.anneau.tire .carte-ecran { cursor: grabbing; }
.ecrans-4.anneau .carte-ecran .leg { display: none; }
.ecrans-4.anneau .carte-ecran img { pointer-events: none; -webkit-user-drag: none; }
/* le voile : plus une pièce est loin, plus elle s'assombrit — posé sur
   l'appareil lui-même, il en suit le rayon et le balancement */
.ecrans-4.anneau .carte-ecran > :first-child { position: relative; }
.ecrans-4.anneau .carte-ecran > :first-child::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--nuit); opacity: var(--voile, 0); pointer-events: none; z-index: 5; }
.anneau-sol { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.anneau-sol .disque { fill: url(#anneau-lueur); }
.anneau-sol .cercle { fill: none; stroke: var(--accent-vif); stroke-width: 1.5; opacity: .6; }
.anneau-sol .cercle-2 { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 3 9; opacity: .45; }
.anneau-sol .ancre { fill: var(--accent); opacity: .85; }
.anneau-cmd { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 14px; }
.anneau-btn { flex: none; width: 44px; height: 44px; border-radius: 50%; border: 1.5px solid var(--filet); background: var(--carte); color: var(--encre); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font: 700 1.3rem/1 var(--sans); padding: 0 0 3px; }
.anneau-btn:hover { border-color: var(--encre); }
.anneau-points { display: flex; }
/* 44 × 44 pour le doigt, un point de 10 px pour l'œil (verifier_rendu.py, 06/09) */
.anneau-point { position: relative; width: 44px; height: 44px; border: 0; padding: 0; background: none; cursor: pointer; }
.anneau-point::before { content: ""; position: absolute; inset: 17px; border-radius: 50%; background: var(--filet); }
.anneau-point[aria-current="true"]::before { background: var(--accent); }
.anneau-leg { text-align: center; margin: 12px 0 0; font-weight: 700; font-size: 1.05rem; min-height: 1.5em; transition: opacity .22s ease; }
.anneau-leg.change { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  /* les pièces des côtés se balancent doucement, pas toutes au même rythme ;
     celle de devant ne bouge pas (--amp: 0), on lit sa page */
  .ecrans-4.anneau .carte-ecran > :first-child { animation: bercer var(--bd, 5s) ease-in-out infinite alternate; animation-delay: var(--br, 0s); }
  @keyframes bercer { from { transform: translateY(calc(-4px * var(--amp, 1))); } to { transform: translateY(calc(4px * var(--amp, 1))); } }
}

/* ------------------------------------------- le réacteur des automatismes
   Une section nuit : le cœur au centre, les huit automatismes autour, reliés
   par des fils sur lesquels circule une impulsion (Anthony, 05/09 :
   « quelque chose de futuriste… que chaque truc ait des visuels et qu'il y
   ait un lien… disponible sur téléphone »). Les fils sont tracés par site.js
   d'après les positions réelles ; sans script, il reste huit cartes et le
   cœur. En dessous de 900 px : une colonne, un bus descend à gauche. */
.sec-nuit { position: relative; overflow: hidden; background: var(--nuit); color: var(--nuit-txt); border-top-color: var(--nuit-2); }
.sec-nuit::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(rgba(110, 231, 183, .07) 1px, transparent 1px), linear-gradient(90deg, rgba(110, 231, 183, .07) 1px, transparent 1px);
  background-size: 60px 60px;
  -webkit-mask-image: radial-gradient(ellipse at 50% 55%, #000 35%, transparent 85%); mask-image: radial-gradient(ellipse at 50% 55%, #000 35%, transparent 85%); }
.sec-nuit > .wrap { position: relative; z-index: 1; }
.sec-nuit .etq { color: var(--menthe); }
.sec-nuit .etq::before { background: var(--menthe); }
.sec-nuit .h2 { color: #fff; }
.sec-nuit h2 em { color: var(--menthe); }
.sec-nuit .chapo { color: var(--nuit-txt-2); }
.reacteur { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; padding-left: 24px; }
.reacteur-trace { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.reacteur-trace .branche { fill: none; stroke: rgba(110, 231, 183, .32); stroke-width: 1.5; }
.reacteur-trace .piste { fill: none; stroke: none; }
.reacteur-trace .pulse { fill: var(--menthe); }
.reacteur-trace .halo { fill: var(--menthe); opacity: .22; }
.reacteur-coeur { position: relative; z-index: 1; text-align: center; padding: 26px 22px 24px; margin-bottom: 22px; border-radius: 22px;
  background: linear-gradient(180deg, rgba(110, 231, 183, .16), rgba(110, 231, 183, .04)); border: 1px solid rgba(110, 231, 183, .5);
  box-shadow: 0 0 0 10px rgba(110, 231, 183, .05), 0 0 70px rgba(110, 231, 183, .16); }
.reacteur-coeur .etq { justify-content: center; margin-bottom: 8px; }
.reacteur-coeur b { display: block; font: 800 1.15rem/1.2 var(--serif); color: #fff; letter-spacing: -.02em; overflow-wrap: anywhere; }
.reacteur-meta { display: block; margin-top: 10px; font: 500 .72rem/1.6 var(--mono); color: var(--nuit-txt-2); }
.noeud { position: relative; z-index: 1; display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 4px 14px; align-items: start; padding: 14px 16px; border-radius: 14px; background: rgba(255, 255, 255, .04); border: 1px solid rgba(110, 231, 183, .24); }
.noeud-ico { grid-row: 1 / span 2; width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; color: var(--menthe); background: rgba(110, 231, 183, .1); border: 1px solid rgba(110, 231, 183, .32); }
.noeud-ico svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.noeud p { margin: 0; font-size: .95rem; line-height: 1.45; color: var(--nuit-txt); }
.noeud b { display: block; color: #fff; font-weight: 700; margin-bottom: 2px; }
.noeud .tag { grid-column: 2; justify-self: start; font: 700 .62rem/1.5 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--menthe); border: 1px solid rgba(110, 231, 183, .38); border-radius: 6px; padding: 3px 8px; margin-top: 4px; }
@media (min-width: 900px) {
  .reacteur { grid-template-columns: minmax(0, 1fr) 280px minmax(0, 1fr); gap: 18px 64px; align-items: center; padding-left: 0; }
  .reacteur-coeur { grid-column: 2; grid-row: 1 / span 4; margin-bottom: 0; }
  /* les enfants du réacteur : le svg (1), le cœur (2), puis les huit nœuds
     (3 à 10) — quatre à gauche, quatre à droite, sur quatre rangs */
  .noeud:nth-child(-n+6) { grid-column: 1; }
  .noeud:nth-child(n+7) { grid-column: 3; }
  .noeud:nth-child(3), .noeud:nth-child(7) { grid-row: 1; }
  .noeud:nth-child(4), .noeud:nth-child(8) { grid-row: 2; }
  .noeud:nth-child(5), .noeud:nth-child(9) { grid-row: 3; }
  .noeud:nth-child(6), .noeud:nth-child(10) { grid-row: 4; }
}
@media (prefers-reduced-motion: no-preference) {
  .reacteur-coeur::after { content: ""; position: absolute; inset: -12px; border-radius: 32px; border: 1px solid rgba(110, 231, 183, .5); animation: coeur 3.2s ease-out infinite; pointer-events: none; }
  @keyframes coeur { from { transform: scale(.96); opacity: .8; } to { transform: scale(1.1); opacity: 0; } }
}

/* --------------------------------------------------- les décisions, en schémas
   Trois cartes, chacune coiffée d'un petit schéma SVG qui bouge (Anthony,
   05/09 au soir : « les décisions doivent être faites de manière visuelle et
   dynamique »). La demande qui traverse écran → serveur → base et que la
   base accepte puis refuse ; la fenêtre de trois niveaux qui glisse sur les
   rayons de la bibliothèque ; la notification qui part du serveur pendant
   que le fil du navigateur est coupé. Sans mouvement réduit : les schémas
   sont animés en CSS ; sinon ils sont posés dans leur état le plus lisible. */
.decisions { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 18px; }
.decision { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); overflow: hidden; min-width: 0; box-shadow: 0 20px 50px -40px rgba(11, 46, 31, .4); }
.decision .schema { display: block; width: 100%; height: auto; background: var(--papier-2); border-bottom: 1px solid var(--filet); }
.decision .texte { padding: 18px 22px 22px; }
.decision .texte p { margin: 0; }
.schema text { font: 600 9.5px var(--mono); fill: var(--encre-2); letter-spacing: .02em; }
.schema .dans { fill: var(--encre); }
.schema .boite { fill: var(--carte); stroke: var(--encre-2); stroke-width: 1.2; }
.schema .boite-base { fill: var(--accent-pale); stroke: var(--accent); }
.schema .fil { fill: none; stroke: var(--encre-2); stroke-width: 1.2; stroke-linecap: round; }
.schema .fil-accent { stroke: var(--accent); }
.schema .fil-mort { stroke: #9DB3A8; stroke-dasharray: 3 4; }
.schema .pastille { fill: var(--encre-2); }
.schema .cadenas { fill: var(--accent); }
.schema .point-ok { fill: var(--accent); }
.schema .point-ko { fill: #DC2626; opacity: 0; }
.schema .point-mort { fill: #9DB3A8; }
.schema .croix { fill: none; stroke: #DC2626; stroke-width: 1.6; stroke-linecap: round; }
.schema .livre { fill: #CFE6DA; }
.schema .fenetre { fill: rgba(16, 185, 129, .22); stroke: var(--accent); stroke-width: 1.2; }
.schema .curseur { fill: var(--accent); }
.schema .ecran-tel { fill: var(--papier); }
.schema .badge { fill: var(--ambre); stroke: var(--carte); stroke-width: 2; transform-box: fill-box; transform-origin: center; }
.schema .s2-fen { transform: translateX(86px); }
.schema .s3-badge { transform: scale(1); }
@media (prefers-reduced-motion: no-preference) {
  .schema .s1-ok { animation: s1-ok 6s linear infinite; }
  .schema .s1-ko { animation: s1-ko 6s linear infinite; }
  .schema .s1-base { animation: s1-base 6s linear infinite; }
  @keyframes s1-ok { 0% { transform: translateX(0); opacity: 1; } 40% { transform: translateX(216px); opacity: 1; } 46%, 100% { transform: translateX(216px); opacity: 0; } }
  @keyframes s1-ko { 0%, 50% { transform: translateX(0); opacity: 0; } 52% { transform: translateX(0); opacity: 1; } 76%, 80% { transform: translateX(178px); opacity: 1; } 98% { transform: translateX(0); opacity: 1; } 100% { transform: translateX(0); opacity: 0; } }
  @keyframes s1-base { 0%, 36%, 46%, 74%, 84%, 100% { stroke: var(--accent); stroke-width: 1.2; } 40% { stroke: var(--accent); stroke-width: 3; } 78% { stroke: #DC2626; stroke-width: 3; } }
  .schema .s2-fen { animation: s2-fen 10s ease-in-out infinite; }
  @keyframes s2-fen { 0%, 22% { transform: translateX(86px); } 32%, 52% { transform: translateX(129px); } 62%, 88% { transform: translateX(43px); } 100% { transform: translateX(86px); } }
  .schema .s3-pt { animation: s3-pt 4.5s linear infinite; }
  .schema .s3-badge { animation: s3-badge 4.5s ease-out infinite; }
  .schema .s3-mort { animation: s3-mort 4.5s linear infinite; }
  .schema .s3-x { animation: s3-x 4.5s linear infinite; }
  @keyframes s3-pt { 0% { transform: translateX(0); opacity: 1; } 50% { transform: translateX(226px); opacity: 1; } 54%, 100% { transform: translateX(226px); opacity: 0; } }
  @keyframes s3-badge { 0%, 50% { transform: scale(0); } 58% { transform: scale(1.25); } 64%, 92% { transform: scale(1); } 100% { transform: scale(0); } }
  @keyframes s3-mort { 0% { transform: translateX(0); opacity: 1; } 40% { transform: translateX(100px); opacity: 1; } 46%, 100% { transform: translateX(100px); opacity: 0; } }
  @keyframes s3-x { 0%, 38% { opacity: .35; } 42%, 60% { opacity: 1; } 70%, 100% { opacity: .35; } }
}

/* ------------------------------------------------------- le socle, vivant
   Les briques de la plateforme, chacune avec son logo en couleur de marque
   (tracés Simple Icons, domaine public), qui flottent et respirent, pas
   toutes au même rythme (Anthony, 05/09 : « les logos en dynamiques
   vivants »). --marque, --r et --d sont posés dans le HTML. */
/* en flex centré, pas en grille : onze tuiles font 6 + 5 centrées, là où la
   grille laissait quatre tuiles collées à gauche sous une rangée de sept */
.socle { list-style: none; margin: 26px 0 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.socle li { flex: 0 1 172px; position: relative; background: var(--carte); border: 1px solid var(--filet); border-radius: 14px; padding: 18px 14px 14px; text-align: center; min-width: 0; box-shadow: 0 14px 24px -20px rgba(11, 46, 31, .45); }
.socle svg { display: block; width: 34px; height: 34px; margin: 0 auto 10px; fill: var(--marque, var(--encre)); }
.socle b { display: block; font: 700 .8rem/1.3 var(--mono); letter-spacing: .01em; color: var(--encre); }
.socle span { display: block; margin-top: 3px; font-size: .8rem; line-height: 1.4; color: var(--encre-2); }
@media (prefers-reduced-motion: no-preference) {
  /* rien que du transform : animer l'ombre repeignait onze tuiles à chaque
     image, et ça saccadait (Anthony, 06/09 : « les logos vivent mal, c'est
     pas fluide »). L'ombre est fixe, le mouvement est composé par la carte
     graphique. */
  .socle li { animation: socle-flotte var(--d, 6s) ease-in-out infinite alternate; animation-delay: var(--r, 0s); will-change: transform; }
  .socle svg { animation: socle-souffle 3.6s ease-in-out infinite alternate; animation-delay: var(--r, 0s); will-change: transform; }
  @keyframes socle-flotte { from { transform: translateY(0); } to { transform: translateY(-7px); } }
  @keyframes socle-souffle { from { transform: scale(1) rotate(-2deg); } to { transform: scale(1.1) rotate(2deg); } }
}
@media (min-width: 1024px) and (hover: hover) and (pointer: fine) {
  .socle li { transition: border-color .3s ease; }
  .socle li:hover { border-color: var(--marque, var(--encre)); }
}

/* ------------------------------------------- l'écran qui enchaîne les pages
   Les onze pages de l'espace élève dans un seul cadre : la suivante arrive
   de la droite et recouvre la précédente (Anthony, 05/09 : « trois secondes
   après, ça switch… ça arrive de droite à gauche »). Le script pose .entrant
   puis .actif ; sans script, seule la première page (.actif) est visible.
   Les captures viennent de l'écran d'Anthony (compte d'essai), pas de
   rafraichir.py : l'espace demande une connexion. */
.enchaine img { opacity: 0; z-index: 0; }
.enchaine img.actif { opacity: 1; z-index: 1; }
.enchaine img.entrant { opacity: 1; z-index: 2; }
@media (prefers-reduced-motion: no-preference) {
  /* en fondu, pas en glissement (Anthony, 06/09 : « fais un dégradé pour passer de l'un à l'autre ») */
  .enchaine img { transition: opacity .8s ease; }
}

/* ------------------------------------------------- les quatre plans (Molière)
   Les espaces professeur et administration contiennent des données de
   familles : pas de capture. À la place, un PLAN d'architecte de chaque
   espace (Anthony, 06/09 : « certaines pages ne peuvent pas être
   affichées… un design dynamique, parlant »). Le menu est le vrai menu de
   l'espace ; chaque bloc répond à une puce de la liste (data-bloc) : la
   survoler allume le bloc. À l'ouverture d'un onglet, les blocs entrent
   l'un après l'autre. */
.plan-col { min-width: 0; }
.plan-promesse { margin: 0 0 12px; font: 700 1.05rem/1.4 var(--sans); letter-spacing: -.01em; color: var(--encre); }
.plan { background: var(--nuit); color: var(--nuit-txt); border-radius: 16px; padding: 14px; box-shadow: var(--ombre); font: 500 .68rem/1.4 var(--mono);
  background-image: linear-gradient(rgba(110, 231, 183, .07) 1px, transparent 1px), linear-gradient(90deg, rgba(110, 231, 183, .07) 1px, transparent 1px); background-size: 24px 24px; }
.plan-tete { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; padding: 0 2px 10px; border-bottom: 1px solid rgba(110, 231, 183, .25); margin-bottom: 10px; }
.plan-role { font: 800 .9rem/1.2 var(--sans); color: #fff; letter-spacing: -.01em; }
.plan-n { color: var(--menthe); }
.plan-corps { display: grid; grid-template-columns: 34% minmax(0, 1fr); gap: 10px; }
.plan-menu { list-style: none; margin: 0; padding: 8px 0; display: grid; align-content: start; gap: 3px; border-right: 1px solid rgba(110, 231, 183, .25); }
.plan-menu li { padding: 3px 8px; border-radius: 6px; color: var(--nuit-txt-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plan-menu li:first-child { background: rgba(252, 211, 77, .9); color: var(--nuit); font-weight: 700; }
.plan-blocs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; align-content: start; padding: 8px 0; }
.plan-bloc { position: relative; padding: 9px 10px 10px; border-radius: 8px; border: 1px solid rgba(110, 231, 183, .3); background: rgba(255, 255, 255, .04); min-width: 0; transition: border-color .25s ease, background-color .25s ease, transform .25s ease; }
.plan-bloc:first-child { grid-column: 1 / -1; }
.plan-bloc b { display: block; color: #fff; font-weight: 600; margin-bottom: 7px; overflow-wrap: anywhere; }
.plan-bloc i { display: block; height: 4px; border-radius: 2px; background: rgba(110, 231, 183, .28); margin-top: 4px; width: 84%; }
.plan-bloc i + i { width: 58%; }
.plan-bloc.vise { border-color: var(--menthe); background: rgba(110, 231, 183, .16); transform: translateY(-2px); }
.plan-bloc.vise b { color: var(--menthe); }
.panneau .liste li[data-bloc] { cursor: default; border-radius: 8px; padding: 2px 6px; margin: -2px -6px; transition: background-color .25s ease; }
.panneau .liste li[data-bloc].vise { background: var(--accent-pale); }
@media (prefers-reduced-motion: no-preference) {
  .panneau.actif .plan-bloc { animation: plan-entre .5s cubic-bezier(.2, .7, .2, 1) both; animation-delay: calc(var(--k, 0) * 90ms); }
  @keyframes plan-entre { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
}
.plan-bloc:nth-child(2) { --k: 1; } .plan-bloc:nth-child(3) { --k: 2; } .plan-bloc:nth-child(4) { --k: 3; } .plan-bloc:nth-child(5) { --k: 4; }

/* ------------------------------------------ graphiques qui suivent (Prépa 600)
   Chaque panneau du produit porte un graphique tracé en SVG/CSS dont le
   remplissage suit le défilement : site.js pose --p (0 → 1) sur chaque
   [data-progres] quand il traverse la fenêtre. Sans script, --p vaut 1 et
   tout est dessiné. Le rapport est un EXEMPLE (dit comme tel) : un vrai
   rapport n'existe qu'après une épreuve. (Anthony, 06/09 : « des graphismes
   pro et évolutifs… quand on fait une action ça suit ».) */
.graphe { background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: clamp(16px, 2.4vw, 24px); min-width: 0; box-shadow: var(--ombre); }
.graphe svg { display: block; width: 100%; height: auto; }
.graphe text { font: 600 9.5px var(--mono); fill: var(--encre-2); }
.graphe text.grand { font: 800 26px var(--serif); fill: var(--encre); letter-spacing: -.03em; }
.graphe text.petit { font: 600 9px var(--mono); fill: var(--encre-2); letter-spacing: .08em; }
.graphe-leg { margin: 14px 0 0; font-size: .9rem; color: var(--encre-2); line-height: 1.5; }
.graphe-note { margin: 8px 0 0; font: 500 .72rem/1.5 var(--mono); color: var(--encre-2); }
/* le chronomètre */
.chrono .arc { fill: none; stroke-width: 12; stroke-linecap: butt; }
.chrono .arc.fond { stroke: var(--filet-2); }
.chrono .arc.trait { stroke: var(--accent); stroke-dasharray: var(--l); stroke-dashoffset: calc(var(--l) * (1 - clamp(0, var(--p, 1) * 6 - var(--i), 1))); }
/* le rapport */
.rapport-haut { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 16px; align-items: center; }
.jauge .fond { fill: none; stroke: var(--filet-2); stroke-width: 14; }
.jauge .trait { fill: none; stroke: var(--accent); stroke-width: 14; stroke-linecap: round; stroke-dasharray: var(--l); stroke-dashoffset: calc(var(--l) * (1 - var(--p, 1) * var(--v))); }
.notes { display: grid; gap: 6px; }
/* .nb et pas .note : .note est déjà la source en petit mono sous les textes */
.nb { display: grid; grid-template-columns: 44px minmax(0, 1fr) 36px; gap: 8px; align-items: center; font: 600 .68rem/1 var(--mono); color: var(--encre-2); }
.nb b { display: block; height: 8px; border-radius: 4px; background: var(--filet-2); overflow: hidden; }
.nb b i { display: block; height: 100%; width: 100%; background: var(--accent); transform-origin: 0 50%; transform: scaleX(calc(var(--p, 1) * var(--v))); border-radius: 4px; }
.nb em { font-style: normal; text-align: right; color: var(--encre); }
.carte-temps { margin-top: 16px; }
.cases { display: grid; grid-template-columns: repeat(15, minmax(0, 1fr)); gap: 3px; }
.cases i { display: block; aspect-ratio: 1; border-radius: 3px; background: var(--filet-2); cursor: crosshair; transition: transform .15s ease, opacity .3s ease; opacity: calc(clamp(0, var(--p, 1) * 90 - var(--q, 0), 1) * .85 + .15); }
.cases .c-j { background: var(--accent-vif); }
.cases .c-f { background: #F59E9B; }
.cases .c-v { background: #D1D9D5; }
.cases i.vise, .cases i:hover { transform: scale(1.35); outline: 2px solid var(--encre); z-index: 1; position: relative; }
/* .bulle-temps et pas .bulle : .bulle est la bulle de conversation de la méthode */
.bulle-temps { margin: 10px 0 0; font: 600 .74rem/1.4 var(--mono); color: var(--encre); min-height: 1.4em; }
.lg { display: inline-flex; align-items: center; gap: 6px; margin-right: 14px; }
.lg::before { content: ""; width: 10px; height: 10px; border-radius: 3px; background: var(--accent-vif); }
.lg-f::before { background: #F59E9B; }
.lg-v::before { background: #D1D9D5; }
/* les blancs */
.tuiles { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.tuile { position: relative; aspect-ratio: 1; border-radius: 10px; border: 1px solid var(--filet); background: var(--papier); display: grid; place-items: center; font: 700 .8rem/1 var(--mono); color: var(--encre-2); cursor: default; overflow: hidden; transition: transform .2s ease; }
.tuile::after { content: ""; position: absolute; inset: 0; background: var(--accent); opacity: calc(clamp(0, var(--p, 1) * var(--n) - var(--k), 1)); transition: opacity .3s ease; }
.tuile span { position: relative; z-index: 1; color: var(--encre); }
.tuile.ouverte span { color: #fff; }
.tuile.gratuit { border-color: var(--accent); }
.tuile.gratuit::after { background: var(--ambre); }
.tuile.gratuit span { color: var(--encre); }
.tuile:hover { transform: translateY(-3px); }
.prix-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; margin-top: 16px; font-size: .9rem; color: var(--encre-2); }
.prix-ligne b { font: 800 1.6rem/1 var(--serif); color: var(--encre); letter-spacing: -.03em; }
.mois { display: inline-flex; gap: 4px; }
.mois em { display: block; width: 26px; height: 10px; border-radius: 5px; background: var(--filet-2); overflow: hidden; position: relative; }
.mois em::after { content: ""; position: absolute; inset: 0; background: var(--accent); transform-origin: 0 50%; transform: scaleX(clamp(0, var(--p, 1) * 4 - var(--j, 0), 1)); }
.mois em:nth-child(2) { --j: 1; } .mois em:nth-child(3) { --j: 2; } .mois em:nth-child(4) { --j: 3; }
/* la banque */
.points { display: grid; grid-template-columns: repeat(16, minmax(0, 1fr)); gap: 5px; }
.points i { display: block; aspect-ratio: 1; border-radius: 50%; background: var(--filet-2); position: relative; }
.points i::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--accent-vif); transform: scale(clamp(0, var(--p, 1) * var(--n) - var(--k), 1)); }
.audit-titre { margin: 14px 0 12px; font-size: .95rem; color: var(--encre-2); }
.audit-titre b { font: 800 1.4rem/1 var(--serif); color: var(--encre); letter-spacing: -.03em; margin-right: .3em; }
.lettres { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px; align-items: end; height: 90px; }
.lettres div { display: grid; grid-template-rows: minmax(0, 1fr) auto; height: 100%; gap: 6px; text-align: center; font: 700 .72rem/1 var(--mono); color: var(--encre-2); }
.lettres b { display: block; position: relative; border-radius: 6px 6px 0 0; background: var(--filet-2); overflow: hidden; }
.lettres b i { position: absolute; left: 0; right: 0; bottom: 0; height: calc(var(--v) * 100% / .21); background: var(--accent); transform-origin: 50% 100%; transform: scaleY(clamp(0, var(--p, 1) * 5 - var(--i), 1)); }
/* le barème, au curseur */
.bareme-grille { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; }
@media (min-width: 900px) { .bareme-grille { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; } }
.bareme { background: var(--nuit); color: var(--nuit-txt); border-radius: var(--rayon); padding: clamp(18px, 2.4vw, 28px); }
.bareme-tete { display: flex; justify-content: space-between; align-items: baseline; font: 600 .74rem/1.3 var(--mono); letter-spacing: .06em; text-transform: uppercase; color: var(--nuit-txt-2); }
.bareme-tete output { font: 800 2.2rem/1 var(--serif); color: var(--menthe); letter-spacing: -.03em; text-transform: none; }
.bareme input[type="range"] { width: 100%; margin: 14px 0 12px; accent-color: var(--menthe); cursor: ew-resize; }
.bareme-barre { height: 10px; border-radius: 5px; background: var(--nuit-2); overflow: hidden; }
.bareme-barre i { display: block; height: 100%; background: linear-gradient(90deg, var(--menthe), var(--ambre)); transform-origin: 0 50%; transform: scaleX(var(--v)); transition: transform .15s ease-out; }
.bareme-res { margin: 14px 0 0; font-size: .95rem; line-height: 1.5; color: var(--nuit-txt); }
.bareme-res b { font: 800 1.5rem/1 var(--serif); color: var(--ambre); letter-spacing: -.03em; }
.bareme-grille .socle { grid-column: 1 / -1; }

/* la semaine type (méthode, étape 2) : sept jours, une mise en ligne chacun —
   à la place d'une frise de commits que personne hors métier ne lisait */
.semaine-jours { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.semaine-jours li { display: grid; justify-items: center; gap: 8px; padding: 12px 4px 10px; border-radius: 12px; background: var(--papier); border: 1px solid var(--filet); font: 700 .78rem/1 var(--mono); color: var(--encre-2); }
.semaine-jours i { width: 22px; height: 22px; border-radius: 50%; background: var(--accent); position: relative; }
.semaine-jours i::after { content: ""; position: absolute; left: 7px; top: 4px; width: 5px; height: 10px; border: solid #fff; border-width: 0 2.5px 2.5px 0; transform: rotate(45deg); }
.semaine-leg { margin: 14px 0 0; font-size: .92rem; color: var(--encre-2); line-height: 1.5; }
.semaine-leg b { color: var(--encre); }
@media (prefers-reduced-motion: no-preference) {
  .serp-etape.passe .semaine-jours i { animation: coche-pop .5s cubic-bezier(.2, .8, .2, 1.2) both; }
  .semaine-jours li:nth-child(2) i { animation-delay: .1s; } .semaine-jours li:nth-child(3) i { animation-delay: .2s; } .semaine-jours li:nth-child(4) i { animation-delay: .3s; }
  .semaine-jours li:nth-child(5) i { animation-delay: .4s; } .semaine-jours li:nth-child(6) i { animation-delay: .5s; } .semaine-jours li:nth-child(7) i { animation-delay: .6s; }
  @keyframes coche-pop { from { transform: scale(0); } to { transform: scale(1); } }
}

/* ------------------------------------------------------- les badges (Molière)
   « Le rôle du compte décide de ce qui s'ouvre » pris au mot : quatre badges
   d'accès, un lecteur, une porte. Passer un badge (survol ou touche) ouvre
   le panneau du rôle : sa promesse, les entrées de son menu qui s'allument
   l'une après l'autre, ce qu'il peut faire. Les espaces professeur et
   administration ne se photographient pas — ici rien n'est photographié,
   les quatre rôles sont traités pareil (Anthony, 06/09 : « je n'aime pas le
   design [des plans], change complètement l'idée »). */
.roles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 18px; }
@media (max-width: 720px) { .roles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } }
.badge { position: relative; display: grid; justify-items: center; gap: 6px; padding: 22px 12px 26px; border-radius: 16px; border: 1.5px solid var(--filet); background: var(--carte); color: var(--encre); font: inherit; cursor: pointer; overflow: hidden; text-align: center; transition: transform .3s cubic-bezier(.2, .7, .2, 1), border-color .3s ease, box-shadow .3s ease; }
.badge::before { content: ""; position: absolute; left: 50%; top: -30px; width: 3px; height: 34px; background: var(--filet); transform: translateX(-50%); }
.badge-trou { width: 26px; height: 7px; border-radius: 4px; background: var(--papier-2); border: 1px solid var(--filet); }
.badge-av { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; background: var(--c-f, var(--c)); color: #fff; font: 800 1.2rem/1 var(--serif); margin-top: 6px; }
.badge b { font: 800 1rem/1.2 var(--sans); letter-spacing: -.01em; }
/* « Administration » sortait de son badge à 320 px (145 px de texte pour
   130 px de case) et se faisait couper par l'`overflow: hidden` (07/09). */
@media (max-width: 380px) { .badge b { font-size: .82rem; } .badge { padding: 18px 8px 22px; } }
.badge-n { font: 600 .72rem/1.3 var(--mono); color: var(--accent-txt); }
.badge small { font: 500 .66rem/1.3 var(--mono); color: var(--encre-2); letter-spacing: .04em; text-transform: uppercase; }
.badge-piste { position: absolute; left: 0; right: 0; bottom: 0; height: 10px; background: var(--c); opacity: .85; }
.badge[aria-selected="true"] { border-color: var(--c); transform: translateY(-6px); box-shadow: 0 24px 40px -28px rgba(11, 46, 31, .55); }
.badge:hover { border-color: var(--c); }
.porte { position: relative; background: var(--carte); border: 1px solid var(--filet); border-radius: var(--rayon); padding: clamp(18px, 2.6vw, 30px); box-shadow: var(--ombre); }
.porte-lecteur { display: inline-flex; align-items: center; gap: 8px; font: 600 .68rem/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--encre-2); margin-bottom: 16px; }
.porte-lecteur .led { width: 10px; height: 10px; border-radius: 50%; background: var(--accent-vif); box-shadow: 0 0 0 3px var(--accent-pale); }
.porte-panneau { display: none; }
.porte-panneau.actif { display: block; }
.porte-promesse { margin: 0 0 18px; font: 800 clamp(1.2rem, 2vw, 1.55rem)/1.25 var(--serif); letter-spacing: -.02em; color: var(--encre); max-width: 34ch; }
.porte-titre { margin: 0 0 10px; font: 600 .72rem/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); }
.pastilles { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.pastilles li { padding: 7px 12px; border-radius: 999px; background: var(--papier); border: 1px solid var(--filet); font: 600 .82rem/1 var(--sans); color: var(--encre); }
.pastilles li:first-child { background: var(--c-f, var(--c)); color: #fff; border-color: var(--c-f, var(--c)); }
/* `--c` est la couleur du rôle, faite pour les aplats ; `--c-f` en est la
   version foncée, la seule qui porte du texte blanc. Blanc sur #0EA5E9 vaut
   3,0 : 1, sur #10B981 2,3 et sur #F59E0B 2,1 — il en faut 4,5 (07/09). */
.liste-porte { gap: 8px; font-size: .95rem; }
@media (prefers-reduced-motion: no-preference) {
  .porte-panneau.actif .pastilles li { animation: pastille-pop .45s cubic-bezier(.2, .8, .2, 1.1) both; animation-delay: calc(var(--k, 0) * 60ms); }
  @keyframes pastille-pop { from { opacity: 0; transform: translateY(8px) scale(.9); } to { opacity: 1; transform: none; } }
  .porte-lecteur .led { animation: led 1.4s ease-out infinite; }
  @keyframes led { 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, .6); } 100% { box-shadow: 0 0 0 9px rgba(16, 185, 129, 0); } }
  .porte.change .led { animation: led-lecture .6s ease-out 1; }
  @keyframes led-lecture { 0%, 40% { background: var(--ambre); } 100% { background: var(--accent-vif); } }
}

/* ------------------------------------------------- Prépa 600 : une autre page
   Rien de commun avec la page Molière (Anthony, 06/09 : « elle ne doit rien
   avoir à voir »). La page porte les couleurs du produit — bleu et nuit
   bleue — en redéfinissant les jetons sur <body> ; en-tête et pied suivent.
   Contrastes : encre #0B1F3A sur #EEF4FB 15:1 ; #1E40AF sur #EEF4FB 7,4:1 ;
   #93C5FD sur #0B1F3A 9,4:1. La composition est un RÉCIT : un téléphone
   collé à gauche dont l'écran change pendant que les six jours défilent à
   droite, puis quatre graphiques qui se remplissent au défilement, le
   curseur du barème, et un bandeau de logos qui défile. */
body.page-prepa-600 { --papier: #EEF4FB; --papier-2: #E2ECF8; --encre: #0B1F3A; --encre-2: #3B5273; --filet: #C3D6EE; --filet-2: #D8E5F5; --accent: #1D4ED8; --accent-txt: #1E40AF; --accent-vif: #3B82F6; --accent-pale: #DBEAFE; --vert: #1D4ED8; --vert-pale: #DBEAFE; --nuit: #0B1F3A; --nuit-2: #17325A; --nuit-txt: #EAF2FF; --nuit-txt-2: #9DB8E0; --menthe: #93C5FD; --ombre: 0 30px 60px -36px rgba(11, 31, 58, .5); }
.page-prepa-600 .ordi-barre { background: #D6E4F5; }
.page-prepa-600 .btn-p:hover { background: var(--accent); }
.hero-p600 .hero-grid { align-items: center; }
.six { position: relative; display: grid; gap: 14px; justify-items: start; padding: 26px 28px; border-radius: 22px; background: var(--nuit); color: var(--nuit-txt); overflow: hidden;
  background-image: linear-gradient(rgba(147, 197, 253, .08) 1px, transparent 1px), linear-gradient(90deg, rgba(147, 197, 253, .08) 1px, transparent 1px); background-size: 28px 28px; }
.six-chiffre { display: flex; align-items: baseline; gap: 12px; }
.six-chiffre b { font: 800 clamp(5.5rem, 12vw, 9.5rem)/.9 var(--serif); letter-spacing: -.06em; color: var(--menthe); }
.six-chiffre span { font: 700 1.3rem/1 var(--sans); color: #fff; }
.six-faits { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; width: 100%; }
.six-faits li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: baseline; border-top: 1px solid var(--nuit-2); padding-top: 8px; font-size: .92rem; color: var(--nuit-txt-2); }
.six-faits time, .six-faits b { font: 700 .82rem/1.3 var(--mono); color: var(--menthe); letter-spacing: .02em; }
/* le récit */
.recit-grille { display: grid; grid-template-columns: minmax(0, 1fr); gap: 28px; }
@media (min-width: 900px) { .recit-grille { grid-template-columns: 320px minmax(0, 1fr); gap: 60px; align-items: start; } }
.recit-colle { position: sticky; top: 84px; display: grid; justify-items: center; gap: 12px; z-index: 1; }
@media (max-width: 899px) {
  .recit-colle { position: static; }
  .recit-colle .tel-grand { width: min(100%, 220px); }
  [data-recit].eclate .recit-colle { display: none; }
}
/* l'écran d'une étape, sur téléphone : le même cadre, posé sous le texte */
.recit-etape-tel { display: none; }
@media (max-width: 899px) {
  .recit-etape-tel { display: flex; justify-content: center; margin: 18px 0 0; }
  .recit-etape-tel .tel { width: min(100%, 230px); }
  .recit-etape-tel .tel-ecran { border-radius: 20px; }
  .recit-etape-tel img { position: absolute; left: 0; top: 0; width: 100%; height: auto; }
}
.recit-tel img { position: absolute; left: 0; top: 0; width: 100%; height: auto; opacity: 0; transition: opacity .7s ease; }
.recit-tel img.actif { opacity: 1; }
.recit-leg { margin: 0; font: 600 .72rem/1.4 var(--mono); color: var(--encre-2); letter-spacing: .02em; text-align: center; }
.recit-etapes { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(60px, 10vh, 120px); }
.recit-etape { position: relative; padding-left: 28px; border-left: 2px solid var(--filet); min-height: 42vh; transition: opacity .5s ease; }
.recit-etape::before { content: ""; position: absolute; left: -9px; top: 4px; width: 16px; height: 16px; border-radius: 50%; background: var(--carte); border: 2px solid var(--filet); transition: background-color .4s ease, border-color .4s ease, box-shadow .4s ease; }
.recit-etape.actif { border-left-color: var(--accent); }
.recit-etape.actif::before { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 0 6px var(--accent-pale); }
.recit-etape:not(.actif) { opacity: .45; }
.recit-etape time { display: block; font: 700 .74rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); margin-bottom: 8px; }
.recit-etape h3, .recit-etape .recit-h { font: 800 clamp(1.5rem, 2.6vw, 2.1rem)/1.15 var(--serif); letter-spacing: -.03em; margin: 0 0 10px; }
.recit-etape p { margin: 0 0 12px; font-size: 1.08rem; line-height: 1.55; color: var(--encre-2); max-width: 52ch; }
/* La pastille est un <p> DANS une étape : `.recit-etape p` (0,1,1) battait
   `.recit-fait` (0,1,0) et lui imposait l'encre grise du corps de texte — sur
   un fond nuit, ça donnait 2,08 : 1 là où il en faut 4,5. Une règle plus
   précise lui rend sa couleur claire (07/09). */
.recit-etape p.recit-fait { color: var(--nuit-txt); }
.recit-etape p b { color: var(--encre); }
.recit-fait { display: inline-flex; align-items: baseline; gap: 8px; padding: 8px 14px; border-radius: 999px; background: var(--nuit); color: var(--nuit-txt); font: 600 .82rem/1 var(--mono); }
/* .recit-etape p b encre le gras des paragraphes : le chiffre de la pastille doit rester clair sur nuit */
.recit-etape .recit-fait b { font: 800 1.3rem/1 var(--serif); color: var(--menthe); letter-spacing: -.03em; white-space: nowrap; }   /* « 0 € » se cassait en deux lignes à 331 px (06/09) */
.recit-fait { max-width: 100%; }   /* le texte de la pastille passe sur deux lignes à côté du chiffre, sans casser la boîte */
@media (max-width: 899px) { .recit-etape { min-height: 0; padding-left: 22px; } .recit-etape:not(.actif) { opacity: 1; } }
/* les quatre graphiques en grille */
.graphes-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); gap: 22px; }
/* Le bas des cadres aligné (07/09) : chaque carte occupe toute la hauteur de
   sa rangée, et le graphique prend la place qui reste sous un titre d'une ou
   deux lignes. Sans ça, deux cartes voisines finissaient à des hauteurs
   différentes. */
.graphes-grille article { min-width: 0; display: grid; grid-template-rows: auto auto 1fr; align-content: start; }
.graphes-grille article > .graphe { height: 100%; }
.graphes-grille article > p { margin: 0 0 14px; color: var(--encre-2); font-size: .97rem; }
/* le bandeau de logos qui défile */
.sec-defilant { border-top: 1px solid var(--filet); padding: 26px 0; overflow: hidden; }
.defilant { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.defilant-piste { list-style: none; margin: 0; padding: 0; display: flex; gap: 46px; width: max-content; }
.defilant-piste li { display: inline-flex; align-items: center; gap: 10px; font: 700 .9rem/1 var(--mono); color: var(--encre); white-space: nowrap; }
.defilant-piste svg { width: 26px; height: 26px; fill: var(--marque, var(--encre)); }
@media (prefers-reduced-motion: no-preference) {
  .defilant-piste { animation: defilant 34s linear infinite; }
  .defilant:hover .defilant-piste { animation-play-state: paused; }
  @keyframes defilant { from { transform: translateX(0); } to { transform: translateX(-50%); } }
}
@media (prefers-reduced-motion: reduce) { .defilant-piste { flex-wrap: wrap; width: auto; justify-content: center; gap: 18px 34px; } .defilant { mask-image: none; -webkit-mask-image: none; } }

/* ------------------------------------------------------------------------
   Le point de départ (page Molière, lot 19) — un double panneau : à gauche
   le site verrouillé (squelette gris, cadenas), à droite le vrai site en
   ligne ; entre les deux, le nombre de jours. Tout se pose au défilement
   avec --p (0 → 1, posé par site.js sur [data-progres]) : les panneaux
   glissent l'un vers l'autre, le trait se tend, le chiffre se pose, les
   quatre dates s'allument l'une après l'autre. Sans JS, --p vaut 1 : tout
   est visible (règle : jamais une visibilité qui dépend d'une transition). */
/* Les deux panneaux s'alignent par le HAUT. Centrés, ils se décalaient dès
   que leurs textes n'avaient pas la même hauteur : 0 px en français, mais
   14 px en anglais — dates, barres et écrans ne tombaient plus au même
   niveau (mesuré le 08/09). Le compteur du milieu reste centré. */
.depart { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: clamp(18px, 3vw, 44px); align-items: start; }
.depart-entre { align-self: center; }
.depart-panneau { min-width: 0; }
.depart-avant { transform: translateX(calc((1 - clamp(0, var(--p, 1) * 1.6, 1)) * -28px)); opacity: clamp(.12, var(--p, 1) * 1.6, 1); }
.depart-apres { transform: translateX(calc((1 - clamp(0, var(--p, 1) * 1.6 - .3, 1)) * 28px)); opacity: clamp(.12, var(--p, 1) * 1.6 - .3, 1); }
.depart-date { margin: 0 0 12px; font: 700 .74rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); }
.depart-panneau h3 { font: 800 clamp(1.25rem, 2vw, 1.6rem)/1.2 var(--serif); letter-spacing: -.03em; margin: 18px 0 8px; }
.depart-panneau p:not(.depart-date) { margin: 0; color: var(--encre-2); font-size: 1.04rem; line-height: 1.55; }
.depart-panneau .ordi-ecran { aspect-ratio: 16 / 10; }
/* le site verrouillé */
.depart-bloque .ordi-barre { background: #D9DEDB; border-bottom-color: rgba(11, 46, 31, .12); }
.depart-bloque .ordi-barre i { background: #B8C0BC !important; }
.depart-bloque .ordi-barre .url { color: #6B7772; }
.depart-ecran { background: #F1F4F2; }
.depart-squelette { position: absolute; inset: 14px 16px; display: grid; gap: 10px; align-content: start; }
.depart-squelette span { display: block; height: 10px; width: var(--w, 50%); border-radius: 5px; background: #CDD6D1; }
.depart-squelette span.bloc { height: 34%; width: 100%; border-radius: 8px; }
.depart-cadenas { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 10px; background: rgba(11, 46, 31, .58); color: #fff; }
.depart-cadenas svg { width: 36px; height: 36px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.depart-cadenas b { font: 700 .7rem/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; padding: 7px 11px; border: 1px solid rgba(255, 255, 255, .55); border-radius: 999px; }
@media (prefers-reduced-motion: no-preference) {
  .depart-cadenas svg { animation: cadenas 3.6s ease-in-out infinite; }
  @keyframes cadenas { 0%, 86%, 100% { transform: none; } 89% { transform: translateX(-2px) rotate(-4deg); } 92% { transform: translateX(2px) rotate(4deg); } 95% { transform: translateX(-1px) rotate(-2deg); } }
}
/* entre les deux : le trait qui se tend, le nombre de jours qui se pose */
.depart-entre { display: grid; justify-items: center; gap: 6px; min-width: 104px; }
.depart-trait { display: block; width: 100%; height: 2px; border-radius: 1px; background: var(--filet); position: relative; overflow: hidden; }
.depart-trait i { position: absolute; inset: 0; background: var(--accent); transform-origin: 0 50%; transform: scaleX(clamp(0, var(--p, 1) * 2.2 - .7, 1)); }
.depart-n { font: 800 clamp(3.2rem, 6vw, 4.8rem)/1 var(--serif); letter-spacing: -.05em; color: var(--accent); transform: scale(clamp(.6, var(--p, 1) * 2.2 - .7, 1)); opacity: clamp(0, var(--p, 1) * 2.2 - .7, 1); }
.depart-u { font: 700 .78rem/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--encre-2); }
/* la frise : quatre dates sur un fil, chaque point s'allume à son tour */
.depart-frise { list-style: none; margin: clamp(30px, 4vw, 48px) 0 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; position: relative; }
.depart-frise::before { content: ""; position: absolute; left: 0; right: 0; top: 7px; height: 2px; background: var(--filet); }
.depart-frise li { position: relative; padding-top: 24px; min-width: 0; }
.depart-frise li::before { content: ""; position: absolute; left: 0; top: 0; width: 16px; height: 16px; border-radius: 50%; background: var(--accent); border: 3px solid var(--papier-2); transform: scale(clamp(0, (var(--p, 1) - .42 - var(--k, 0) * .15) / .08, 1)); }
.depart-frise time { display: block; font: 700 .7rem/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent-txt); margin-bottom: 4px; }
.depart-frise span { display: block; font-size: .95rem; color: var(--encre-2); line-height: 1.4; }
.depart-frise span b { color: var(--encre); }
@media (max-width: 760px) {
  .depart { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .depart-entre { grid-auto-flow: column; align-items: center; justify-content: start; gap: 14px; }
  .depart-trait { width: 56px; }
  .depart-avant, .depart-apres { transform: none; }
}
@media (max-width: 640px) {
  .depart-frise { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 14px; }
  .depart-frise::before { display: none; }
}

/* la piste d'une vitrine qui déroule : les tuiles d'une page entière (nom-1,
   nom-2…, rafraichir.py) empilées, et c'est la piste que site.js translate.
   Le navigateur ne décode que les tuiles visibles : plus d'écran blanc. */
.defile-piste { position: absolute; left: 0; top: 0; width: 100%; }
.defile-piste img { position: static; display: block; width: 100%; height: auto; }

/* ------------------------------------------------------ le formulaire de contact
   Les six chemins de contact du site étaient des `mailto:` : sur un téléphone
   sans application de courrier configurée, le clic ne fait rien et personne ne
   le sait. Le formulaire, lui, part toujours. Champs à 48 px de haut (la règle
   des 44 px du bout du doigt), contour de focus visible, piège à robots hors
   de l'écran mais lisible par un lecteur d'écran qui l'ignorera. */
.form { display: grid; gap: 15px; }
.champ { display: grid; gap: 6px; margin: 0; }
.champ label { font: 600 .86rem/1.35 var(--sans); color: var(--encre-2); }
.champ input, .champ textarea {
  font: 400 1rem/1.5 var(--sans); color: var(--encre); background: var(--papier);
  border: 1.5px solid var(--filet); border-radius: 12px; padding: 12px 14px;
  width: 100%; min-height: 48px; }
.champ textarea { min-height: 118px; resize: vertical; }
.champ input:hover, .champ textarea:hover { border-color: var(--encre-2); }
.champ input:focus-visible, .champ textarea:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.champ.piege { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form .btn { width: 100%; }
.form .petit { margin: 0; }
.form-ou { margin-top: 16px; }
