/* ============================================================================
   Système de design : d'après Duolingo (refero.design/style/7088d695), l'orange
   mangue à la place du vert. Un seul fichier, les tokens en tête.

   Les trois règles qui font le style :
   1. Canevas blanc. La couleur n'apparaît que sur les aplats et les titres.
   2. Tout est un autocollant : bordure 2px, tranche basse 4px, coins arrondis.
   3. Le corps de texte reste gris. Il ne prend jamais la couleur de marque.
   ========================================================================= */

/* ── Polices ──────────────────────────────────────────────────────────────
   Feather → Nunito 900. duolingo-sans → Nunito Sans. Variables, latin,
   auto-hébergées : pas de CDN, rien à déclarer au RGPD. */
@font-face {
  font-family: 'Nunito';
  src: url('fonts/nunito.woff2') format('woff2-variations');
  font-weight: 400 1000;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Nunito Sans';
  src: url('fonts/nunito-sans.woff2') format('woff2-variations');
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Couleurs : mangue remplace Eager Green #58cc02 */
  --mangue: #ff9600;         /* aplats : CTA, bandeau de pied, bulles */
  --mangue-bord: #d97706;    /* tranche 3D des boutons, survol */
  --mangue-titre: #c26a05;   /* titres ≥ 24px          : ≥ 3,4:1 sur les 4 fonds */
  --mangue-texte: #9a4a08;   /* marque en petit corps  : ≥ 5,4:1 sur les 4 fonds */
  --mangue-douce: #ffedd5;   /* lavis de fond */
  --mangue-claire: #ffc46b;  /* accent clair sur surface sombre */

  --ciel: #1cb0f6;           /* aplat secondaire */
  --ciel-bord: #1899d6;
  --ciel-lien: #0f72ae;      /* liens en ligne         : 5,20:1 sur blanc */
  --ciel-lavis: #e3f4fe;     /* fond du bandeau immersif */

  --nuit: #000437;           /* encre sur aplat mangue : 8,96:1 */
  --nuit-fond: #0b1033;      /* le fond de la bande de rupture */
  --blanc: #ffffff;
  --charbon: #4b4b4b;        /* titres, texte fort     : 8,72:1 */
  --crayon: #6b6b6b;         /* paragraphes            : ≥ 4,6:1 sur les 4 fonds */
  --brume: #afafaf;          /* bordures de boutons fantômes */
  --trait: #e5e5e5;          /* filets, bordures de cartes */
  --ombre-trait: #d4d4d4;    /* la tranche basse, plus foncée que le contour */

  /* Typographie */
  --display: 'Nunito', ui-rounded, 'Segoe UI', system-ui, sans-serif;
  --sans: 'Nunito Sans', ui-sans-serif, system-ui, 'Segoe UI', sans-serif;

  --t-legende: 0.8125rem;  /* 13 */
  --t-nav: 0.9375rem;      /* 15 */
  --t-corps: 1.0625rem;    /* 17 */
  --t-inter: 1.1875rem;    /* 19 */
  --t-titre-sm: 2rem;      /* 32 */
  --t-titre: 3rem;         /* 48 */
  --t-display: 4rem;       /* 64 */

  /* Espacement : base 4 */
  --e8: 8px;   --e12: 12px; --e16: 16px; --e24: 24px; --e32: 32px;
  --e40: 40px; --e48: 48px; --e64: 64px; --e80: 80px; --e96: 96px;

  --largeur: 1200px;
  --gouttiere: clamp(24px, 5vw, 48px);

  /* Formes */
  --r: 12px;      /* pastilles, tags, nav : la valeur de la référence */
  --r-lg: 16px;   /* boutons chunky, cartes */
  --r-xl: 24px;   /* grands blocs */
  --tranche: 4px; /* l'épaisseur qui fait l'autocollant */
}

/* ── Base ────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

/* clip et non hidden : `overflow: hidden` sur html/body ferait du corps un
   conteneur de défilement, ce qui casse `position: sticky` de la barre. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--blanc);
  color: var(--crayon);
  font: 500 var(--t-corps)/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, ol, dl, dd, figure, table { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { max-width: 100%; height: auto; display: block; }
table { border-collapse: collapse; width: 100%; }

a { color: var(--ciel-lien); }

:focus-visible {
  outline: 3px solid var(--ciel);
  outline-offset: 3px;
  border-radius: 4px;
}

.sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.saut {
  position: absolute; left: 50%; top: 8px; transform: translate(-50%, -200%);
  z-index: 100; background: var(--nuit); color: var(--blanc);
  padding: 12px 20px; border-radius: var(--r); font-weight: 800;
  text-decoration: none;
}
.saut:focus { transform: translate(-50%, 0); }

/* ── Typographie ─────────────────────────────────────────────────────── */
.display, .titre {
  font-family: var(--display);
  font-weight: 900;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--mangue-titre);
  text-wrap: balance;
}
.display { font-size: clamp(2.25rem, 6vw, var(--t-display)); }
.titre   { font-size: clamp(2rem, 4.6vw, var(--t-titre)); }

.titre--charbon { color: var(--charbon); }
.titre--nuit { color: var(--nuit); }

/* Le titre du hero reste neutre : la couleur est portée par le bouton en dessous. */
.hero__titre {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(1.75rem, 4vw, var(--t-titre-sm));
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--charbon);
  text-wrap: balance;
}.prose { max-width: 30rem; }
.prose p + p { margin-top: var(--e16); }

.surtitre {
  font-family: var(--sans);
  font-weight: 800;
  font-size: var(--t-nav);
  line-height: 1.33;
  letter-spacing: 0.053em;
  text-transform: uppercase;
  color: var(--mangue-texte);
}

/* --brume est une couleur de bordure : en texte elle tombe à 2,2:1. */
.legende { font-size: var(--t-legende); font-weight: 700; color: var(--crayon); }

/* ── Boutons autocollant ─────────────────────────────────────────────────
   Le geste signature : une tranche pleine de 4px sous le bouton, qui
   disparaît au clic pendant que le bouton descend de 4px. Ombre dure, sans
   flou : ce n'est pas une élévation, c'est l'épaisseur d'un autocollant. */
.btn {
  --tranche-c: var(--mangue-bord);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e8);
  min-height: 52px;
  padding: 0 var(--e24);
  border: 0;
  border-radius: var(--r-lg);
  background: var(--mangue);
  color: var(--nuit);
  font: 800 var(--t-nav)/1 var(--sans);
  letter-spacing: 0.053em;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  box-shadow: 0 var(--tranche) 0 var(--tranche-c);
  transition: transform .08s ease, box-shadow .08s ease, background-color .12s ease;
}
.btn:hover { background: var(--mangue-bord); --tranche-c: #b45309; }
.btn:active {
  transform: translateY(var(--tranche));
  box-shadow: 0 0 0 var(--tranche-c);
}

.btn--ciel { background: var(--ciel); --tranche-c: var(--ciel-bord); }
.btn--ciel:hover { background: var(--ciel-bord); --tranche-c: #0f72ae; }

/* Le bouton fantôme est un citoyen de première classe, pas un repli. */
.btn--fantome {
  background: var(--blanc);
  color: var(--ciel-lien);
  border: 2px solid var(--trait);
  --tranche-c: var(--ombre-trait);
}
.btn--fantome:hover { background: #f7f7f7; --tranche-c: var(--ombre-trait); }.btn--grand { min-height: 60px; font-size: var(--t-corps); padding: 0 var(--e32); }

/* ── Pastilles et tags ───────────────────────────────────────────────── */
.pastille {
  display: inline-flex;
  align-items: center;
  gap: var(--e8);
  min-height: 44px;
  padding: 10px var(--e16);
  border: 2px solid var(--trait);
  border-radius: var(--r);
  background: var(--blanc);
  color: var(--crayon);
  font: 800 var(--t-nav)/1.33 var(--sans);
  letter-spacing: 0.053em;
  text-transform: uppercase;
  text-decoration: none;
  transition: border-color .12s ease, color .12s ease;
}
.pastille:hover { border-color: var(--brume); color: var(--charbon); }
.pastille--active { border-color: var(--mangue); color: var(--mangue-texte); }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px var(--e16);
  border-radius: var(--r);
  background: var(--mangue-douce);
  color: var(--mangue-texte);
  font: 800 var(--t-legende)/1.23 var(--sans);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.tag--ciel { background: #e0f2fe; color: var(--ciel-lien); }
.tag--vide {
  background: transparent;
  border: 2px dashed var(--brume);
  color: var(--crayon);
}

.tags { display: flex; flex-wrap: wrap; gap: var(--e8); }

/* ── Mise en page ────────────────────────────────────────────────────── */
.enveloppe {
  width: 100%;
  max-width: var(--largeur);
  margin-inline: auto;
  padding-inline: var(--gouttiere);
}

.section { padding-block: clamp(40px, 7vw, var(--e80)); }

/* Une ancre doit poser le haut de la bande SOUS la barre collante, pas
   derriere elle. Mesure du 2026-09-15 : sans cette regle, cliquer « Nos
   prestations » dans la nav cache le surtitre de la section sous les 72 px
   de la barre. La valeur suit la hauteur reelle de .barre__inner (72 px,
   64 px sous 720 px) plus une respiration. */
section[id] { scroll-margin-top: 84px; }
.section--douce { background: #fff7ed; }

/* Le rythme de la référence : texte à gauche, illustration à droite,
   inversé à la section suivante, sur blanc, sans conteneur. */
.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(32px, 6vw, var(--e80));
}
.duo--inverse .duo__texte { order: 2; }
.duo__illus { display: grid; place-items: center; }

.pile { display: flex; flex-direction: column; align-items: flex-start; gap: var(--e24); }
.rangee { display: flex; flex-wrap: wrap; gap: var(--e12); }

.entete-section { margin-bottom: clamp(28px, 4vw, var(--e40)); }
.entete-section .surtitre { display: block; margin-bottom: var(--e12); }
.entete-section .prose { margin-top: var(--e16); }

.centre { text-align: center; }
.centre .prose { margin-inline: auto; }
.centre .rangee { justify-content: center; }

/* ── Barre de navigation ─────────────────────────────────────────────── */
.barre {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--blanc);
  border-bottom: 2px solid var(--trait);
}
.barre__inner {
  display: flex;
  align-items: center;
  gap: var(--e24);
  min-height: 72px;
  max-width: var(--largeur);
  margin-inline: auto;
  padding-inline: var(--gouttiere);
}
.marque {
  display: inline-flex;
  align-items: center;
  gap: var(--e12);
  text-decoration: none;
  color: var(--mangue-titre);
  font: 900 1.375rem/1 var(--display);
  letter-spacing: -0.02em;
  margin-right: auto;
}
.marque svg { width: 40px; height: 40px; flex: none; }

.nav { display: flex; gap: var(--e8); }
.nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--e12);
  border-radius: var(--r);
  color: var(--crayon);
  font: 800 var(--t-nav)/1.33 var(--sans);
  letter-spacing: 0.053em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color .12s ease, color .12s ease;
}
.nav a:hover { background: var(--mangue-douce); color: var(--mangue-texte); }

/* ── Hero ────────────────────────────────────────────────────────────── */
.hero { padding-block: clamp(40px, 7vw, var(--e80)); }
.hero__actions { display: flex; flex-direction: column; gap: var(--e12); width: min(100%, 340px); }
.hero .btn { width: 100%; }

/* ── Bandeau de statut / réseaux ─────────────────────────────────────────
   L'équivalent de la bande de drapeaux de la référence : une rangée de
   pastilles à plat, sans conteneur, entre deux filets. */
.bande {
  border-block: 2px solid var(--trait);
  padding-block: var(--e24);
}
.bande__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--e12) var(--e24);
  max-width: var(--largeur);
  margin-inline: auto;
  padding-inline: var(--gouttiere);
}

.statut {
  display: inline-flex;
  align-items: center;
  gap: var(--e8);
  padding: 10px var(--e16);
  border-radius: var(--r);
  background: var(--mangue-douce);
  color: var(--mangue-texte);
  font: 800 var(--t-nav)/1.33 var(--sans);
  text-decoration: none;
}
.statut::before {
  content: '';
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--brume); flex: none;
}
.statut[data-ouvert='oui'] { background: #dcfce7; color: #15803d; }
.statut[data-ouvert='oui']::before { background: #22c55e; }
.statut[data-ouvert='non'] { background: #fee2e2; color: #b91c1c; }
.statut[data-ouvert='non']::before { background: #ef4444; }

/* ── Cartes autocollant ──────────────────────────────────────────────── */
.grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--e24);
}

.carte {
  background: var(--blanc);
  border: 2px solid var(--trait);
  border-bottom: var(--tranche) solid var(--ombre-trait);
  border-radius: var(--r-lg);
  padding: var(--e24);
  display: flex;
  flex-direction: column;
  gap: var(--e12);
  transition: transform .12s ease, border-color .12s ease;
}
.carte:hover { transform: translateY(-2px); border-color: var(--brume); }
.carte__icone {
  width: 56px; height: 56px;
  display: grid; place-items: center;
  border-radius: var(--r);
  background: var(--mangue-douce);
  color: var(--mangue-texte);
  flex: none;
}
.carte__icone svg { width: 30px; height: 30px; }
.carte__icone--ciel { background: #e0f2fe; color: var(--ciel-lien); }
.carte h3 { font: 800 var(--t-inter)/1.4 var(--sans); color: var(--charbon); }
.carte p { font-size: var(--t-corps); }

/* ── Le parcours ─────────────────────────────────────────────────────────
   La file de bulles de Duolingo, en zigzag. Chaque bulle a la même tranche
   que les boutons : c'est le même objet, en rond. */
.chemin { position: relative; display: grid; gap: var(--e24); justify-items: center; }
.chemin::before {
  content: '';
  display: none;
  position: absolute;
  top: 40px; bottom: 40px; left: 50%;
  width: 4px; margin-left: -2px;
  background: repeating-linear-gradient(
    to bottom, var(--trait) 0 12px, transparent 12px 24px);
}
.etape {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--e12);
  text-align: center;
  max-width: 22rem;
}
.etape:nth-child(odd)  { transform: translateX(clamp(-140px, -12vw, -40px)); }
.etape:nth-child(even) { transform: translateX(clamp(40px, 12vw, 140px)); }

/* Chaque bulle envoie une onde quand elle s'allume, et la suivante s'allume
   dans la foulée : le relais se voit, au lieu de quatre pulsations parallèles. */
.etape__bulle { position: relative; }
.etape__bulle::after {
  content: '';
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 4px solid var(--mangue);
  transform-origin: center;
  animation: onde var(--rythme, 6s) cubic-bezier(.2,.7,.3,1) var(--tempo, 0s) infinite;
}
.etape:nth-child(4n+2) .etape__bulle::after { border-color: var(--ciel); }

.etape__bulle {
  width: 92px; height: 92px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--mangue);
  color: var(--nuit);
  box-shadow: 0 8px 0 var(--mangue-bord);
  font: 900 1.75rem/1 var(--display);
}
.etape__bulle svg { width: 42px; height: 42px; }
.etape:nth-child(4n+2) .etape__bulle { background: var(--ciel); box-shadow: 0 8px 0 var(--ciel-bord); }
.etape__num {
  font: 800 var(--t-legende)/1 var(--sans);
  letter-spacing: 0.053em; text-transform: uppercase; color: var(--crayon);
}
.etape h3 { font: 800 var(--t-inter)/1.4 var(--sans); color: var(--charbon); }

/* ── Avis ────────────────────────────────────────────────────────────── */
.note {
  display: inline-flex;
  align-items: center;
  gap: var(--e16);
  padding: var(--e16) var(--e24);
  border-radius: var(--r-lg);
  background: var(--nuit);
  color: var(--blanc);
}
.note__frimousse { width: 46px; height: 46px; flex: none; }
.note__chiffre { font: 900 2.5rem/1 var(--display); color: var(--mangue-claire); }
.note__detail { text-align: left; font-size: var(--t-legende); font-weight: 700; color: #c7cbe0; }
.note__detail strong { display: block; font-size: var(--t-nav); color: var(--blanc); }

.etoiles { display: inline-flex; gap: 2px; color: var(--mangue); }
.etoiles svg { width: 18px; height: 18px; }

/* ── Horaires ────────────────────────────────────────────────────────── */
/* Sur un écran très étroit le tableau défile dans sa carte, jamais la page. */
.tbl-boite { overflow-x: auto; margin-inline: calc(var(--e24) * -1); padding-inline: var(--e24); }
.tbl { min-width: 17rem; }

.tbl th, .tbl td {
  padding: 12px var(--e12);
  text-align: left;
  font-size: var(--t-corps);
  border-bottom: 2px solid var(--trait);
}
.tbl tr:last-child th, .tbl tr:last-child td { border-bottom: 0; }
.tbl th { font-weight: 800; color: var(--charbon); width: 42%; }
.tbl td { text-align: right; font-variant-numeric: tabular-nums; }
.tbl tr[data-today] th, .tbl tr[data-today] td { color: var(--mangue-texte); font-weight: 800; }
.tbl tr[data-today] th { border-radius: var(--r) 0 0 var(--r); }
.tbl tr[data-today] td { border-radius: 0 var(--r) var(--r) 0; }
.tbl tr[data-today] { background: var(--mangue-douce); }

/* ── Venir ───────────────────────────────────────────────────────────── */
.infos { display: grid; gap: var(--e24); grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }
.info__label {
  display: block;
  font: 800 var(--t-legende)/1.23 var(--sans);
  letter-spacing: 0.053em; text-transform: uppercase; color: var(--crayon);
  margin-bottom: 6px;
}
.info__valeur { font-size: var(--t-corps); color: var(--charbon); font-weight: 700; }

.carte-plan {
  width: 100%;
  border: 2px solid var(--trait);
  border-bottom: var(--tranche) solid var(--ombre-trait);
  border-radius: var(--r-lg);
  background: var(--mangue-douce);
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.carte-plan svg { width: 100%; height: 100%; }

/* ── Contact ─────────────────────────────────────────────────────────── */
/* Aplat coloré : le titre y prend l'encre, comme sur le bandeau mangue. */
.contact .titre { color: var(--nuit); }
.contact {
  background: var(--mangue-douce);
  border-radius: var(--r-xl);
  padding: clamp(32px, 6vw, var(--e64));
  text-align: center;
}
/* .prose est plafonne a 30rem et seul .centre .prose recoit des marges auto.
   Le bloc contact n'est pas .centre : sans cette ligne sa colonne de 30rem
   reste collee a gauche d'une boite centree, texte centre dedans, ce qui se
   lit comme un decentrage. */
.contact .prose { margin-inline: auto; }

/* ── Pied : le bandeau plein cadre ───────────────────────────────────── */
.pied {
  position: relative;
  overflow: hidden;
  background: var(--mangue);
  color: var(--nuit);
  /* Le padding haut n'est pas du vide : la frise est en position absolue
     de 16 px a 86 px, donc 96 px est le minimum pour qu'elle ne passe pas
     sous le contenu. Mesure du 2026-09-15 : a 64 px elles se chevauchent
     de 22 px. Ce qui etait du vide, c'est la marge haute de 64 px : elle
     s'ajoutait aux 96 px de padding, soit 160 px avant le premier mot. */
  padding-block: var(--e96) var(--e48);
  margin-top: 0;
}
.pied__frise {
  position: absolute;
  top: var(--e16);
  left: 0;
  right: 0;
  height: 70px;
  pointer-events: none;
}
.pied__frise svg { width: 130px; height: 70px; display: block; }
.pied a { color: var(--nuit); text-decoration: none; }
.pied a:hover { text-decoration: underline; }
.pied__grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
  gap: var(--e32);
}
.pied h2 {
  font: 800 var(--t-nav)/1.33 var(--sans);
  letter-spacing: 0.053em; text-transform: uppercase;
  margin-bottom: var(--e16);
}
.pied li + li { margin-top: var(--e8); }
.pied li a { font-weight: 700; font-size: var(--t-corps); }
.pied__bas {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--e16);
  margin-top: var(--e48); padding-top: var(--e24);
  border-top: 2px solid rgba(0, 4, 55, .18);
  font-size: var(--t-legende); font-weight: 700;
}
.pied__marque {
  display: inline-flex; align-items: center; gap: var(--e12);
  font: 900 1.5rem/1 var(--display); letter-spacing: -0.02em;
}
.pied__marque svg { width: 36px; height: 36px; }
.pied__legal {
  display: flex; flex-direction: column; gap: var(--e8);
  flex: 1 1 280px;
}
.pied__detail summary { cursor: pointer; }
.pied__detail p { font-weight: 400; max-width: 62ch; margin-top: var(--e8); }
.pied__detail p + p { margin-top: var(--e12); }

/* ── Barre d'appel mobile ────────────────────────────────────────────────
   Le téléphone est l'action principale d'un magasin. Sur mobile elle reste
   sous le pouce, à toute profondeur de page. */
.barre-appel {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 60;
  display: none;
  gap: var(--e8);
  padding: var(--e12) var(--gouttiere) calc(var(--e12) + env(safe-area-inset-bottom));
  background: var(--blanc);
  border-top: 2px solid var(--trait);
}
.barre-appel .btn { flex: 1; min-height: 48px; padding-inline: var(--e12); }

/* ── Écrans étroits ──────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .nav { display: none; }
  .duo { grid-template-columns: 1fr; }
  .duo--inverse .duo__texte { order: 0; }
  .duo__illus { order: -1; }
  .chemin::before { left: 46px; display: block; }
  .etape { justify-items: start; text-align: left; grid-template-columns: auto 1fr; }
  .etape:nth-child(odd), .etape:nth-child(even) { transform: none; }
  .etape__bulle { grid-row: span 3; margin-right: var(--e16); }
  .barre-appel { display: flex; }
  body { padding-bottom: 84px; }
  .pied { margin-bottom: 0; }
}

@media (max-width: 720px) {
  /* Le numéro est déjà sous le pouce dans la barre d'appel : la pastille de
     l'en-tête ferait passer le nom du magasin sur deux lignes pour rien. */
  .barre .pastille { display: none; }
  .barre__inner { min-height: 64px; }
  .marque { font-size: 1.1875rem; }
  .marque svg { width: 34px; height: 34px; }
  .hero__actions { width: 100%; }
  /* L'illustration ne doit pas repousser le titre sous la ligne de flottaison. */
  .hero .illus { max-height: 34vh; width: auto; }
  .hero { padding-block: 24px 32px; }
  .section .illus { max-height: 38vh; width: auto; }
  section[id] { scroll-margin-top: 76px; }
}

/* ── Mouvement neutralisé ────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .etape__bulle::after { animation: none !important; opacity: 0; }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .js .apparait { opacity: 1; transform: none; }
}

/* ── Impression ──────────────────────────────────────────────────────────
   Quelqu'un qui imprime cette page veut une fiche : le nom, l'adresse, le
   téléphone, les horaires. Pas les scènes animées ni les aplats de couleur,
   qui vident une cartouche pour rien. */
@media print {
  /* Ce qui n'a pas de sens sur papier */
  .barre-appel, .bande, .champ__objets, .champ__sandrine, .pied__frise,
  .duo__illus, .illus, .saut, .copie, .nav, .barre .pastille { display: none !important; }

  /* Pas d'aplat, pas d'ombre : de l'encre seulement là où il y a du sens */
  *, *::before, *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
  }
  body { font-size: 11pt; padding-bottom: 0; }
  .barre { position: static; border-bottom: 2px solid #000; }
  .marque svg { filter: grayscale(1); }

  /* Le titre en dégradé est en `color: transparent` : sans ça il ne
     s'imprime pas du tout. */
  .nuit__titre { -webkit-text-fill-color: #000 !important; }

  /* Les colonnes se déplient sur une page */
  .duo, .grille, .pied__grille, .infos, .nuit__grille { display: block !important; }
  .champ, .nuit, .section, .pied { padding-block: 12pt !important; min-height: 0 !important; }

  /* Un lien imprimé sans son adresse ne sert à rien */
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8.5pt; word-break: break-all; }
  a[href^="#"]::after, a[href^="tel:"]::after, a[href^="mailto:"]::after { content: ""; }

  /* Ce qui ne doit pas être coupé en deux par un saut de page */
  .tbl, .carte, .etape, .agrement { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }

  /* Sans fond, les pastilles ont besoin d'un contour pour rester des objets */
  .tag, .pastille, .btn, .agrement { border: 1px solid #555 !important; }
  .tbl th, .tbl td { border-bottom: 1px solid #999 !important; }
}

/* Icônes au trait, dans le style épais de la mascotte. */
.ico {
  display: inline-block;
  vertical-align: -0.28em;
  width: 1.5em; height: 1.5em;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.ico--plein { fill: currentColor; stroke: none; }
.btn .ico, .pastille .ico, .statut .ico { width: 20px; height: 20px; flex: none; }

/* ============================================================================
   La section étalon : « tout ce qui roule »
   Bandeau plein cadre teinté, champ de pièces flottant en biais et rogné par
   les bords. C'est le seul endroit de la page où le contenu déborde du cadre :
   c'est ce débordement qui donne l'impression d'un monde plus grand que l'écran.
   ========================================================================= */
.champ {
  position: relative;
  overflow: hidden;
  background: var(--ciel-lavis);
  padding-block: clamp(64px, 9vw, 120px);
  min-height: clamp(620px, 104vh, 1080px);
  display: grid;
  align-content: center;
}

.champ__objets {
  position: absolute;
  inset: -6% -2%;
  pointer-events: none;
}

.piece {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc(96px * var(--s, 1));
  height: calc(96px * var(--s, 1));
  margin: calc(-48px * var(--s, 1));
  transform: rotate(var(--t, 0deg));
}
.piece svg { width: 100%; height: 100%; display: block; }

.piece--gag { overflow: visible; }
/* L'air s'échappe par la valve, en haut : le souffle ne doit pas recouvrir
   la chambre elle-même. */
.piece--gag .souffle {
  position: absolute;
  left: 46%;
  top: -28%;
  width: 84%;
  height: 70%;
  transform-origin: 10% 70%;
}

.champ__sandrine {
  position: absolute;
  left: clamp(-24px, 2vw, 40px);
  bottom: 0;
  width: clamp(150px, 24vw, 380px);
  pointer-events: none;
}
.sandrine { width: 100%; height: auto; display: block; }

.champ__texte { position: relative; z-index: 2; text-align: center; }
.champ__titre { margin-bottom: var(--e16); }
.champ__prose { margin-inline: auto; color: #3c4a6b; }
.champ__actions { justify-content: center; margin-top: var(--e32); }

/* Le champ ne doit jamais gêner la lecture : sur écran étroit on retire les
   pièces qui passeraient derrière le texte, et Sandrine rétrécit. */
@media (max-width: 900px) {
  /* Sur mobile la composition se retourne : le texte en haut, Sandrine dessous et
     centrée, et seules les pièces des bandes haute et basse restent : celles du
     milieu passeraient derrière le texte et les boutons. */
  .champ {
    min-height: 0;
    padding-block: 72px 300px;
    align-content: start;
  }
  .champ__sandrine {
    left: 50%;
    width: 186px;
    transform: translateX(-50%);
  }
  .piece { width: calc(60px * var(--s, 1)); height: calc(60px * var(--s, 1)); margin: calc(-30px * var(--s, 1)); }
  .piece:not(.piece--mob) { display: none; }
  .champ__texte { padding-inline: var(--gouttiere); }
  .champ__actions { flex-direction: column; align-items: stretch; }
}

/* ============================================================================
   La bande nuit : la rupture de registre
   Une seule section sur fond profond, et le seul endroit de la page où un
   dégradé est autorisé. C'est l'exception qui fait l'effet.
   ========================================================================= */
.nuit {
  background: var(--nuit-fond);
  color: #C3CCE8;
  padding-block: clamp(44px, 7vw, var(--e96));
}
.nuit__grille {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(24px, 5vw, 64px);
}
.nuit__illus { display: grid; place-items: center; }
/* Seule la bande nuit a droit à une lueur : c'est le courant lui-même qui
   éclaire, pas un halo posé derrière l'objet. */
.nuit .courant { filter: drop-shadow(0 0 9px rgba(28, 176, 246, .75)); }
.nuit__illus .illus { max-width: 520px; }

.nuit__titre {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(2.5rem, 6.4vw, 4.5rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  margin: var(--e12) 0 var(--e24);
  background-image: linear-gradient(96deg, var(--ciel) 8%, var(--mangue) 92%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.nuit__prose { color: #9FAAC9; margin-bottom: var(--e24); }
.surtitre--clair { color: var(--mangue-claire); }

/* Bouton fantôme sur fond profond : le contour clair remplace la tranche. */
.btn--contour {
  background: transparent;
  color: var(--blanc);
  border: 2px solid rgba(255, 255, 255, .32);
  --tranche-c: rgba(255, 255, 255, .18);
}
.btn--contour:hover {
  background: rgba(255, 255, 255, .08);
  --tranche-c: rgba(255, 255, 255, .28);
}

@media (max-width: 900px) {
  .nuit__grille { grid-template-columns: 1fr; }
  .nuit__illus { order: -1; }
  .nuit__illus .illus { max-width: 340px; }
}

/* ── Cibles tactiles ─────────────────────────────────────────────────────
   Clientèle de tous âges, souvent sur téléphone en extérieur : rien
   d'interactif ne descend sous 44 px de haut. */
.marque { min-height: 44px; }
.statut { min-height: 44px; }
.pied li a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.pied li + li { margin-top: 0; }
.foot-adresse { display: block; padding-block: 10px; }

/* ── Rangées de service ──────────────────────────────────────────────────
   Le rythme de la référence : une idée par rangée pleine, alternée, avec
   assez d'air entre elles pour qu'on lise une scène à la fois. */
.rangee-service { padding-block: clamp(26px, 5vw, 64px); }
.rangee-service + .rangee-service { border-top: 2px solid rgba(0, 4, 55, .06); }
.rangee-service .titre { font-size: clamp(1.75rem, 3.6vw, 2.5rem); }
.service__reste { margin-top: clamp(32px, 5vw, 56px); }
.service__reste .surtitre { display: block; margin-bottom: var(--e16); }

@media (max-width: 900px) {
  .rangee-service .illus { max-height: none; width: 100%; }
}

/* L'horloge est en portrait : sans plafond elle écrase la colonne. */
.illus--horloge { max-width: 300px; }

/* Le wordmark reprend le contraste de l'enseigne : AIN détaché du reste. */
.marque__ain { color: var(--nuit); }

/* ── Marques et agréments ────────────────────────────────────────────────
   Les marques sont des pastilles, pas des logos : dix-sept logos étrangers,
   chacun avec sa couleur et son contour, détruiraient le langage sans
   contour et la palette. En pastilles, elles restent à nous. */
.tag--phare {
  background: var(--mangue);
  color: var(--nuit);
  font-size: var(--t-nav);
  padding: 13px 20px;
  border-radius: var(--r-lg);
  box-shadow: 0 4px 0 var(--mangue-bord);
}
.marques__famille {
  display: block;
  margin: var(--e16) 0 var(--e8);
  color: var(--crayon);
}
.marques__famille:first-of-type { margin-top: var(--e8); }.agrement {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-height: 44px;
  padding: var(--e16) var(--e24);
  border: 2px solid rgba(255, 255, 255, .3);
  border-radius: var(--r-lg);
  text-decoration: none;
  max-width: 21rem;
  transition: background-color .12s ease, border-color .12s ease;
}
.agrement:hover { background: rgba(255, 255, 255, .07); border-color: rgba(255, 255, 255, .5); }
.agrement strong {
  font: 800 var(--t-inter)/1.25 var(--sans);
  color: var(--blanc);
}
.agrement span { font-size: var(--t-legende); line-height: 1.4; color: #A8B2CE; }

/* Le téléphone et l'e-mail du bloc « Venir » sont cliquables : ils prennent
   donc la couleur de lien et la hauteur de cible minimale. */
.info__lien {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ciel-lien);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.info__lien:hover { color: var(--nuit); }

/* Origine passe en second plan : un filet, un label discret, une ligne. Les
   deux agréments ne pèsent pas pareil, la hiérarchie doit le dire. */
.nuit__second {
  margin-top: var(--e32);
  padding-top: var(--e24);
  border-top: 2px solid rgba(255, 255, 255, .14);
}
.nuit__legende { display: block; margin-bottom: var(--e8); color: #8A95B5; }
.agrement--mineur {
  flex-direction: row;
  align-items: center;
  gap: var(--e12);
  padding: 10px var(--e16);
  max-width: none;
  border-color: rgba(255, 255, 255, .18);
}
.agrement--mineur strong { font-size: var(--t-nav); flex: none; }
.agrement--mineur span { font-size: var(--t-legende); }

@media (max-width: 560px) {
  .agrement--mineur { flex-direction: column; align-items: flex-start; gap: 4px; }
}

/* Le badge de l'enseigne est rond et plein : il lui faut sa place. */
.marque svg { border-radius: 50%; }
.pied__marque svg { width: 52px; height: 52px; border-radius: 50%; }
@media (min-width: 720px) { .pied__marque svg { width: 64px; height: 64px; } }

/* La confirmation de copie, quand la page est servie dans un cadre qui bloque
   les liens tel: et mailto:. */
.copie {
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 200;
  transform: translate(-50%, 20px);
  margin: 0;
  padding: 12px var(--e24);
  border-radius: var(--r);
  background: var(--nuit);
  color: var(--blanc);
  font: 800 var(--t-nav)/1.3 var(--sans);
  opacity: 0;
  pointer-events: none;
  /* Le bac à sable bloque aussi execCommand : si la copie échoue, au moins la
     valeur est sélectionnable d'un double-clic. */
  user-select: all;
  -webkit-user-select: all;
  transition: opacity .2s ease, transform .3s var(--ressort);
}
.copie--vu { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
@media (max-width: 900px) { .copie { bottom: 100px; } }

/* website-os:palette : written by website-os/tools/build.py, do not edit by hand */
:root {
  --mangue: #ff9600;
  --mangue-bord: #d97706;
  --mangue-titre: #c26a05;
  --mangue-texte: #9a4a08;
  --mangue-douce: #ffedd5;
  --mangue-claire: #ffc46b;
  --ciel: #1cb0f6;
  --ciel-bord: #1899d6;
  --ciel-lien: #0f72ae;
  --ciel-lavis: #e3f4fe;
  --nuit: #000437;
  --nuit-fond: #0b1033;
  --charbon: #4b4b4b;
  --crayon: #6b6b6b;
}
/* /website-os:palette */

/* ============================================================================
   MOTEUR D'ANIMATION — anciennement anim.css, fusionné ici le 2026-09-15.
   Un seul fichier CSS = une requête de moins, et la page 404 hérite du moteur
   sans requête supplémentaire. Le scellement SRI porte donc sur ce fichier
   entier : toute retouche impose une nouvelle release.
   ========================================================================= */

/* ============================================================================
   Le moteur d'animation : tout ce qui fait que la page est vivante.

   Quatre règles, appliquées partout :
   1. Rien n'est jamais immobile. Chaque personnage respire, 3 à 5 s, déphasé.
   2. Squash & stretch : ce qui atterrit s'écrase, ce qui part s'étire.
   3. Anticipation et dépassement : tout mouvement dépasse sa cible puis se cale.
      C'est ce ressort qui donne la sensation Duolingo.
   4. Le secondaire suit le principal : les boucles de cheveux ont 80 ms de retard.

   Les durées sont des nombres premiers de secondes (7, 11, 13, 17) pour que les
   boucles ne se resynchronisent jamais à l'œil.
   ========================================================================= */

:root {
  --ressort: cubic-bezier(.34, 1.56, .64, 1);   /* dépassement */
  --entree: cubic-bezier(.2, .7, .3, 1);        /* arrivée franche */
  --doux: cubic-bezier(.45, 0, .55, 1);         /* va-et-vient */
}

/* Toute scène hors cadre est mise en pause : une page pleine de boucles qui
   tournent dans le vide coûte cher en batterie pour rien. */
[data-anim='pause'] *,
[data-anim='pause'] { animation-play-state: paused !important; }

/* ── 1. La respiration de fond ──────────────────────────────────────────── */
@keyframes respire {
  0%, 100% { transform: translateY(0) scaleY(1); }
  50%      { transform: translateY(calc(var(--souffle, 5px) * -1)) scaleY(1.015); }
}
.respire {
  animation: respire var(--rythme, 4s) var(--doux) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: bottom center;
}

/* ── 2. La dérive : le champ d'objets flottants ─────────────────────────── */
@keyframes derive {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50%      { transform: translate3d(var(--dx, 0px), var(--dy, -22px), 0) rotate(var(--dr, 4deg)); }
}
.derive { animation: derive var(--rythme, 11s) var(--doux) var(--tempo, 0s) infinite; }

/* ── 3. La roue qui tourne ──────────────────────────────────────────────── */
@keyframes tourne { to { transform: rotate(360deg); } }
.tourne {
  animation: tourne var(--rythme, 9s) linear infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 4. Le clignement ────────────────────────────────────────────────────
   Un vrai clignement dure 120 ms sur un cycle de 5 s. La paupière est une
   forme couleur peau qui descend sur l'œil. */
@keyframes cligne {
  0%, 92%, 100% { transform: scaleY(0); }
  95%           { transform: scaleY(1); }
  98%           { transform: scaleY(0); }
}
.cligne {
  animation: cligne var(--rythme, 5.5s) steps(1, end) var(--tempo, 1s) infinite;
  transform-box: fill-box;
  transform-origin: top center;
}

/* ── 5. Le secondaire : les boucles suivent la tête ─────────────────────── */
@keyframes ballotte {
  0%, 100% { transform: rotate(0deg); }
  35%      { transform: rotate(var(--amplitude, 2.5deg)); }
  70%      { transform: rotate(calc(var(--amplitude, 2.5deg) * -0.6)); }
}
.ballotte {
  animation: ballotte var(--rythme, 4s) var(--doux) var(--tempo, .08s) infinite;
  transform-box: fill-box;
  transform-origin: var(--pivot, top center);
}

/* ── 6. Le rebond : squash & stretch à l'atterrissage ───────────────────── */
/* ── 7. La chambre à air, l'épine et la rustine ─────────────────────────
   Le récit complet en 12 s. La piqûre tombe à 14 %, soit **1,7 s** après que
   la scène entre dans le cadre : le visiteur la voit, au lieu d'arriver après
   coup. Et 42 % du cycle se passe en repos rustiné : c'est la réparation qui
   doit rester à l'écran, pas la panne.

   opacity est déclarée à CHAQUE étape : si elle n'apparaît qu'aux étapes où
   elle change, le navigateur l'interpole depuis l'image 0. */
@keyframes fuse {
  0%,  6%   { transform: translate3d(0,0,0) scale(1) rotate(0deg); opacity: 1; }
  14%       { transform: translate3d(0,0,0) scale(1) rotate(0deg); opacity: 1; }
  16%       { transform: translate3d(0,0,0) scale(1.07, 1.04) rotate(0deg); opacity: 1; }
  18%       { transform: translate3d(0,0,0) scale(1.02, 1.07) rotate(-4deg); opacity: 1; }
  20%       { transform: translate3d(0,0,0) scale(1.06, 1.01) rotate(4deg);  opacity: 1; }
  22%       { transform: translate3d(0,0,0) scale(1.0, 1.05) rotate(-4deg);  opacity: 1; }
  24%       { transform: translate3d(0,0,0) scale(.82, .86) rotate(-8deg);   opacity: 1; }
  42%       { transform: translate3d(var(--vol, 64vw), var(--chute, -24vh), 0) scale(.5, .55) rotate(-480deg); opacity: 0; }
  43%, 51%  { transform: translate3d(0,0,0) scale(.6) rotate(0deg); opacity: 0; }
  58%, 100% { transform: translate3d(0,0,0) scale(1) rotate(0deg);  opacity: 1; }
}
.fuse {
  animation: fuse var(--rythme, 12s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* Le sifflement part du TROU, pas de la valve. */
@keyframes souffle {
  0%,  13%  { transform: scale(.15); opacity: 0; }
  16%       { transform: scale(.9);  opacity: .95; }
  22%       { transform: scale(1.6); opacity: .8; }
  34%       { transform: scale(2.4) translateX(22px); opacity: 0; }
  100%      { transform: scale(.15); opacity: 0; }
}
.souffle {
  animation: souffle var(--rythme, 12s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* L'épine arrive en accélérant, se plante, reste fichée, puis dérive. */
@keyframes pique {
  0%,  5%   { transform: translate(170px, -46px) rotate(-30deg); opacity: 0; }
  8%        { transform: translate(124px, -30px) rotate(-30deg); opacity: 1; }
  14%       { transform: translate(0, 0) rotate(-30deg);         opacity: 1; }
  15%, 30%  { transform: translate(-7px, 5px) rotate(-30deg);    opacity: 1; }
  44%       { transform: translate(96px, 72px) rotate(64deg);    opacity: 0; }
  100%      { transform: translate(170px, -46px) rotate(-30deg); opacity: 0; }
}
.pique {
  animation: pique var(--rythme, 12s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* Les yeux s'écarquillent à la piqûre et le restent pendant la panique. */
@keyframes sursaut {
  0%,  13%  { transform: scale(1); }
  15%, 30%  { transform: scale(1.45); }
  36%, 100% { transform: scale(1); }
}
.sursaut {
  animation: sursaut var(--rythme, 12s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* La rustine est posée au retour et reste jusqu'à la piqûre suivante : elle
   traverse donc la couture de la boucle, qui devient invisible. */
@keyframes rustine {
  0%,  13%  { opacity: 1; transform: scale(1); }
  14%, 51%  { opacity: 0; transform: scale(.4); }
  57%       { opacity: 1; transform: scale(1.18); }
  62%, 100% { opacity: 1; transform: scale(1); }
}
.rustine {
  animation: rustine var(--rythme, 12s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}
/* L'inverse, pour ce qui n'existe QUE pendant la panne. */
@keyframes rustine-envers {
  0%,  13%  { opacity: 0; }
  14%, 51%  { opacity: 1; }
  57%, 100% { opacity: 0; }
}
.rustine-envers { animation: rustine-envers var(--rythme, 12s) steps(1, end) var(--tempo, 0s) infinite; }

/* ── 8. L'étincelle de la clé ───────────────────────────────────────────── */
@keyframes etincelle {
  0%, 70%, 100% { transform: scale(0) rotate(0deg); opacity: 0; }
  76%           { transform: scale(1.15) rotate(35deg); opacity: 1; }
  86%           { transform: scale(.5) rotate(70deg); opacity: 0; }
}
.etincelle {
  animation: etincelle var(--rythme, 7s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 9. La pulsation d'appel ────────────────────────────────────────────── */
@keyframes pulse {
  0%, 70%, 100% { transform: scale(1); }
  78%           { transform: scale(1.06); }
  86%           { transform: scale(.99); }
}
.pulse { animation: pulse var(--rythme, 6s) var(--ressort) var(--tempo, 0s) infinite; }

/* ── 10. L'arrivée au défilement, avec dépassement ──────────────────────── */
.js .apparait { opacity: 0; transform: translateY(22px) scale(.985); }
.js .apparait.vu {
  opacity: 1;
  transform: none;
  transition: opacity .45s var(--entree), transform .7s var(--ressort);
}
.js .apparait.vu:nth-child(2) { transition-delay: .07s; }
.js .apparait.vu:nth-child(3) { transition-delay: .14s; }

/* ── 11. Le compteur de note ────────────────────────────────────────────── */
/* ── Mouvement neutralisé ────────────────────────────────────────────────
   Tout se fige sur la première image. La page reste entièrement lisible. */
@media (prefers-reduced-motion: reduce) {
  .respire, .derive, .tourne, .cligne, .ballotte,
  .fuse, .souffle, .etincelle, .pulse {
    animation: none !important;
  }
  .js .apparait { opacity: 1; transform: none; }
  .fuse { opacity: 1; }
}

/* ── 12. Les traits de vitesse qui défilent ─────────────────────────────── */
@keyframes file {
  0%        { transform: translateX(34px); opacity: 0; }
  22%       { opacity: 1; }
  78%       { opacity: 1; }
  100%      { transform: translateX(-46px); opacity: 0; }
}
.file { animation: file var(--rythme, 1.6s) linear var(--tempo, 0s) infinite; }

/* ── 13. Le courant qui parcourt le cadre ────────────────────────────────
   Un tiret unique qui file le long du tracé : c'est le trait lui-même qui se
   déplace, pas une lueur posée par-dessus. */
@keyframes courant { to { stroke-dashoffset: var(--course, -900); } }
.courant { animation: courant var(--rythme, 3.4s) linear var(--tempo, 0s) infinite; }

/* ── 14. La batterie qui se remplit ─────────────────────────────────────── */
@keyframes charge {
  0%,  52%  { transform: scaleX(0); }   /* elle dort */
  76%       { transform: scaleX(1); }   /* le courant est arrivé */
  94%       { transform: scaleX(1); }
  100%      { transform: scaleX(0); }   /* reset bref, sur la couture */
}
.charge {
  animation: charge var(--rythme, 5s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: left center;
}

@media (prefers-reduced-motion: reduce) {
  .file, .courant, .charge { animation: none !important; }
  .file { opacity: 1; }
  .charge { transform: scaleX(1); }
}

/* ── 15. La goutte d'huile ──────────────────────────────────────────────── */
@keyframes goutte {
  0%, 52%   { transform: translateY(0) scale(.4, .6);  opacity: 0; }
  58%       { transform: translateY(0) scale(1, 1);    opacity: 1; }
  90%       { transform: translateY(var(--chute, 62px)) scale(.85, 1.25); opacity: 1; }
  100%      { transform: translateY(var(--chute, 62px)) scale(1.5, .35);  opacity: 0; }
}
.goutte {
  animation: goutte var(--rythme, 4s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 16. La marche des pédales ──────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .goutte { animation: none !important; }
  .goutte { opacity: 1; }
}

/* ── 17. La traversée ───────────────────────────────────────────────────
   Manon roule d'un bord à l'autre du plan, en boucle. Linéaire : une vitesse
   constante lit comme un déplacement, une courbe lirait comme une hésitation. */
@keyframes traverse {
  from { transform: translateX(var(--depart, -30%)); }
  to   { transform: translateX(var(--arrivee, 130%)); }
}
.traverse { animation: traverse var(--rythme, 11s) linear var(--tempo, 0s) infinite; }

@media (prefers-reduced-motion: reduce) {
  .traverse { animation: none !important; transform: translateX(28%); }
}

/* ── 18. La mâchoire de la clé à molette ────────────────────────────────
   Le geste signature d'une molette : la mâchoire mobile qui coulisse. */
@keyframes machoire {
  0%, 100% { transform: translateX(0); }
  45%      { transform: translateX(var(--ecart, -5px)); }
}
.machoire {
  animation: machoire var(--rythme, 3.1s) var(--doux) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 19. Le démontage ────────────────────────────────────────────────────
   La pièce sort, flotte, puis revient s'emboîter. L'animation-timing-function
   posée DANS l'étape à 58 % donne au retour le ressort qui fait « clic » :
   c'est ce détail qui sépare une pièce qui glisse d'une pièce qui s'emboîte. */
@keyframes detache {
  0%        { transform: translate(0,0) rotate(0deg); animation-timing-function: cubic-bezier(.2,.7,.3,1); }
  14%       { transform: translate(0,0) rotate(0deg); }
  30%       { transform: translate(var(--ex, 0px), var(--ey, 0px)) rotate(var(--er, 0deg)); }
  58%       { transform: translate(var(--ex, 0px), var(--ey, 0px)) rotate(var(--er, 0deg));
              animation-timing-function: cubic-bezier(.34,1.56,.64,1); }
  76%, 100% { transform: translate(0,0) rotate(0deg); }
}
.detache {
  animation: detache var(--rythme, 14s) linear var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 20. Le tube qu'on presse ───────────────────────────────────────────
   Il se comprime juste avant que la goutte tombe : on voit QUI la produit. */
@keyframes presse {
  0%,  40%  { transform: scale(1, 1); }
  52%       { transform: scale(.88, 1.07); }
  62%       { transform: scale(1.03, .97); }
  72%, 100% { transform: scale(1, 1); }
}
.presse {
  animation: presse var(--rythme, 4s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 21. La chaîne qui défile ────────────────────────────────────────────
   Le groupe se translate d'exactement un pas de maillon. Au bouclage, le
   maillon n+1 a pris la place du maillon n : la couture est invisible. */
@media (prefers-reduced-motion: reduce) {
  .machoire, .detache, .presse, .pique, .sursaut,
  .rustine, .rustine-envers {
    animation: none !important;
  }
  .pique { opacity: 0; }
  .rustine { opacity: 1; }
  .rustine-envers { opacity: 0; }
}

/* ── 22. L'onde ─────────────────────────────────────────────────────────
   Un anneau qui s'ouvre et s'efface. C'est le véhicule de la cause : ce que
   le spectateur suit des yeux d'un objet à l'autre. */
@keyframes onde {
  0%, 58%   { transform: scale(.3);  opacity: 0; }
  64%       { transform: scale(.62); opacity: .8; }
  100%      { transform: scale(2);   opacity: 0; }
}
.onde {
  animation: onde var(--rythme, 7s) var(--entree) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 23. Le sursaut ─────────────────────────────────────────────────────
   La réaction à l'onde. Elle arrive avec le retard qu'il faut à l'onde pour
   traverser la distance : c'est ce décalage qui fait lire la cause. */
@keyframes sursaute {
  0%, 66%, 100% { transform: translateY(0) scale(1, 1); }
  70%           { transform: translateY(-18px) scale(.93, 1.09); }
  77%           { transform: translateY(0) scale(1.09, .91); }
  84%           { transform: translateY(-6px) scale(1, 1); }
}
.sursaute {
  animation: sursaute var(--rythme, 7s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: bottom center;
}

/* ── 24. Le réveil ──────────────────────────────────────────────────────
   Les yeux fermés s'ouvrent quand le courant arrive. Une paupière qui se lève
   raconte mieux qu'une lueur qui monte. */
@keyframes reveil {
  0%,  52%  { transform: scaleY(.1); }
  58%, 90%  { transform: scaleY(1); }
  100%      { transform: scaleY(.1); }
}
.reveil {
  animation: reveil var(--rythme, 6.4s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

@media (prefers-reduced-motion: reduce) {
  .onde, .sursaute, .reveil { animation: none !important; }
  .onde { opacity: 0; }
  .reveil { transform: scaleY(1); }
}

/* ── 25. L'éclat qui traverse les étoiles ───────────────────────────────
   Décalées de 0,11 s, les cinq étoiles font passer une vague de gauche à
   droite. C'est cette vague que la note attend pour sourire. */
@keyframes brille {
  0%, 70%, 100% { transform: scale(1) rotate(0deg); }
  76%           { transform: scale(1.38) rotate(-14deg); }
  84%           { transform: scale(1) rotate(0deg); }
}
.brille {
  animation: brille var(--rythme, 9s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── 26. Le sourire qui répond ──────────────────────────────────────────
   Il s'élargit juste APRÈS que la vague a fini de traverser : c'est le
   décalage qui fait lire la réaction plutôt que la coïncidence. */
@keyframes sourit {
  0%, 82%, 100% { transform: scale(1, 1); }
  88%           { transform: scale(1.55, 1.35); }
  95%           { transform: scale(1, 1); }
}
.sourit {
  animation: sourit var(--rythme, 9s) var(--ressort) var(--tempo, 0s) infinite;
  transform-box: fill-box;
  transform-origin: center;
}

@media (prefers-reduced-motion: reduce) {
  .brille, .sourit { animation: none !important; }
}

/* ── 28. La chaîne qui court sur sa transmission ────────────────────────
   Le tracé est fermé et tangent au plateau et à la cassette. Les maillons
   sont un pointillé ; c'est le décalage du pointillé qui défile, donc la
   boucle est parfaite quel que soit le périmètre. */
@keyframes defile-chaine { to { stroke-dashoffset: var(--pas-chaine, -16px); } }
.defile-chaine { animation: defile-chaine var(--rythme, .55s) linear infinite; }

/* ── 29. Les antennes de Boulon ─────────────────────────────────────────
   Elles suivent sa respiration avec du retard, comme les boucles de Sandrine. */
@keyframes antenne {
  0%, 100% { transform: rotate(0deg); }
  40%      { transform: rotate(var(--amplitude, 6deg)); }
  75%      { transform: rotate(calc(var(--amplitude, 6deg) * -0.5)); }
}
.antenne {
  animation: antenne var(--rythme, 3.6s) var(--doux) var(--tempo, .14s) infinite;
  transform-box: fill-box;
  transform-origin: bottom center;
}

@media (prefers-reduced-motion: reduce) {
  .defile-chaine, .antenne { animation: none !important; }
}

/* ============================================================================
   PAGE 404 — « Cette page a crevé »

   Ces règles vivent ici et pas en attributs `style` sur la page : la CSP du
   404 ne déclare pas de `style-src-attr`, donc la directive retombe sur
   `default-src 'none'` et un `style="…"` y est refusé. La mise en page tenait
   entièrement dans ces attributs : elle tombait en pile brute. Mesuré le
   2026-09-15 sur une page sonde portant la même CSP — Chrome refuse :
   « Applying inline style violates … 'default-src 'none''. The action has
   been blocked. » C'est la CSP en <meta> qui bloque, pas l'en-tête HTTP :
   la page était donc déjà cassée en local, pas seulement en ligne.

   Contrastes mesurés sur fond blanc : --nuit #000437 → 19,4:1,
   --charbon #4b4b4b → 8,6:1. Les deux passent AAA.
   ========================================================================= */

.introuvable {
  display: grid;
  place-items: center;
  /* 640 px, jamais 100vh : sur un portable en paysage, 100vh écrase la scène
     et le bouton sort de l'écran. */
  min-height: 640px;
  text-align: center;
}
.introuvable__corps {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e24);
}
.introuvable__scene {
  width: min(290px, 66vw);
  height: auto;
}
.introuvable__prose { max-width: 46ch; }
.introuvable__secours { color: var(--charbon); }
.introuvable__secours a {
  color: var(--nuit);
  font-weight: 800;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

/* La chambre perd son air, l'épine tremble : deux boucles décalées, durées
   premières (7 s et 11 s) pour qu'elles ne se resynchronisent jamais à l'œil.
   Le bloc global `prefers-reduced-motion` plus haut les coupe toutes les deux. */
/* Origines exprimées dans le repère LOCAL de chaque pièce (la boîte 0-100 du
   casting), pas dans celui du viewBox : le <g> qui anime est à l’intérieur du
   <g> qui place. */
.introuvable__chambre {
  transform-origin: 50px 96px;
  animation: degonfle 7s ease-in-out infinite;
}
.introuvable__epine {
  transform-origin: 88px 10px;
  animation: tremble 11s ease-in-out infinite;
}
@keyframes degonfle {
  0%, 62%, 100% { transform: scaleY(1); }
  78%           { transform: scaleY(.955); }
}
@keyframes tremble {
  0%, 88%, 100% { transform: rotate(0deg); }
  92%           { transform: rotate(-2.5deg); }
  96%           { transform: rotate(1.5deg); }
}
