/* ===========================================================================
   Vauban — mise en page du site public.
   Toutes les couleurs, tailles, espacements et rayons viennent de tokens.css.
   Ce fichier ne pose que des gabarits : grilles, largeurs de colonne, états.
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body { margin: 0; }
img, svg { max-width: 100%; }
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: var(--accent); }
/* Le survol d'un lien ne vaut QUE pour les liens de corps de texte. Sans le
   :not(.btn), `a:hover` (0,1,1) l'emporte sur `.btn--accent` (0,1,0) et le
   texte du bouton vert prend la couleur de son propre fond : le libellé
   disparaît. Chaque variante de bouton décide de sa couleur, c'est la règle
   du § 6 — ce sélecteur ne doit jamais repasser devant. */
a:not(.btn):hover { color: var(--accent-hover); }

:where(a, button, input, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Gouttière latérale : posée une seule fois, sur l'enveloppe extérieure. */
.wrap {
  width: 100%;
  max-width: 1152px;          /* 128 × 9, sur l'échelle d'espacement */
  margin-inline: auto;
  padding-inline: var(--gutter-min);
}
@media (min-width: 760px) { .wrap { padding-inline: var(--sp-8); } }
.wrap--narrow { max-width: 896px; }  /* 128 × 7 */

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: var(--sp-4); top: var(--sp-4); z-index: 20;
  background: var(--paper); color: var(--ink);
  padding: var(--sp-2) var(--sp-4);
  border: var(--border-w) solid var(--rule-strong);
  border-radius: var(--r-control);
}

/* --- Utilitaires ---------------------------------------------------------- */
/* Lu par les lecteurs d'écran, invisible à l'œil : la réponse « Oui » ou
   « Non » d'un tableau comparatif, que le pictogramme seul ne donnerait pas. */
.vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.mt-2 { margin-top: var(--sp-2); }
.mt-4 { margin-top: var(--sp-4); }
.mt-6 { margin-top: var(--sp-6); }
.mt-8 { margin-top: var(--sp-8); }
.mt-12 { margin-top: var(--sp-12); }
.mb-8 { margin-bottom: var(--sp-8); }
.lede { color: var(--ink-2); }
.prose > * + * { margin-top: var(--sp-4); }
.stack-2 { display: grid; gap: var(--sp-2); }
.stack-4 { display: grid; gap: var(--sp-4); }
.stack-6 { display: grid; gap: var(--sp-6); }

.list-dash { display: grid; gap: var(--sp-3); }
.list-dash li { position: relative; padding-left: var(--sp-6); }
.list-dash li::before { content: "\2014"; position: absolute; left: 0; color: var(--accent); }
.plan-card .list-dash li { display: block; }   /* sinon la règle flex de tokens.css coupe le gras et le texte en deux colonnes */

/* --- Le trait qui encercle — une seule fois sur tout le site --------------
   Un groupe de mots du titre entouré d'un trait, comme on annote une page.
   Le dessin s'étire sur les mots sans que son épaisseur suive : c'est un
   geste de stylo, et un stylo ne s'épaissit pas quand la phrase s'allonge. */
/* `isolation` n'est pas décoratif : sans lui, le trait en z-index négatif
   remonte au contexte d'empilement de la page et disparaît derrière le fond
   de la bande. Isolé ici, il passe SOUS les lettres et rien d'autre. */
.marque { position: relative; display: inline-block; isolation: isolate; }
.marque__trait {
  position: absolute; z-index: -1; pointer-events: none;
  /* L'air latéral est passé de 0,3 à 0,14 em le 17 septembre 2026. Mesuré à
     80 px sur l'accroche : l'espace entre « IT » et « externalisée » ne fait
     que 15 px, et 0,3 em en demandait 24 de chaque côté — le trait mordait
     de 14 px à droite et frôlait « Votre » à 1 px à gauche. À 0,14 em il
     reste 4 px de jeu des deux côtés. La valeur est en em, donc le rapport
     tient à toutes les tailles : l'espace entre les mots suit la même
     échelle que le trait. */
  left: -0.14em; top: -0.12em;
  width: calc(100% + 0.28em); height: calc(100% + 0.24em);
  /* La règle générale `svg { max-width: 100% }` rognait le dessin de sa
     marge : il s'arrêtait avant la fin des mots qu'il devait entourer. */
  max-width: none;
  overflow: visible;
  color: var(--accent-vif);
}

/* Le trait se TRACE au chargement, il n'est pas déjà là : c'est ce qui en
   fait un geste d'annotation plutôt qu'un ornement imprimé. Il part une fois
   l'accroche posée, et ne se rejoue jamais.

   Un seul tiret, long comme tout le chemin, décalé hors de lui puis ramené :
   la pointe avance d'un bout à l'autre. Sa longueur est mesurée par site.js
   et posée ici en `--trait-len` — elle ne peut pas être écrite en dur.
   `non-scaling-stroke` fait calculer les tirets dans l'espace de l'ÉCRAN,
   où `preserveAspectRatio="none"` a étiré le dessin à la largeur des mots :
   une longueur prise sur le viewBox arrêterait le tracé avant la fin.

   La valeur de repli est volontairement trop grande : tant que la mesure
   n'est pas faite, le trait n'est pas tracé du tout — jamais à moitié.

   Tout est sous `.js`. Sans script — ou en mouvement réduit, où la classe
   n'est jamais posée — le trait est simplement là, entier. */
.js .marque__trait path {
  stroke-dasharray: var(--trait-len, 4000px);
  stroke-dashoffset: var(--trait-len, 4000px);
}
/* La transition est portée par l'état d'arrivée, pas par l'état de repos :
   sinon la mesure elle-même — qui fait passer le décalage du repli à la
   longueur vraie — serait animée, et le tracé démarrerait d'un point faux. */
.js .is-in .marque__trait path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset var(--dur-trait) var(--ease-trait) var(--delay-trait);
}

/* --- Pictogrammes : Lucide, trait 1,5 px, jamais remplis, jamais colorés --- */
.ico {
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: var(--ink-2); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.ico--lg { width: 24px; height: 24px; }

/* --- Logo ----------------------------------------------------------------
   Le logo est une image : les lettres de « Vauban » découpées dans une
   grille de six cases. Ses contreformes sont fines — en dessous de 104 px
   de large elles se bouchent, d'où le plancher posé ici.                   */
.logo { display: block; width: 104px; height: auto; }
.logo--foot { width: 152px; }
.logo-lien { display: inline-flex; line-height: 0; border-radius: var(--r-control); }
.logo-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.logo-lockup { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
@media (max-width: 559px) { .logo { width: 104px; } .logo--foot { width: 132px; } }

/* --- Wordmark ------------------------------------------------------------- */
.wordmark {
  display: inline-flex; align-items: baseline;
  font-family: var(--font-serif); font-weight: 500;
  font-size: var(--size-h3); letter-spacing: -0.02em;
  color: var(--ink); text-decoration: none;
}
.wordmark--footer { font-size: var(--size-h2); }

/* --- En-tête : figé en haut, comme chez Graneet -------------------------- */
.site-head {
  position: sticky; top: 0; z-index: 10;
  border-bottom: var(--border-w) solid var(--rule);
  background: var(--paper);
}
.head-inner {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-4) var(--sp-8); padding-block: var(--sp-3);
}
/* Une ancre ne doit pas passer sous la barre figée. */
:target { scroll-margin-top: var(--sp-16); }
@media print { .site-head { position: static; } }
.site-nav { margin-inline-start: auto; }
.site-nav ul { display: flex; flex-wrap: wrap; gap: var(--sp-6); }
.site-nav .nav-link { position: relative; font-size: var(--size-small); border-bottom: 2px solid transparent; }
.site-nav .nav-link:hover { color: var(--ink); }
/* Le filet se TRACE de gauche à droite, il n'apparaît pas d'un bloc : c'est
   le seul endroit du site où un survol dessine quelque chose. L'entrée ne
   bouge pas d'un pixel — le trait est un calque posé sur la place que la
   bordure transparente réserve déjà, et la largeur de l'entrée ne change
   jamais. La page courante ne le prend pas : son filet d'encre est une
   information, pas un état, et les deux se confondraient.                   */
.site-nav .nav-link::after {
  content: ""; position: absolute; inset-inline: 0; bottom: -2px;
  border-bottom: 2px solid var(--rule-strong);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-control) var(--ease-entry);
}
.site-nav .nav-link:hover::after { transform: scaleX(1); }
.site-nav .nav-link[aria-current]::after { content: none; }
/* Le repère de section active reste en encre : le bouton primaire de la page
   est déjà en accent vif et les deux sont visibles sur le même écran.
   La graisse + le filet portent l'information (tokens.css, emploi 3). */
.site-nav .nav-link[aria-current] { border-bottom-color: var(--ink); }
@media (max-width: 759px) {
  .site-nav { margin-inline-start: 0; order: 3; width: 100%; }
  .head-cta { margin-inline-start: auto; }
}

/* --- Boutons : le survol change de couleur, franchement et sans délai -----
   tokens.css pose les fonds ; on ajoute ici le filet qui rend le changement
   perceptible au premier coup d'œil, sans ombre et sans sortir des jetons.  */
.btn { transition: background-color var(--dur-state) var(--ease-state),
                   border-color     var(--dur-state) var(--ease-state),
                   color            var(--dur-state) var(--ease-state); }
.btn--primary:hover   { background: var(--accent-vif-hover); border-color: var(--on-accent-vif); }
.btn--secondary:hover { background: var(--paper-2); border-color: var(--ink); color: var(--ink); }
/* Le bouton « Texte » prend son soulignement au survol : c'est la bordure
   que le § 6 lui prescrit, et elle manquait. */
.btn--text:hover      { color: var(--accent-hover); text-decoration: underline; text-underline-offset: 3px; }

/* Le bouton persistant de l'en-tête : fond vert anglais, papier dessus.
   Il ne prend jamais le jaune — celui-ci reste l'action de la page,
   et les deux seraient visibles en même temps sous la barre figée. */
.btn--accent { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.btn--accent:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn--accent:focus-visible { outline-color: var(--ink); }

/* --- Bandes : l'alternance de fond porte le découpage ---------------------- */
.band { padding-block: var(--sp-12); }
.band--p2 { background: var(--paper-2); }
.band--p3 { background: var(--paper-3); }
.band--soft { background: var(--accent-soft); }
/* Le champ vert plein. Seule bande sombre du corps du site : elle isole le
   procédé sans avoir à l'annoncer (brand.md § Les interdits, 18 septembre
   2026). Le vert profond, pas le vert de marque — c'est le jeton dont le rôle
   est déjà « champ plein cadre », et le pied de page garde le vert plein.
   Les deux ne se touchent jamais : deux bandes claires les séparent. */
.band--vert { background: var(--accent-hover); color: var(--on-accent); }
.band--vert .t-label,
.band--vert .lede,
.band--vert .t-small { color: var(--accent-soft); }
.band--ruled { border-top: var(--border-w) solid var(--rule); }
@media (min-width: 760px) { .band { padding-block: var(--sp-24); } }

.band-head { display: grid; gap: var(--sp-4); max-width: 68ch; margin-bottom: var(--sp-8); }
@media (min-width: 760px) { .band-head { margin-bottom: var(--sp-12); } }
.band-head--row { max-width: none; }
@media (min-width: 880px) {
  .band-head--row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
    /* Les deux colonnes partagent le même axe : le titre et le chapeau se
       centrent l'un sur l'autre. Alignés par le bas, un titre d'une ligne et
       un chapeau de deux ou trois lignes se lisaient comme décalés. */
    align-items: center; gap: var(--sp-12);
  }
}

/* --- Accroche ------------------------------------------------------------- */
.hero-text { display: grid; gap: var(--sp-6); justify-items: start; }
/* L'accroche est centrée sur toute la largeur : c'est l'échelle du titre qui
   porte la promesse, et une colonne de gauche la lui retire. */
.hero-text--centre { justify-items: center; text-align: center; }
.hero-text--centre .accroche-suite { max-width: 68ch; margin-inline: auto; }
.hero-text--centre .cta-row { justify-content: center; }
.hero-text--centre .tags { justify-content: center; }
/* L'accroche respire plus large que les autres bandes : c'est elle qui décide
   où tombe le pli de l'écran. Visé, à hauteur d'un portable : le logo émerge
   de la couture et s'arrête là ; les messages du schéma ne se découvrent qu'au
   défilement, sinon ils se lisent comme la suite de l'accroche. */
@media (min-width: 760px) {
  .hero-text--centre { gap: var(--sp-8); }
  .hero-text--centre .accroche-suite { margin-top: 0; }
  .hero-text--centre .cta-row { margin-top: var(--sp-6); }
  .band:has(.hero-text--centre) { padding-top: var(--sp-32); }
  .band:has(.hero-text--centre):has(+ .band .illus) {
    padding-bottom: calc(var(--sp-24) + 120px);
  }
}
.accroche-suite { color: var(--ink-2); max-width: 24ch; margin-top: calc(-1 * var(--sp-2)); }
@media (min-width: 760px) { .accroche-suite { max-width: 34ch; } }
.cta-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }

.tags { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.tag {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--size-small); color: var(--ink-2);
}

/* --- Les métiers auxquels on s'adresse ------------------------------------ */
/* À gauche la liste qui défile, à droite la phrase qui dit pourquoi. Le motif
   vient de rfa.com. C'est la SEULE boucle du système : l'interdit du § 5 est
   amendé pour ce seul objet, et il tient partout ailleurs.

   Le raccord tient à deux choses et à rien d'autre : la piste porte la liste
   deux fois, et chaque ligne a la MÊME hauteur — d'où `--metier-h` plutôt
   qu'un `gap`, dont la moitié manquerait à chaque tour et ferait sauter la
   boucle. Le décalage va de -50 % à 0 : les lignes descendent.              */
.verticales { display: grid; gap: var(--sp-8); }
@media (min-width: 880px) {
  .verticales { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: var(--sp-16); align-items: center; }
}
.verticales__mot { display: grid; gap: var(--sp-4); align-content: center; }
.verticales__mot p { max-width: 46ch; }

.verticales__defile {
  --metier-h: 4.5rem;
  --lignes: 3;
  overflow: hidden;
  height: calc(var(--lignes) * var(--metier-h));
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 24%, #000 76%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 24%, #000 76%, transparent);
}
/* La durée est celle d'UNE ligne : la piste multiplie par le nombre de
   métiers, posé en ligne par le rendu. Sans ça, allonger la liste la
   ralentirait — et c'est la vitesse de défilement d'un nom qui se règle,
   pas la durée d'un tour.                                                   */
.metiers {
  display: block;
  animation: defile calc(var(--dur-defile) * var(--n, 5)) linear infinite;
}
/* Une boucle qu'on ne peut pas arrêter est une faute d'accessibilité avant
   d'être une faute de charte : elle s'arrête sous le pointeur et dès qu'un
   élément de la bande prend le focus.                                       */
.verticales__defile:hover .metiers,
.verticales__defile:focus-within .metiers { animation-play-state: paused; }

/* Un nom qui passe sur deux lignes doit tenir dans la hauteur de sa ligne :
   c'est elle qui fait le raccord de la boucle, elle ne peut pas s'adapter au
   contenu. D'où la rangée plus haute là où les noms se replient.            */
@media (max-width: 879px) { .verticales__defile { --metier-h: 6rem; } }
.metier {
  display: flex; align-items: center;
  height: var(--metier-h);
  font-family: var(--font-serif); font-weight: 500;
  font-size: var(--size-h2); line-height: 1.15; letter-spacing: -0.02em;
}
@keyframes defile {
  from { transform: translateY(-50%); }
  to   { transform: translateY(0); }
}
/* Mouvement réduit : la page se rend telle qu'elle est — la liste entière,
   immobile, et le double qui servait au raccord disparaît (§ 5).            */
@media (prefers-reduced-motion: reduce) {
  .verticales__defile { height: auto; -webkit-mask-image: none; mask-image: none; }
  .metiers { animation: none; }
  .metier[aria-hidden="true"] { display: none; }
}

/* --- Bande de repères ----------------------------------------------------- */
.marks { display: grid; gap: var(--sp-8); }
@media (min-width: 560px) { .marks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 880px) { .marks { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-6); } }
.mark { display: grid; gap: var(--sp-2); padding-top: var(--sp-4); border-top: 2px solid var(--ink); }
.mark__n { font-family: var(--font-sans); font-weight: 600; font-size: var(--size-h2); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

/* --- Cartes de service ---------------------------------------------------- */
.cards { display: grid; gap: var(--sp-4); }
.cards > * { min-width: 0; }
@media (min-width: 620px) { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 960px) { .cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.card {
  display: grid; gap: var(--sp-3); align-content: start;
  background: var(--paper);
  border: var(--border-w) solid var(--rule);
  border-radius: var(--r-card);
  padding: var(--sp-6);
  transition: border-color     var(--dur-control) var(--ease-state),
              background-color var(--dur-control) var(--ease-state);
}
.band--p2 .card, .band--p3 .card { background: var(--paper); }
/* La carte qui porte une photo : même filet, même rayon, aucun rembourrage.
   L'image va au bord du cadre, sinon la photo flotte dans une carte et on
   voit le gabarit avant de voir les visages. */
.card--photo { padding: 0; overflow: hidden; align-content: stretch; }
.card--photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.card--photo:hover { background: var(--paper); }
/* Le survol d'une carte : le filet se renforce, la surface descend d'un cran,
   le numéro fonce. Trois signaux de couleur, aucun déplacement et aucune
   ombre — la carte ne se soulève pas (brand.md § 6). C'est exactement le
   geste du bouton secondaire, appliqué à une surface plus grande.           */
.card:hover { border-color: var(--rule-strong); background: var(--paper-2); }
.card:hover .card__n { color: var(--accent-hover); }
.card__top { display: flex; align-items: center; gap: var(--sp-3); }
.card__n { font-family: var(--font-mono); font-size: var(--size-label); font-weight: 500; letter-spacing: 0.08em; color: var(--accent); }
.card__plan { margin-top: auto; padding-top: var(--sp-2); }

/* --- Deux colonnes de document, séparées par un filet -------------------- */
.duo { display: grid; gap: var(--sp-8); }
.duo > * { min-width: 0; }
@media (min-width: 760px) {
  .duo { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-16); }
  .duo-col + .duo-col { padding-left: var(--sp-8); border-left: var(--border-w) solid var(--rule); margin-left: calc(-1 * var(--sp-8)); }
}
.duo-col { display: grid; gap: var(--sp-4); align-content: start; }

/* --- Schéma au trait ------------------------------------------------------
   Sous 760 px, le dessin ne se réduit pas — les libellés deviendraient
   illisibles. Il garde sa largeur et le lecteur le fait défiler. */
.schema-scroll { overflow-x: auto; }
/* Un dessin qui déborde sans le dire se lit comme une page cassée : le lecteur
   tombe sur son bord gauche et n'a aucune raison de soupçonner la suite. Deux
   indices, et pas un de plus : la phrase sous l'image, et le bord droit qui
   s'estompe. Rien au-dessus de 760 px, où rien ne déborde. */
.glisse { display: none; }
@media (max-width: 759px) {
  .glisse { display: block; margin-top: var(--sp-2); color: var(--ink-3); }
  .schema-scroll, .apercu__scroll {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
}
.schema { width: 100%; height: auto; display: block; fill: none; }
/* Une illustration posée en image à la place du dessin au trait. Ni filet ni
   ombre : le cadre dit « capture, donc preuve », et ne se prête pas à un
   dessin (§ 8). Son fond est déjà celui de la bande, elle s'y pose sans
   caisson. Plus étroite que la colonne de texte, comme l'illustration de
   tête : un dessin qui touche les deux gouttières se lit comme un bandeau, et
   un bandeau décore. C'est aussi ce qui tient sa hauteur. */
.schema-img { width: 100%; max-width: 896px; height: auto; display: block; margin-inline: auto; }
/* Le schéma défile plutôt que de rétrécir : à 680 px de large, un texte de
   15 px se rendait à 11,5 px, sous le plancher de lisibilité (brand.md § 8). */
@media (max-width: 759px) { .schema, .schema-img { min-width: 880px; } }
/* Appuyé, jamais ténu — brand.md § 8. Le trait d'un dessin qui porte
   l'information n'est pas le filet de 1 px qui sépare deux blocs. */
.schema .s-box { fill: var(--paper); stroke: var(--ink-2); stroke-width: 2; }
.schema .s-box--core { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 2.5; }
.schema .s-line { stroke: var(--ink-2); stroke-width: 2; fill: none; }
.schema .s-arrow { stroke: var(--accent); stroke-width: 2.5; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.schema .s-t { font-family: var(--font-sans); font-size: 15px; fill: var(--ink); }
.schema .s-t--mut { fill: var(--ink-2); font-size: 13px; }
.schema .s-seg { fill: none; stroke-width: 4; stroke-linecap: round; }
.schema .s-seg--sur { stroke: var(--accent); }
.schema .s-seg--incertain { stroke: var(--ink-3); stroke-dasharray: 2 7; }
.schema .s-pt { fill: var(--paper); stroke: var(--ink-2); stroke-width: 2.5; }
.schema .s-pt--plein { fill: var(--accent); stroke: var(--accent); }
.schema .s-bar { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 2; }
.schema .s-num { font-family: var(--font-sans); font-weight: 600; font-size: 21px; fill: var(--ink); font-variant-numeric: tabular-nums; }
.schema .s-t--lbl { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.08em; fill: var(--ink-2); }

/* --- Étapes --------------------------------------------------------------- */
.steps { display: grid; gap: var(--sp-6); counter-reset: step; }
@media (min-width: 560px) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 960px) { .steps { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-4); } }
.step { display: grid; gap: var(--sp-2); align-content: start; padding-top: var(--sp-4); border-top: 2px solid var(--ink); }
.step__n { font-family: var(--font-mono); font-size: var(--size-label); font-weight: 500; letter-spacing: 0.08em; color: var(--accent); }

/* --- Le procédé : les capacités, deux majeures puis trois mineures --------
   La composition d'allo.fr : la première rangée porte deux cartes larges avec
   leur illustration, la seconde trois cartes courtes. Ce n'est pas un rythme
   décoratif — les deux premières capacités sont celles qu'il faut voir pour
   comprendre ce que fait l'agent, les trois autres se lisent une fois qu'on
   a compris.                                                                 */
.capacites { display: grid; gap: var(--sp-6); }
.capacites--majeures { margin-bottom: var(--sp-6); }
@media (min-width: 760px) {
  .capacites { gap: var(--sp-8); }
  .capacites--majeures { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: var(--sp-8); }
  .capacites--mineures { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Un filet, aucun caisson : un fond de carte sur le champ vert mettrait deux
   verts côte à côte (1,30:1, § 3). */
.capacite {
  display: grid; gap: var(--sp-3); align-content: start;
  padding: var(--sp-6);
  border: var(--border-w) solid var(--accent-soft); border-radius: var(--r-card);
}
.capacite .ph { margin-top: var(--sp-3); }
/* L'illustration se pose sans caisson ni filet : l'encadrer, c'est la présenter
   comme une capture, et la charte l'interdit tant que c'est un dessin (§ 8).
   Son champ est déjà celui de la bande, le rayon suffit à la tenir. */
.capacite__img {
  display: block; width: 100%; height: auto;
  margin-top: var(--sp-3); border-radius: var(--r-card);
}
/* Le cadre d'attente, retourné sur le champ sombre : mêmes tirets, même
   libellé entre crochets, la couleur seule change (§ 8). */
.band--vert .ph { background: transparent; border-color: var(--accent-soft); }
.band--vert .ph .t-data { color: var(--accent-soft); }
.procede__limite { margin-top: var(--sp-8); max-width: 68ch; }

/* --- Cartes de forfait ---------------------------------------------------- */
.plans { display: grid; gap: var(--sp-4); align-items: stretch; }
@media (min-width: 880px) { .plans { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.plan-card { display: flex; flex-direction: column; gap: var(--sp-4); background: var(--paper); }
.band--p2 .plan-card, .band--p3 .plan-card { background: var(--paper); }
.plan-card__price { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
/* Le trait qui encercle dépasse le chiffre de 0,14 em de chaque côté
   (§ Marque) : avec l'écart courant de 8 px, « par employé » se posait sur la
   courbe. C'est la seule chose que le trait demande à son porteur — la place
   qu'il prend. L'écart reste à 24 px depuis que la marge est passée de 0,3 à
   0,14 em : le trait n'en demande plus que 6, mais un prix entouré supporte
   d'avoir de l'air autour, et on ne resserre pas ce qui ne gêne pas. */
.plan-card__price:has(.marque) { gap: var(--sp-6); }
.plan-card__socle { color: var(--ink-3); }
.plan-card__excl {
  padding-top: var(--sp-4);
  border-top: var(--border-w) solid var(--rule);
  display: grid; gap: var(--sp-2);
}
.plan-card__foot { margin-top: auto; display: grid; gap: var(--sp-2); }

/* --- Panneau « tout est compris » ----------------------------------------
   Un seul bloc large qui liste l'intégralité du socle, en colonnes denses.
   Le lecteur doit pouvoir constater d'un coup d'œil qu'il n'y a pas de
   version bridée — c'est l'inverse d'une carte à cinq puces. */
.panel {
  background: var(--paper);
  border: var(--border-w) solid var(--rule);
  border-radius: var(--r-card);
  padding: var(--sp-6);
  display: grid; gap: var(--sp-6);
}
@media (min-width: 760px) { .panel { padding: var(--sp-8); } }
/* La liste dense, sous les lignes commentées : quatre colonnes de lignes
   courtes. Son travail est le nombre — on doit voir d'un coup d'œil qu'il n'y
   a pas de version bridée, pas lire chaque ligne. D'où le corps small et le
   tiret cadratin, comme la liste au-dessus : c'est la même liste, à une autre
   échelle de lecture, pas un second composant. */
.panel__dense {
  display: grid; gap: var(--sp-2) var(--sp-6);
  padding-top: var(--sp-6);
  border-top: var(--border-w) solid var(--rule);
}
@media (min-width: 560px) { .panel__dense { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 960px) { .panel__dense { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.panel__dense li { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2); }
.panel__dense li::before { content: "\2014"; color: var(--accent); }
/* Repliée derrière un dépliable -- le cas de l'accueil, où la bande n'est
   qu'une étape du parcours. Le filet et le chevron du dépliable remplacent le
   filet de la liste : deux traits l'un sur l'autre feraient un cadre. */
.panel__repli > summary { font-size: var(--size-body); }
.panel__repli .panel__dense { padding-top: 0; border-top: 0; }

.panel__head {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: space-between; gap: var(--sp-6);
  padding-bottom: var(--sp-6);
  border-bottom: var(--border-w) solid var(--rule);
}
.panel__head > div { display: grid; gap: var(--sp-2); max-width: 46ch; }
.panel__list { display: grid; gap: var(--sp-4); }
@media (min-width: 700px)  { .panel__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--sp-12); } }
@media (min-width: 1000px) { .panel__list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.panel__list li { position: relative; padding-left: var(--sp-6); }
.panel__list li::before { content: "\2014"; position: absolute; left: 0; color: var(--accent); }
.panel__excl { border-top: var(--border-w) solid var(--rule); padding-top: var(--sp-6); display: grid; gap: var(--sp-2); }

/* Le détail d'une carte de forfait, et le panneau « tout ce qui est compris ».
   Puces en tiret cadratin accent — jamais de coche verte (brand.md § 6). */
.card__list { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.card__list li { position: relative; padding-left: var(--sp-6); color: var(--ink-2); }
.card__list li::before { content: "\2014"; position: absolute; left: 0; color: var(--accent); }
.card__list strong { color: var(--ink); font-weight: 600; }
.card__more { padding-top: var(--sp-2); }
/* La carte a cinq lignes : prix, nom, résumé, détail, compte. Le détail
   absorbe le mou pour que le compte s'aligne d'une carte à l'autre. */
.card:has(> .card__more) { grid-template-rows: auto auto auto 1fr auto; }
.card:has(> .card__more) > .card__list { align-content: start; }

.incl { display: grid; gap: var(--sp-6); }
@media (min-width: 760px) {
  .incl { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--sp-8); }
}
.incl__group { display: grid; gap: var(--sp-3); align-content: start; }
.incl__group > .t-label { padding-bottom: var(--sp-2); border-bottom: var(--border-w) solid var(--rule); }
@media (min-width: 880px) { .plans--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* --- Dépliables : le détail et la technique, repliés par défaut ------------ */
.disc { border-top: var(--border-w) solid var(--rule); }
.discs { display: grid; }
.discs .disc:last-child { border-bottom: var(--border-w) solid var(--rule); }
.discs > .disc > summary { font-size: var(--size-body); padding-block: var(--sp-4); }
.disc > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding-block: var(--sp-3);
  cursor: pointer; list-style: none;
  font-size: var(--size-small); font-weight: 600; color: var(--ink);
}
.disc > summary::-webkit-details-marker { display: none; }
.disc > summary::after {
  content: ""; margin-left: auto; flex: none;
  width: 8px; height: 8px;
  border-right: 1.5px solid var(--ink-2); border-bottom: 1.5px solid var(--ink-2);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-control) var(--ease-state);
}
.disc[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.disc > summary:hover { color: var(--accent); }
.disc__body { padding-bottom: var(--sp-4); display: grid; gap: var(--sp-3); }
.disc--tech .disc__body { padding-bottom: var(--sp-2); }

.kv { display: grid; gap: var(--sp-2); font-family: var(--font-mono); font-size: var(--size-data); line-height: 1.45; }
.kv div { display: grid; gap: var(--sp-1); }
.kv dt { color: var(--ink-3); }
.kv dd { color: var(--ink); }

/* --- Citations ------------------------------------------------------------ */
.quotes { display: grid; gap: var(--sp-8); }
@media (min-width: 760px)  { .quotes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Huit citations en DEUX LIGNES de quatre (24 septembre 2026). Le mur cesse
   d'être une colonne à parcourir pour devenir un bloc qu'on embrasse d'un
   regard : c'est un constat, il doit se prendre d'un coup. La colonne
   rétrécissant de 350 à 250 px, le corps descend d'un cran de l'échelle,
   sinon les citations longues font huit lignes de quatre mots. */
@media (min-width: 1000px) { .quotes { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-6) var(--sp-8); } }
@media (min-width: 1000px) { .quotes .quote { font-size: var(--size-body); line-height: 1.5; } }
/* La source se cale en bas de la colonne (24 septembre 2026) : sur une grille,
   les citations n'ont pas la même longueur, et une source qui flotte à la
   hauteur où le texte s'arrête donne une ligne de base en dents de scie. Alignées
   en pied, elles redeviennent une rangée qu'on lit d'un seul regard. */
.quote { display: flex; flex-direction: column; }
.quote cite { display: block; font-style: normal; margin-top: auto; padding-top: var(--sp-4); }

/* --- Statu quo ------------------------------------------------------------ */
.sq { display: grid; gap: var(--sp-12); }
/* Trois constats côte à côte ne tiennent que si la gouttière se voit : sous
   une gouttière large, l'œil lit trois colonnes ; sous une gouttière étroite,
   il lit un bloc de texte. La colonne s'arrête donc avant le bord du gabarit,
   et c'est le vide entre les colonnes qui prend la place rendue. */
@media (min-width: 760px) {
  .sq { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--sp-6); }
}
/* La gouttière ne s'élargit qu'une fois la colonne assez large pour la rendre :
   creusée trop tôt, elle réduit les colonnes à des tours de trois mots. */
@media (min-width: 900px)  { .sq { column-gap: var(--sp-12); } }
@media (min-width: 1080px) { .sq { column-gap: var(--sp-16); } }
/* Constat, explication, verbatim — les trois étages d'un constat. */
.sq-item { display: grid; grid-template-rows: auto 1fr auto; gap: var(--sp-3);
           color: var(--ink-2); }
.sq-item h3 { color: var(--ink); }
/* Les trois constats ne font une ligne que si leurs étages sont solidaires.
   Chaque constat étant sa propre grille, ses rangées ne se partagent pas :
   un titre qui passait sur deux lignes décalait le texte et le filet de sa
   seule colonne, et le 1fr ne rattrapait rien — il se contentait de coller
   les citations en bas, si bien que les trois filets verts démarraient à
   trois hauteurs. Le subgrid fait partager les trois rangées aux trois
   colonnes : les textes commencent sur la même ligne, les filets aussi, et
   seule leur longueur diffère — ce qui est la seule chose qu'ils doivent
   dire. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 760px) {
    .sq { row-gap: var(--sp-3); }
    .sq-item { grid-row: span 3; grid-template-rows: subgrid; }
    /* Sans verbatim, le constat n'a que deux étages : lui en faire couvrir
       trois laisserait une rangée vide et sa gouttière en bas de bande. */
    .sq:not(:has(.quote)) .sq-item { grid-row: span 2; }
  }
}

/* --- Tableaux ------------------------------------------------------------- */
.table-scroll { overflow-x: auto; }
.table th:not(:first-child) { text-align: left; }
.table--text td:not(:first-child) { text-align: left; }
.table--num th:not(:first-child), .table--num td:not(:first-child) { text-align: right; }
.table td:first-child { color: var(--ink); }
.table tbody th {
  font-family: var(--font-sans); font-size: var(--size-body); font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink);
  border-bottom: var(--border-w) solid var(--rule);
}
.band--p2 .table tbody tr:nth-child(even),
.band--p3 .table tbody tr:nth-child(even) { background: transparent; }
.band--p2 .table tbody tr:nth-child(even) { background: var(--paper); }

/* --- Tableau comparatif ---------------------------------------------------
   Plusieurs situations en regard, la nôtre comprise. Sa colonne porte le fond
   d'accent doux, et c'est la seule couleur du tableau : les marques restent
   en `ink-2` comme tous les pictogrammes (§ 8), et c'est la forme — coche ou
   croix — qui porte la réponse. Le fond ne dit pas qui gagne, il dit où
   regarder. Le fond posé sur la cellule et non sur la rangée passe par-dessus
   la zébrure sans avoir à la surcharger. */
.table--compare th, .table--compare td { vertical-align: top; }
/* Les colonnes comparées pèsent le même poids : sans cette largeur, la
   nôtre — la plus brève, puisqu'elle n'a rien à expliquer — sortait la plus
   étroite des trois, et le tableau donnait raison à ses voisines par la seule
   place qu'il leur laissait. La règle ne vaut que pour un tableau qui désigne
   une colonne : un comparatif sans camp (le périmètre du forfait) garde la
   répartition automatique, qui remplit mieux la largeur. */
.table--compare:has(.is-notre) thead th:not(:first-child) { width: 26%; }
.table--compare thead th:not(:first-child) { min-width: 9rem; }
.table--compare tbody th { min-width: 8rem; }
.table--compare .is-notre { background: var(--accent-soft); }
.table--compare thead .is-notre { color: var(--accent); }
/* `.mark` est déjà le marqueur chiffré des repères : la réponse d'un
   comparatif porte donc son propre nom. */
.reponse { display: inline-flex; }
.ico--reponse { width: 18px; height: 18px; }

/* --- Bloc technique ------------------------------------------------------- */
.tech { border-radius: var(--r-document); min-width: 0; container-type: inline-size; }
.band--p2 .tech { background: var(--paper); }
.stack-list { display: grid; gap: var(--sp-3); }
@container (min-width: 620px) {
  .stack-list { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); gap: var(--sp-3) var(--sp-8); }
  .stack-row { display: contents; }
}
.stack-row { display: grid; gap: var(--sp-1); }
.stack-row dt { color: var(--ink-3); }
.stack-row dd { color: var(--ink); }

/* --- Personnes, captures, marqueurs --------------------------------------- */
/* Un seul cliché, l'équipe entière, sur toute la largeur de la bande. Deux
   cadres 4:5 côte à côte donnaient une fiche d'annuaire : chacun dans son
   caisson, et rien qui dise qu'ils travaillent ensemble. La photo prend le
   rayon des caissons et rien d'autre -- ni filet ni ombre : c'est une preuve,
   pas une capture, et § 8 ne l'encadre pas. */
.equipe { display: grid; margin: 0; }
.equipe__img { display: block; width: 100%; height: auto; border-radius: var(--r-card); }

.ph {
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: var(--paper-2);
  border: var(--border-w) dashed var(--rule-strong);
  border-radius: var(--r-card);
  padding: var(--sp-6);
  width: 100%; max-width: 100%; min-width: 0;
}
.band--p2 .ph, .band--p3 .ph { background: var(--paper); }
/* La proportion donne la hauteur à partir de la largeur, jamais l'inverse :
   sans width explicite, un contenu haut fait grossir la boîte en largeur. */
.ph--shot { aspect-ratio: 16 / 10; height: auto; }

/* L'aperçu du produit, quand il existe. Il prend toute la largeur de la bande
   — en demi-colonne, un écran de 2000 px rendrait ses libellés sous 6 px — et
   il défile plutôt que de rétrécir, comme les schémas. Ni filet ni caisson :
   encadrer l'image, c'est la présenter comme une capture, et tant que c'est un
   dessin la charte l'interdit (§ 8). Le rayon suffit à la tenir. */
.apercu-duo { display: grid; gap: var(--sp-8); align-items: start; }
/* L'aperçu prend la colonne large : c'est une image d'interface, et la moitié
   d'une bande ne lui laisserait pas de quoi se lire. La liste tient le tiers
   droit — quatre lignes courtes n'en demandent pas plus. */
@media (min-width: 900px) { .apercu-duo { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--sp-12); } }
.apercu { display: grid; min-width: 0; }
.apercu__scroll { overflow-x: auto; }
.apercu__img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-card);
}
@media (max-width: 759px) { .apercu__img { min-width: 880px; } }
/* La proportion d'un groupe : large, pas haut. */
.ph--equipe { aspect-ratio: 16 / 9; height: auto; }

/* Une illustration qui tient la place d'une capture. Ni filet ni ombre : la
   charte encadre la capture pour dire « c'est une preuve », et interdit
   d'encadrer l'illustration comme une capture (§ 8). Son champ est déjà le
   papier du système, donc elle se pose sur la bande sans caisson ; le rayon
   suffit à la tenir. La légende la nomme pour ce qu'elle est. */
.illus {
  display: grid;
  /* Plus étroite que la colonne de texte : un dessin qui touche les deux
     gouttières se lit comme un bandeau, et un bandeau décore. */
  max-width: 896px; margin-inline: auto; margin-bottom: var(--sp-12);
  /* Le dessin franchit la couture entre les deux bandes, et la couture passe
     au milieu du logo : c'est lui qui tient les deux fonds ensemble. 99 px,
     c'est la moitié du logo mesurée sur l'image rendue à 896 px de large. */
  margin-top: calc(-1 * (var(--sp-12) + 99px));
}
@media (min-width: 760px) { .illus { margin-top: calc(-1 * (var(--sp-24) + 99px)); } }
.illus__scroll { overflow-x: auto; }
.illus__img {
  display: block; width: 100%; height: auto;
}
/* Comme les schémas tracés : l'image défile plutôt que de rétrécir. Réduite à
   la largeur d'un téléphone, la mention « Mise à jour automatique logiciel »
   se rendrait sous 8 px, très loin du plancher de 15 px (brand.md § 8). */
@media (max-width: 759px) { .illus__img { min-width: 880px; } }

/* La bande qui précède lui fait de la place : sans cela le dessin, en montant
   chercher la couture, vient toucher la dernière ligne de l'accroche. */
.band:has(+ .band .illus) { padding-bottom: calc(var(--sp-12) + 104px); }
@media (min-width: 760px) { .band:has(+ .band .illus) { padding-bottom: calc(var(--sp-24) + 64px); } }

/* Quand l'illustration a sa variante portrait, le téléphone ne fait plus rien
   défiler : c'est la composition en colonne qui s'affiche, à la largeur de la
   page. Les deux images sont dans le HTML et c'est d'ici qu'on choisit —
   `<picture media>` aurait été l'outil prévu pour ça, mais le navigateur ne
   redescend jamais sur une source plus étroite une fois qu'il a choisi (voir
   lib/sections.mjs). Elle ne franchit pas non plus la couture : la marque
   n'est plus à mi-hauteur de l'image, il n'y a plus deux fonds à tenir
   ensemble, et une colonne de 866 px qui remonterait de 99 px n'irait
   chercher que du vide. */
.illus__img--colonne { display: none; }
@media (max-width: 759px) {
  .illus--portrait { margin-top: 0; margin-bottom: var(--sp-8); }
  .illus--portrait .illus__scroll { overflow-x: visible; }
  .illus--portrait .illus__img--large { display: none; }
  .illus--portrait .illus__img--colonne {
    display: block;
    /* Plafonnée : à 759 px de large elle atteindrait 1 800 px de haut, et une
       illustration ne prend pas trois écrans. */
    min-width: 0; max-width: 420px; margin-inline: auto;
  }
  .band:has(+ .band .illus--portrait) { padding-bottom: var(--sp-12); }
}
/* Sur l'accueil, les portraits sont un aperçu : À propos les montre en grand. */

.todo {
  display: block;
  font-family: var(--font-mono); font-size: var(--size-data); line-height: 1.45;
  color: var(--ink);
  background: var(--paper-3);
  border: var(--border-w) dashed var(--rule-strong);
  border-radius: var(--r-document);
  padding: var(--sp-3) var(--sp-4);
}
.band--p3 .todo { background: var(--paper-2); }

/* --- Simulateur ----------------------------------------------------------- */
.sim { display: grid; gap: var(--sp-8); align-items: start; }
@media (min-width: 880px) { .sim { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-16); } }
.sim-controls { display: grid; gap: var(--sp-6); }
.field { display: grid; gap: var(--sp-2); max-width: 288px; }
.field input {
  font-family: var(--font-sans); font-size: var(--size-body); font-weight: 500;
  font-variant-numeric: tabular-nums;
  height: 40px; padding-inline: var(--sp-3);
  color: var(--ink); background: var(--paper);
  border: var(--border-w) solid var(--rule-strong);
  border-radius: var(--r-control);
  width: 100%;
  transition: border-color var(--dur-control) var(--ease-state);
}
.field input:hover { border-color: var(--ink-3); }
.opts { display: grid; border: 0; padding: 0; margin: 0; min-inline-size: 0; }
.opts legend { padding: 0; }
.opt {
  display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content;
  gap: var(--sp-3); align-items: start;
  padding-block: var(--sp-4);
  border-top: var(--border-w) solid var(--rule);
  cursor: pointer;
}
.opt:hover { color: var(--ink); }
.opt input { margin: 0; width: 16px; height: 16px; accent-color: var(--accent); color: var(--ink); border-color: var(--rule-strong); }
.opt--locked { color: var(--ink-3); cursor: default; }
.opt__price { font-variant-numeric: tabular-nums; }

.sim-total {
  background: var(--paper);
  border: var(--border-w) solid var(--rule-strong);
  border-radius: var(--r-card);
  padding: var(--sp-6);
  display: grid; gap: var(--sp-4); justify-items: start;
}
.sim-total__value { transition: opacity var(--dur-control) var(--ease-state); }
.sim-total.is-updating .sim-total__value { opacity: 0.55; }
.sim-lines { display: grid; gap: var(--sp-2); width: 100%; }
.sim-line { display: flex; justify-content: space-between; gap: var(--sp-4); font-variant-numeric: tabular-nums; }
.sim-line--sum { border-top: var(--border-w) solid var(--rule-strong); padding-top: var(--sp-2); }

/* L'économie estimée. Délibérément PLUS PETITE que le total (h2 contre price) :
   le chiffre du contrat reste le chiffre principal, l'économie est un argument
   qui s'y ajoute. La bande de preuve, elle, n'était pas utilisable ici -- elle
   impose de publier la source (brand.md § 6), et l'arbitrage du 24 septembre
   2026 est justement de ne pas publier la base de comparaison. */
.sim-eco {
  width: 100%;
  display: grid; gap: var(--sp-1);
  border-top: var(--border-w) solid var(--rule);
  padding-top: var(--sp-4);
}
.sim-eco__value { font-size: var(--size-h2); color: var(--accent); }

/* --- Contact : le rendez-vous et les canaux --------------------------------
   Deux gestes, et plus de formulaire depuis le 18 septembre 2026 : l'agenda
   embarqué à gauche, les canaux directs à droite. On ne demande pas cinq
   champs à quelqu'un qui veut poser une question. */
.contact-duo { display: grid; gap: var(--sp-8); align-items: start; }
/* Le bloc du rendez-vous est le plus large : il porte un objet de 320 px de
   plancher et de 700 px de haut, l'autre porte trois lignes. À largeur égale,
   le calendrier flottait au milieu de sa colonne et les trois canaux nageaient
   dans la leur — ce n'est pas le rang qui change, c'est ce qu'il y a dedans. */
@media (min-width: 900px) { .contact-duo { grid-template-columns: 1.6fr 1fr; gap: var(--sp-12); } }
.contact-bloc { display: grid; gap: var(--sp-4); align-content: start; }
.contact-bloc + .contact-bloc { padding-top: var(--sp-8); border-top: 2px solid var(--ink); }
@media (min-width: 900px) {
  .contact-bloc + .contact-bloc { padding-top: 0; padding-left: var(--sp-12); border-top: 0; border-left: var(--border-w) solid var(--rule-strong); }
}
.canaux { display: grid; gap: var(--sp-2); }
/* Le canal est une ligne entière cliquable, pas un mot souligné au milieu
   d'une phrase : sur un téléphone, la cible doit faire la largeur du bloc. */
.canal {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-4); align-items: center;
  padding: var(--sp-4) 0;
  border-bottom: var(--border-w) solid var(--rule);
  text-decoration: none; color: var(--ink);
}
.canaux li:first-child .canal { border-top: var(--border-w) solid var(--rule); }
.canal:hover { color: var(--accent-hover); }
.canal:hover strong { text-decoration: underline; text-underline-offset: 3px; }
.canal .ico { width: 20px; height: 20px; color: var(--ink-2); }
.canal:hover .ico { color: var(--accent); }
.canal > span { display: grid; gap: 2px; }
.canal .t-small { color: var(--ink-2); }

/* --- Appel à l'action et pied de page -------------------------------------- */
/* Le texte de l'appel à l'action garde sa mesure de lecture ; la bande, elle,
   garde la gouttière et la largeur de toutes les autres. Les deux ont été
   confondus une heure le 18 septembre 2026 : élargir la mesure en posant
   `max-width: none` sur l'élément qui portait AUSSI la classe `wrap` a retiré
   les 1152 px de la bande entière, et le contact s'est collé au bord de
   l'écran pendant que le reste du site restait centré. La mesure et la
   largeur de page ne doivent pas vivre sur le même élément. */
.cta { display: grid; gap: var(--sp-6); max-width: 68ch; }
.cta + .contact-duo { margin-top: var(--sp-12); }

/* Le pied de page est le seul aplat d'accent plein du site (arbitrage du
   15/09/2026). Le wordmark y prend sa seconde version autorisée : papier
   sur vert anglais, carré compris. */
.site-foot {
  background: var(--accent); color: var(--on-accent);
  border-top: 0; padding-block: var(--sp-16);
}
.site-foot .wordmark { color: var(--on-accent); }
.site-foot .lede { color: var(--accent-soft); }
.site-foot .todo {
  background: transparent; color: var(--accent-soft);
  border-color: var(--on-accent);
}
.foot-legal { color: var(--accent-soft); }
.foot-inner { display: grid; gap: var(--sp-8); }
@media (min-width: 760px) { .foot-inner { grid-template-columns: 1.4fr 1fr; } }
.foot-nav ul { display: grid; gap: var(--sp-2); }
.foot-nav a { color: var(--accent-soft); text-decoration: none; font-size: var(--size-small); }
.foot-nav a:hover { color: var(--on-accent); text-decoration: underline; }
.foot-nav .t-label { color: var(--accent-soft); }
.site-foot :where(a):focus-visible { outline-color: var(--on-accent); }

/* --- Entrées : l'arrivée d'un élément, jouée une seule fois ----------------
   24 px, 600 ms, sur la courbe d'entrée (tokens.css § Mouvement). Les valeurs
   ne sont pas écrites ici : ce sont des jetons, comme une couleur.

   Ce n'est pas la bande qui entre, ce sont ses éléments, chacun quand il
   franchit le pli. Ceux qui arrivent ensemble — une rangée de cartes qui se
   découvre d'un coup — se décalent de 60 ms : l'ordre de lecture rendu
   visible, le titre puis ce qu'il annonce. Le décalage est posé par le
   script, en ligne, parce qu'il dépend de ce qui arrive dans la même passe
   et non d'un rang écrit d'avance ; il repart à zéro à chaque bande.

   Une grille ne bouge pas elle-même : ce sont ses cartes qui entrent. D'où
   les deux sélecteurs, répétés à l'identique pour l'état d'arrivée. La liste
   des grilles est fermée, et le script tient la même.                        */
.js .reveal > :not(:is(.cards, .duo, .steps, .capacites, .plans, .marks, .quotes, .sq)),
.js .reveal > :is(.cards, .duo, .steps, .capacites, .plans, .marks, .quotes, .sq) > * {
  opacity: 0; transform: translateY(var(--rise-entry));
  transition: opacity   var(--dur-entry) var(--ease-entry),
              transform var(--dur-entry) var(--ease-entry);
}
.js .reveal > :not(:is(.cards, .duo, .steps, .capacites, .plans, .marks, .quotes, .sq)).is-in,
.js .reveal > :is(.cards, .duo, .steps, .capacites, .plans, .marks, .quotes, .sq) > .is-in {
  opacity: 1; transform: none;
}

/* Le dessin monte plus longuement, et après le reste de sa bande : il arrive
   une fois la page posée, au lieu de se lever avec elle. Une seule fois, au
   passage — « rien ne bouge en continu » vise la boucle, pas l'entrée. Son
   retard est écrit ici, et le script ne le remplace pas.                     */
.js .reveal .illus {
  opacity: 0; transform: translateY(var(--rise-illus));
  transition: opacity   var(--dur-illus) var(--ease-entry) var(--delay-illus),
              transform var(--dur-illus) var(--ease-entry) var(--delay-illus);
}
.js .reveal .illus.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js .reveal, .js .reveal > *, .js .reveal > * > * { opacity: 1 !important; transform: none !important; transition: none !important; }
  .sim-total.is-updating .sim-total__value { opacity: 1 !important; }
  .disc > summary::after { transition: none !important; }
}

/* --- Impression : le test du document -------------------------------------- */
@media print {
  .skip, .site-nav, .head-cta, .cta-row, .card__plan a { display: none !important; }
  .band { padding-block: var(--sp-8); break-inside: avoid; }
  .band--p2, .band--p3, .band--soft { background: none; border-top: var(--border-w) solid var(--rule); }
  /* Le champ vert coûterait une page d'encre : sur le papier, le procédé
     redevient un document, comme le pied de page. */
  .band--vert { background: none; color: var(--ink); border-top: var(--border-w) solid var(--rule); }
  .band--vert .t-label, .band--vert .lede, .band--vert .t-small { color: var(--ink-2); }
  .capacite { border-color: var(--rule-strong); }
  .band--vert .ph { border-color: var(--rule-strong); }
  .band--vert .ph .t-data { color: var(--ink-2); }
  .site-foot { background: none; color: var(--ink); border-top: var(--border-w) solid var(--rule); }
  .site-foot .wordmark { color: var(--ink); }
  .logo--foot { filter: invert(1) grayscale(1) contrast(3); }
  .site-foot .lede, .site-foot .foot-nav a, .foot-legal { color: var(--ink-2); }
  .card, .plan-card, .tech, .sim-total { background: none; }
  .disc__body { display: block !important; }   /* tout est déplié sur le papier */
}

/* --- Lignes des offres : un titre, puis sa phrase -------------------------
   Sur le socle comme sur les briques, le gras ouvre sa propre ligne et la
   phrase passe en gris, un cran plus bas. L'œil lit les titres en colonne,
   et ne descend dans le détail que là où il s'arrête. */
.panel__list li strong,
.plan-card .list-dash li strong { display: block; color: var(--ink); margin-bottom: var(--sp-1); }
.panel__list li,
.plan-card .list-dash li { color: var(--ink-2); line-height: 1.55; }
.panel__list { row-gap: var(--sp-6); }
.plan-card .list-dash { gap: var(--sp-4); }
