/*
 * Feuille de style de la vitrine NVBES.
 *
 * Les tokens ci-dessous sont repris tels quels de la Charte Merkaba (Notion),
 * qui est la source de vérité : ne pas inventer de valeur ici, l'ajouter d'abord
 * à la charte. Trois règles de la charte gouvernent tout le reste :
 *   1. le fond de page est #F9F9F8, jamais du blanc pur ;
 *   2. l'or ne paraît qu'au bouton d'appel à l'action et sur un seul mot du
 *      titre principal — nulle part ailleurs ;
 *   3. aucune ombre décorative, bordures à 0.5px, DM Sans en 400 et 500 seulement.
 *
 * La règle 3 garde ses 0.5px, mais la COULEUR du contour a bougé deux fois.
 * Le 2026-09-04, `--color-border` est passé de l'ivoire 300 à l'ardoise 400 :
 * 1,18:1 sur le fond de page, là où WCAG 1.4.11 demande 3:1 pour le contour
 * d'un élément d'interface. Le 2026-09-07, ce token unique a été scindé en
 * trois rôles — voir le bloc de tokens, qui porte le raisonnement. Retenir
 * une chose : **1.4.11 ne vaut que pour ce qu'on peut viser**. L'appliquer à
 * un filet de séparation ou à une illustration n'accessibilise rien et charge
 * la page. Amendements datés dans la Charte.
 *
 * La règle 3 porte une exception, et une seule, amendée dans la charte le
 * 2026-08-28 : l'aperçu de site (`.mockup`) figure un écran, et son ombre dit
 * qu'il est une surface distincte de la page — ce n'est pas de la décoration.
 * Elle ne s'étend à aucun autre élément : cards, boutons, badges et champs
 * restent sans ombre.
 */

:root {
  /* Couleurs de base */
  --slate-900: #0D1620;
  --slate-800: #152030;
  --slate-700: #1C2A3A;
  --slate-600: #2A3E55;
  --slate-500: #4A6888;
  --slate-400: #6A8BAA;
  --slate-300: #8AAFC8;
  --slate-200: #B0CCE0;
  --slate-100: #D4E4F0;
  --slate-50:  #EEF4FA;

  --gold-500: #A8903A;
  --gold-400: #C0A852;

  --ivory-800: #4A4838;
  --ivory-700: #607888;
  --ivory-600: #8A8070;
  --ivory-400: #C8D8E8;
  --ivory-300: #DDE8F0;
  --ivory-200: #EEF2F8;
  --ivory-100: #F4F6F8;
  --ivory-50:  #F9F9F8;

  /* Tokens sémantiques */
  --color-bg-page:        var(--ivory-50);
  /*
   * ⚠️ Deux tokens là où il n'y en avait qu'un, et le nom mentait : jusqu'au
   * 2026-09-09, `--color-bg-card` ne servait qu'au `.notice`, tandis que
   * `.card` écrivait `var(--ivory-50)` en dur — c'est-à-dire EXACTEMENT le
   * fond de page. Un bloc de l'espace client ne se distinguait donc de son
   * fond que par un trait de 0,5px, ce qui revient à ne pas s'en distinguer.
   *
   * Valeur arrêtée par Sloan le 2026-09-09 sur panneau d'arbitrage, supprimé
   * depuis : `--ivory-100`, soit 1,03:1 avec le fond de page. Un écart
   * volontairement ténu — il ne s'agit pas de découper la page en panneaux,
   * seulement de donner un corps aux blocs pour que leur contour cesse d'être
   * la seule chose qui les distingue.
   */
  --color-bg-card:        var(--ivory-100);
  --color-bg-notice:      var(--ivory-200);
  --color-bg-surface:     var(--slate-700);
  --color-text-primary:   var(--slate-700);
  --color-text-secondary: var(--ivory-700);
  --color-text-on-dark:   var(--ivory-50);
  /*
   * DEUX tokens de contour ici, un troisième sur `.mockup` — et c'est le point
   * de cette section.
   *
   * Le 2026-09-04, un seul `--color-border` a été porté de l'ivoire 300 à
   * l'ardoise 400 pour satisfaire WCAG 1.4.11 (3:1 pour le contour d'un
   * élément d'interface). La mesure était juste, et elle a été prise sur le
   * bon témoin : un champ de formulaire. Mais le token servait TROIS rôles à
   * la fois, et la valeur d'un seul a été appliquée aux trois — d'où trente
   * déclarations passées à l'ardoise, dont la plupart ne sont pas des
   * éléments d'interface.
   *
   * 1.4.11 couvre les « composants d'interface utilisateur » et les objets
   * graphiques nécessaires à la compréhension. Un filet sous l'entête, la
   * séparation de deux questions dans une FAQ, le cadre d'une grille
   * tarifaire n'en sont ni l'un ni l'autre : leur disparition ne prive
   * personne d'une information ni d'une commande. Les tenir au même seuil
   * n'améliore aucune accessibilité, et charge la page de traits bleus.
   */

  /*
   * Contour STRUCTUREL : filets de section, cadres de surface, séparateurs,
   * cartes de l'espace client. Purement présentation, donc pas de plancher de
   * contraste à tenir. Il doit se voir juste assez pour dire « ça s'arrête
   * ici », et pas davantage.
   *
   * Valeur arrêtée le 2026-09-07 sur panneau d'arbitrage, supprimé depuis :
   * retour à l'ivoire 300, celle d'avant le 04.
   */
  --color-border:         var(--ivory-300);

  /*
   * Contour de CARTE, séparé du contour structurel le 2026-09-09.
   *
   * ⚠️ C'est le même défaut qu'en septembre, un cran plus bas : un seul token
   * servait les filets de section (posés sur le fond de page) ET le cadre des
   * cartes (posé autour d'un fond distinct). La valeur avait été arrêtée sur
   * les premiers, et personne n'avait regardé les secondes — d'où des blocs
   * qu'on ne voyait pas. Ce ne sont pas des composants d'interface au sens de
   * WCAG 1.4.11 : aucun plancher de contraste à tenir, il s'agit seulement de
   * dire où un bloc commence et où il s'arrête.
   *
   * Valeur arrêtée par Sloan le 2026-09-09, en même temps que le fond
   * ci-dessus : `--ivory-400`, soit 1,34:1 avec le fond de carte. Les deux se
   * sont jugés ensemble, un contour ne se réglant pas sans savoir sur quel
   * fond il se pose — c'est d'ailleurs pour les avoir jugés séparément que le
   * réglage précédent ne se voyait pas.
   */
  --color-border-card:    var(--ivory-400);

  /*
   * Contour de CONTRÔLE : ce que le visiteur peut viser, saisir ou ouvrir —
   * champs, boutons, cartes d'option, menus. C'est ici, et seulement ici, que
   * WCAG 1.4.11 s'applique. `--slate-400` est la seule valeur de la palette à
   * atteindre 3:1 sur le fond de page (3,39:1) ; `--slate-300` plafonne à
   * 2,20:1. L'épaisseur reste à 0,5px : c'est le contraste qui manquait, pas
   * le trait. La valeur du 2026-09-04 est donc conservée telle quelle — c'est
   * son PÉRIMÈTRE qui se réduit, pas son chiffre.
   * ⚠️ Amendement à porter dans la Charte Merkaba, qui reste la source.
   */
  --color-border-control: var(--slate-400);

  --color-accent:         var(--gold-500);

  /* Typographie */
  --ff-display: 'Lora', Georgia, 'Times New Roman', serif;
  --ff-ui: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /*
   * Échelle typographique. Aucune taille ne s'écrit en dur ailleurs dans ce
   * fichier — hors `.pane__*`, qui figure l'écran d'un AUTRE produit et garde
   * donc sa propre échelle, indépendante de celle de la vitrine.
   *
   * Valeurs arrêtées le 2026-09-01, après essai comparatif en dev sur la page
   * entière. L'échelle d'origine (30/26/20/18/16/15/14/13/12/11) manquait de
   * corps : Lora, à empattements, paraît plus petite qu'une sans-serif à taille
   * égale. D'où +15 % partout — MOINS 10 % sur les seuls grands titres, qui ne
   * manquaient de rien : les grossir dans le même rapport n'aurait fait que
   * reconduire le déséquilibre en plus gros. L'écart titre/texte s'en trouve
   * resserré, ce qui est l'intention et non un effet de bord.
   *
   * `--fs-h3` est le seul rôle d'affichage EXCLU de ce −10 % : ce n'est pas un
   * grand titre mais un titre de carte, posé juste au-dessus de son paragraphe
   * (garanties, fonctionnalités). À −10 % il passait SOUS `--fs-body` — un
   * intitulé plus petit que le texte qu'il annonce.
   *
   * Arrondi au pixel entier : le calcul exact tombait sur des demi-pixels, que
   * le rendu ne distingue pas et qu'une charte ne sait pas énoncer.
   *
   * EN REM, ET JAMAIS EN PX. Le rem se lit sur la taille de police par défaut
   * du navigateur : un visiteur qui a agrandi la sienne voit la page grandir
   * avec lui. En px, ce réglage est purement ignoré — et la cible de cette
   * vitrine est précisément celle qui l'a déjà poussé. Le zoom de page reste
   * possible, mais il agrandit tout, y compris ce qui n'en avait pas besoin.
   *
   * Le px indiqué en commentaire est la valeur rendue au réglage par défaut
   * (16px), qui est celle qu'énonce la charte. Les fractions sont exactes,
   * seizième par seizième : aucun arrondi ne s'ajoute à celui ci-dessus.
   *
   * Pas de `html { font-size: 62.5% }` pour se donner 1rem = 10px : cette
   * bidouille rabaisse le réglage du visiteur avant de le multiplier, et le
   * confort de calcul ne vaut pas ça.
   */
  --fs-hero:  1.9375rem; /* 31px */
  --fs-h2:    1.6875rem; /* 27px */
  --fs-step:  1.3125rem; /* 21px */
  --fs-lead:  1.1875rem; /* 19px */
  --fs-h3:    1.125rem;  /* 18px */
  --fs-body:  1.0625rem; /* 17px */
  --fs-ui:    1rem;      /* 16px */
  --fs-ui-sm: 0.9375rem; /* 15px */
  --fs-ui-xs: 0.875rem;  /* 14px */
  --fs-micro: 0.8125rem; /* 13px */

  /* Rayons */
  --radius-card: 12px;
  --radius-button: 6px;

  /* Élévation — réservée à l'aperçu de site, cf. exception en tête de fichier.
     Teintée ardoise et non noire : un noir neutre vire au gris sale sur ivoire.
     Deux couches — la courte donne le contact, la longue l'élévation ; une
     seule couche donnerait un halo flou au lieu d'un objet posé. */
  --shadow-lifted:
    0 1px 2px rgba(28, 42, 58, 0.04),
    0 10px 28px -6px rgba(28, 42, 58, 0.10);

  /*
   * Hauteur de l'entête collante, telle que la donnent ses paddings et son
   * logo (26 + 26 + 26 sur écran large, 16 + 28 + 16 en compacte). Ce qui
   * s'épingle sous elle s'en sert ; ⚠️ la changer ici sans changer les
   * paddings de `.header__logo` (ou l'inverse) fait glisser le texte des
   * étapes sous l'entête, ou le décolle trop. Relevé par la revue de la PR #87.
   */
  --header-h: 78px;

  /* Mise en page */
  --page-max: 1080px;
  --page-gutter: 40px;
}

/* --------------------------------------------------------------- Socle --- */

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

/*
 * Les ancres de navigation s'arrêtent SOUS l'entête, pas derrière lui.
 *
 * L'entête est `sticky` en haut de page : sans cette réserve, cliquer sur
 * « Comment ça marche » amène le titre de la section exactement là où l'entête
 * le recouvre, et le visiteur atterrit sur un contenu qui commence hors de sa
 * vue. Relevé le 2026-09-09 sur `#etapes`, mais le défaut vaut pour les trois
 * liens de la vitrine.
 *
 * Posé sur l'élément de défilement plutôt qu'en `scroll-margin-top` sur chaque
 * cible : c'est une propriété de la page, pas de chacune de ses sections, et
 * une ancre ajoutée demain en hérite sans qu'on y pense.
 *
 * 96px contre 79px d'entête mesurés : le reste est la marge qui évite que le
 * titre affleure le filet.
 */
html { scroll-padding-top: 96px; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-bg-page);
  color: var(--color-text-primary);
  font-family: var(--ff-ui);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--color-text-primary); text-decoration: none; }
a:hover { color: var(--slate-500); }
::selection { background: var(--slate-100); }

img { max-width: 100%; }

/*
 * Toute l'animation de cette page est de l'agrément. Quand le visiteur a
 * demandé à son système de réduire les animations, on la lui coupe — et on
 * force la révélation, sinon le contenu resterait invisible.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Visible seulement au clavier : le lien d'évitement. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--slate-700);
  color: var(--ivory-50);
  font-size: var(--fs-ui-sm);
  font-weight: 500;
  padding: 10px 20px;
  border-radius: 0 0 var(--radius-button) 0;
}
.skip-link:focus {
  left: 0;
  color: var(--ivory-50);
}

:focus-visible {
  outline: 2px solid var(--slate-500);
  outline-offset: 3px;
  border-radius: 2px;
}

.shell {
  max-width: var(--page-max);
  margin: 0 auto;
  padding-left: var(--page-gutter);
  padding-right: var(--page-gutter);
}

/* --------------------------------------------------------- Typographie --- */

.t-hero    { font-family: var(--ff-display); font-size: var(--fs-hero); font-weight: 500; line-height: 1.55; margin: 0; }
.t-h2      { font-family: var(--ff-display); font-size: var(--fs-h2); font-weight: 500; margin: 0; }
.t-h3      { font-family: var(--ff-display); font-size: var(--fs-h3); font-weight: 500; margin: 0; }
.t-step    { font-family: var(--ff-display); font-size: var(--fs-step); font-weight: 400; margin: 0; }
.t-lead    { font-family: var(--ff-display); font-size: var(--fs-lead); font-weight: 400; font-style: italic; line-height: 1.7; margin: 0; }
.t-body    { font-family: var(--ff-display); font-size: var(--fs-body); line-height: 1.75; margin: 0; }
.t-ui      { font-family: var(--ff-ui); font-size: var(--fs-ui); line-height: 1.65; margin: 0; }
.t-note    { font-family: var(--ff-ui); font-size: var(--fs-micro); color: var(--color-text-secondary); margin: 0; }

/* Une note qui suit un bouton lui collait : `.btn` est en ligne et `.t-note`
   n'a aucune marge. La règle vise l'adjacence plutôt que la note elle-même, qui
   sert ailleurs sans bouton au-dessus. */
.btn + .t-note { margin-top: 14px; }
.t-eyebrow {
  font-family: var(--ff-ui); font-size: var(--fs-micro); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--color-text-secondary); margin: 0;
}

/* Le seul or autorisé dans un titre : un mot, une fois. */
.t-accent { color: var(--color-accent); }

.balance { text-wrap: balance; }
.pretty  { text-wrap: pretty; }

/* ------------------------------------------------------------- Boutons --- */

.btn {
  display: inline-block;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 10px 20px;
  border: none;
  border-radius: var(--radius-button);
  cursor: pointer;
  transition: background 200ms ease, color 200ms ease;
}
.btn--primary { background: var(--slate-700); color: var(--ivory-50); }
.btn--primary:hover { background: var(--slate-800); color: var(--ivory-50); }
.btn--gold { background: var(--gold-500); color: var(--slate-700); }
/*
 * Au survol, les deux couleurs s'échangent au lieu de s'éclaircir. L'ancien
 * état (or plus clair, texte inchangé) se voyait à peine et perdait du
 * contraste au moment précis où l'on vise le bouton ; l'inversion se voit,
 * et le contraste augmente. `--gold-400` plutôt que `--gold-500` sur
 * l'ardoise : c'est la variante claire que la charte prévoit pour les fonds
 * sombres.
 * ⚠️ À confirmer dans la Charte Merkaba lors de la reprise de palette — la
 * charte décrit l'état au repos, pas le survol.
 */
.btn--gold:hover { background: var(--slate-700); color: var(--gold-400); }

/*
 * 🔴 L'inversion ne survit pas à un fond sombre : sur `--color-bg-surface`,
 * qui *est* `--slate-700`, le bouton prenait la couleur de la section et
 * disparaissait au survol — il ne restait que son libellé en or flottant.
 * Défaut introduit avec l'inversion le 2026-09-01, relevé par Sloan le
 * lendemain sur la section « À vous de jouer ».
 *
 * Sur fond sombre, le bouton garde donc son ancien survol : un or plus clair.
 * Ce qui le rendait faible sur fond ivoire — l'écart entre `--gold-500` et
 * `--gold-400` se perd contre une page claire — le rend au contraire net
 * contre l'ardoise, où toute la surface s'éclaircit d'un cran.
 */
.section--dark .btn--gold:hover {
  background: var(--gold-400);
  color: var(--slate-700);
}

/* -------------------------------------------------------------- Entête --- */

.header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(249, 249, 248, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 0.5px solid var(--color-border);
}
.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
/*
 * Le logo d'entête ne paraît qu'une fois celui du hero sorti de l'écran :
 * deux logos visibles en même temps, ce serait un doublon.
 */
.header__logo {
  display: block;
  padding: 26px 26px 26px 0;
  opacity: 0;
  transition: opacity 300ms ease;
  pointer-events: none;
}
.header__logo.is-visible { opacity: 1; pointer-events: auto; }
.header__logo img { display: block; width: 130px; height: auto; }

.nav { display: flex; align-items: center; gap: 28px; flex-wrap: wrap; padding: 12px 0; }
.nav__link {
  /*
   * Pilote à la fois le retrait du libellé et le départ du filet de survol.
   * Une seule valeur pour les deux : séparés, ils dérivent, et le filet part
   * alors sous la bordure de séparation au lieu de partir sous le texte.
   */
  --nav-filet-inset: 0px;

  position: relative;
  padding-left: var(--nav-filet-inset);
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  transition: color 200ms ease;
}

/*
 * Survol des liens d'entête : un filet apparaît sous le libellé.
 *
 * 🔴 Pas d'or ici, et ce n'est pas un oubli — règle 2 de la Charte Merkaba :
 * l'or ne paraît qu'au bouton d'appel à l'action et sur un mot du titre
 * principal, nulle part ailleurs. Le motif tient au signal : si le survol
 * apprend à l'œil que l'or veut dire « texte cliquable », il cesse de vouloir
 * dire « l'action qui convertit ». S'y ajoute le contraste — `--gold-500` sur
 * le fond ivoire donne 2,9:1, loin des 4,5:1 exigés pour du texte.
 *
 * Le filet plutôt qu'un soulignement de texte : sur des capitales à 0,08em
 * d'interlettrage, `text-decoration` colle aux jambages et salit.
 *
 * ⚠️ Et la couleur ne s'éclaircit plus au survol. Le `a:hover` global emmène
 * vers `--slate-500`, plus clair que le texte au repos : le lien s'affaiblit
 * au moment précis où on le vise. Ici il garde sa couleur et gagne un trait.
 */
.nav__link::after {
  content: '';
  position: absolute;
  left: var(--nav-filet-inset);
  /* Le dernier caractère traîne son interlettrage : sans cette retenue, le
     filet dépasse la lettre d'un cheveu, visible sur un trait de 0,5px. */
  right: 0.08em;
  bottom: -6px;
  height: 0.5px;
  background: var(--color-text-primary);

  /*
   * Le filet se trace de gauche à droite, et ressort par la droite.
   *
   * L'astuce est dans les deux origines : au survol il grandit depuis la
   * gauche, au retrait il se rétracte vers la droite — le trait *traverse* au
   * lieu de rebrousser chemin. Une origine unique le ferait revenir en arrière
   * à la sortie, ce qui se lit comme une annulation plutôt que comme un geste.
   *
   * `scaleX` et non `width` : la largeur relance la mise en page à chaque
   * image, la transformation est composée par le GPU et ne touche à rien.
   */
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 220ms ease;
}

.nav__link:hover,
.nav__link:focus-visible {
  color: var(--color-text-primary);
}

.nav__link:hover::after,
.nav__link:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/*
 * Même règle que le reste du site : sous `prefers-reduced-motion`, tout
 * mouvement gratuit est désarmé. Le filet paraît toujours au survol — c'est
 * l'information, elle reste — mais d'un coup, sans être tracé.
 */
@media (prefers-reduced-motion: reduce) {
  .nav__link::after { transition: none; }
}

/* ---------------------------------------------------------------- Hero --- */

.hero {
  padding-top: 104px;
  padding-bottom: 132px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 32px;
}
.hero__logo { padding: 58px 58px 10px; }
.hero__logo img { display: block; width: 290px; max-width: 100%; height: auto; }
.hero__title { max-width: 41.25rem; } /* 660px — borne une longueur de ligne, donc en rem */
.hero__lead { color: var(--slate-500); max-width: 32.5rem; } /* 520px, idem */
.hero__cta { display: flex; flex-direction: column; align-items: center; gap: 14px; margin-top: 8px; }

/* ------------------------------------------------------------ Sections --- */

.section { padding-top: 96px; padding-bottom: 96px; }
.section--dark { background: var(--color-bg-surface); }
.section--dark .t-h2, .section--dark .t-h3 { color: var(--color-text-on-dark); }
.section--ruled { border-top: 0.5px solid var(--color-border); }
.section__title { margin-bottom: 48px; text-align: center; }

/* ---------------------------------------------------- Fonctionnalités --- */

/*
 * Carrousel de fonctionnalités.
 *
 * Le défilement est natif : la piste est simplement une liste qui déborde, avec
 * un calage par carte. Aucune position n'est calculée en JavaScript, ce qui
 * laisse la molette, le trackpad, le doigt et le clavier fonctionner tels quels
 * — les flèches ne font que déplacer ce même défilement.
 */
.carousel {
  display: flex;
  gap: 20px;
  list-style: none;
  margin: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* La barre native ferait doublon avec les flèches, et sur fond sombre elle
     salit la rangée. Le défilement, lui, reste entier. */
  scrollbar-width: none;

  /* La piste vit hors de `.shell` et court sur toute la largeur de la page ;
     c'est ce retrait qui replace sa première carte sur la colonne de texte,
     tandis que la fin de la rangée est coupée par le bord de la fenêtre.
     Le `max()` couvre les fenêtres plus étroites que `--page-max`, où le seul
     retrait à rattraper est la gouttière.
     `100%` se lit ici sur la largeur de page utile — hors barre de défilement
     verticale — là où `100vw` la compterait et décalerait la colonne. */
  --carousel-inset: max(
    var(--page-gutter),
    calc((100% - var(--page-max)) / 2 + var(--page-gutter))
  );
  padding: 0 var(--page-gutter) 0 var(--carousel-inset);
  /* Sans quoi seule la carte au repos serait alignée : les suivantes se
     caleraient sur le bord de la fenêtre, pas sur la colonne. */
  scroll-padding-left: var(--carousel-inset);
}
.carousel::-webkit-scrollbar { display: none; }

/* 15.625rem = 250px. En rem comme les autres mesures de texte : à préférence
   de police agrandie, une carte figée en px hacherait ses phrases en lignes de
   trois mots. Le calage par carte n'en dépend pas (`scroll-snap-align`). */
.carousel .feature { flex: 0 0 15.625rem; scroll-snap-align: start; }

.carousel__nav { display: flex; justify-content: flex-end; gap: 10px; margin-top: 28px; }
.carousel__arrow {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0.5px solid var(--slate-600);
  border-radius: 50%;
  background: #22344A;
  color: var(--ivory-50);
  cursor: pointer;
  transition: background 200ms ease, opacity 200ms ease;
}
.carousel__arrow:hover:not(:disabled) { background: #28405A; }
/* Désactivée plutôt que masquée : une flèche qui disparaît déplace l'autre, et
   ne dit pas au clavier qu'on est en bout de course. */
.carousel__arrow:disabled { opacity: 0.3; cursor: default; }

.feature {
  background: #22344A;
  border: 0.5px solid var(--slate-600);
  border-radius: var(--radius-card);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: background 200ms ease;
}
.feature:hover { background: #28405A; }
.feature p { color: var(--slate-300); }

/* --------------------------------------------------------- Garanties --- */

.guarantees { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 48px; }
.guarantee {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 24px;
  border-top: 0.5px solid var(--slate-600);
}
.guarantee p { color: var(--slate-300); }

/* ------------------------------------------------------------- Étapes --- */

.steps { padding-top: 96px; padding-bottom: 12vh; }

/*
 * Ligne de composition de la section : l'aperçu et le titre de l'étape s'y
 * centrent tous les deux. Le script, lui, ne lit pas cette variable : il lit
 * le `top` épinglé de `.step__content` et y ajoute 3rem, ce qui retombe sur
 * cette ligne ici, et sur une autre en pile sous 900px. Une seule valeur, deux
 * usages — les déplacer ensemble ou pas du tout.
 */
:root { --steps-center: 50vh; }
.steps__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: 64px;
  align-items: stretch;
}
.steps__left { display: grid; grid-template-columns: 1px minmax(0, 1fr); gap: 32px; align-items: start; }

/*
 * La jauge est courte et IMMOBILE, épinglée sur la même bande centrée que
 * l'aperçu et le titre. C'est ce qui la rend lisible.
 *
 * Une piste qui s'étirerait sur toute la hauteur des étapes donnerait une
 * progression exacte mais invisible : le remplissage valant « position de
 * lecture dans la piste », son bord resterait collé à mi-écran du début à la
 * fin. Rien ne bougerait à l'œil.
 */
.steps__track {
  position: sticky;
  top: calc(var(--steps-center) - 200px);
  height: 400px;
  background: var(--color-border);
}
.steps__fill {
  position: absolute; top: 0; left: 0; width: 100%; height: 0;
  background: var(--slate-700);
  /*
   * Aucune transition. La hauteur est recalculée à chaque image d'après la
   * position de défilement : une transition la poursuivrait sans jamais
   * l'atteindre, en repartant à zéro à chaque image — c'est ce qui faisait
   * sauter la jauge.
   */
}
/* Aucune traîne ici : la piste de la jauge s'étire sur cette pile, elle doit
   donc couvrir exactement les trois étapes, ni plus ni moins. L'air se
   respire sous la section. */
.steps__stack { padding-bottom: 0; }
/*
 * Le bloc d'étape ne sert qu'à réserver de la course de défilement : c'est lui
 * que le script mesure. Son contenu, lui, est collant et reste au centre tant
 * que l'étape dure — d'où la séparation en deux éléments. Mesurer le contenu
 * serait une erreur : une fois épinglé, il ne bouge plus.
 */
.step { min-height: 62vh; padding: 20px 0; }
.step__content {
  position: sticky;
  /* Même durée que le fondu des volets : les deux colonnes changent ensemble. */
  transition: opacity 240ms ease;
  /* 3rem ≈ moitié de la hauteur du bloc titre + texte (mesurée : 101px).
     En rem parce que ce bloc EST du texte : il grandit avec la préférence de
     police du visiteur, et son point de centrage doit grandir avec lui. */
  top: calc(var(--steps-center) - 3rem);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.step p { color: var(--slate-800); max-width: 22.5rem; } /* 360px, idem */

/* 200px = moitié de la hauteur de l'aperçu, fixée juste en dessous. */
.mockup-col { position: sticky; top: calc(var(--steps-center) - 200px); align-self: start; }

/*
 * L'aperçu ne paraît qu'une fois arrivé au centre, et s'efface quand la
 * section est finie — plutôt que de monter puis descendre à travers l'écran.
 *
 * Le masquage n'est armé que par le script (`.steps--armed`) : sans
 * JavaScript, l'aperçu reste simplement visible. Une page ne doit jamais
 * dépendre d'un script pour montrer son contenu.
 */
.steps--armed .mockup-col { opacity: 0; transition: opacity 300ms ease; }
.steps--armed .mockup-col.is-engaged { opacity: 1; }
.mockup {
  /* Sert deux fois : hauteur de la barre, et décalage des volets sous elle.
     Les deux doivent bouger ensemble, d'où la variable. */
  --chrome-h: 30px;

  /*
   * L'aperçu a SES tokens, comme il a déjà sa propre échelle de tailles et ses
   * propres polices (voir le bloc `.pane` plus bas) : il figure l'écran d'un
   * AUTRE produit, il n'est pas un morceau d'interface de la vitrine. Rien ici
   * n'est cliquable, tout le bloc est `aria-hidden` — WCAG 1.4.11 ne s'y
   * applique donc à aucun titre, et le contour de contrôle à l'ardoise 400 n'y
   * avait pas sa place : sur l'ivoire du volet, il posait un bleu franc qui se
   * battait avec le bordeaux de la paroisse.
   */
  --pane-border: var(--ivory-400);
  /*
   * Ces deux valeurs sont arrêtées le 2026-09-07, panneau d'arbitrage à
   * l'appui, supprimé depuis.
   */
  /*
   * Distinct du contour, et ce n'en est même pas un : c'est le gris d'attente
   * des aplats que le script repeint à la couleur de la paroisse (entête,
   * pastilles, vignette). Il partageait le token de bordure sans avoir jamais
   * rien eu à voir avec lui — d'où un aplat qui virait au bleu vif le jour où
   * la bordure a bougé.
   */
  --pane-placeholder: var(--ivory-300);

  position: relative;
  height: 400px;
  background: var(--ivory-100);
  border: 0.5px solid var(--pane-border);
  border-radius: var(--radius-card);
  overflow: hidden;
  /* Seule ombre de la vitrine — voir l'exception à la règle 3, en tête. Elle
     ne se justifie qu'accompagnée de la barre ci-dessous : c'est le couple
     « fenêtre + élévation » qui dit « écran posé sur la page », pas l'ombre
     seule. Retirer l'une sans l'autre laisse un objet qui ne dit plus rien. */
  box-shadow: var(--shadow-lifted);
}

/*
 * Barre de fenêtre. Trois pastilles, aucune barre d'adresse — voir la règle
 * en rouge dans le gabarit.
 */
.mockup__chrome {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: var(--chrome-h);
  display: flex;
  align-items: center;
  gap: 5px;
  padding-left: 14px;
  background: var(--ivory-200);
  border-bottom: 0.5px solid var(--pane-border);
}
.mockup__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ivory-400);
}
/*
 * Retiré du flux visuel sans l'être de l'arbre d'accessibilité : `display:none`
 * et `visibility:hidden` retireraient le texte des deux à la fois.
 */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * L'aperçu représente un site paroissial — donc un autre produit, avec sa
 * propre charte. D'où Fraunces et Albert Sans, qui n'appartiennent pas à la
 * Charte Merkaba et ne doivent servir qu'ici.
 *
 * ⚠️ C'est aussi le seul endroit de la vitrine dont les tailles restent en px,
 * et il ne faut pas les « uniformiser » en rem : cet aperçu est une image dans
 * un cadre de 400px de haut, pas du texte à lire. Rendu solidaire de la
 * préférence de police du visiteur, son contenu déborderait du cadre — alors
 * que personne ne lit ces libellés, on les regarde.
 */
.pane {
  /* Décalés sous la barre. Un `padding-top` sur `.mockup` ne suffirait pas :
     le bloc conteneur d'un élément en `absolute` est la *padding box* de son
     ancêtre, donc `inset: 0` s'étendrait quand même derrière la barre. */
  position: absolute; inset: var(--chrome-h) 0 0 0;
  padding: 28px;
  display: flex; flex-direction: column; gap: 14px;
  font-family: 'Albert Sans', var(--ff-ui);
  opacity: 0;
  pointer-events: none;
  /*
   * Fondu court, et sur la seule opacité. En remontant le défilement, le volet
   * entrant arrive déjà composé : un fondu long superposerait deux textes
   * lisibles pendant une demi-seconde. Aucun déplacement non plus — le
   * mouvement, ici, appartient au contenu, pas au conteneur.
   */
  transition: opacity 240ms ease;
}
.pane.is-active { opacity: 1; pointer-events: auto; }

/*
 * Le volet qui figure un SITE, par opposition au volet 1 qui figure un panneau
 * de réglages. Il occupe la fenêtre entière, bord à bord : c'est ce que fait
 * une page web dans un navigateur.
 *
 * C'est le vrai correctif du 2026-09-07, et il ne porte pas sur une couleur.
 * L'entête, le bloc d'accueil et le pied portaient chacun leur cadre et leur
 * rayon, séparés par la gouttière de 28px et l'écart de 14px du volet : le
 * site se lisait comme quatre cartes empilées dans une fenêtre, pas comme une
 * page. Les rayons `8px 8px 0 0` de l'entête et `0 0 8px 8px` du pied le
 * disaient déjà — ils n'ont de sens qu'accolés à quelque chose, et rien ne les
 * accolait. Le cadre du navigateur découpe désormais la page, comme un vrai.
 */
.pane--site { padding: 0; gap: 0; }

/*
 * Pas de transition sur les éléments animés : leur opacité et leur position
 * sont recalculées à chaque image à partir de la position de défilement.
 * Une transition CSS viendrait courir après la valeur au lieu de la suivre,
 * et rendrait le geste mou.
 */

.pane__eyebrow {
  font-size: 11px; font-weight: 500; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ivory-600);
}
.pane__field { display: flex; flex-direction: column; gap: 7px; }
.pane__label { font-size: 12px; color: var(--color-text-secondary); }
.pane__input {
  border: 0.5px solid var(--pane-border);
  border-radius: 8px;
  padding: 11px 13px;
  background: var(--ivory-50);
  font-size: 14px;
  color: var(--slate-700);
  min-height: 19px;
}
.pane__caret {
  display: inline-block; width: 1px; height: 14px;
  margin-left: 1px; background: var(--ivory-600); vertical-align: -2px;
}
.pane__swatches { display: flex; align-items: center; gap: 10px; }
.pane__swatch {
  width: 30px; height: 30px; border-radius: 8px;
  background: var(--pane-placeholder);
  transition: background 600ms ease;
}
.pane__swatch--2 { opacity: 0.28; }
.pane__swatch--3 { opacity: 0.14; }
/*
 * Carré, et non une zone pleine largeur : ce champ reçoit un logo, dont le
 * format est justement carré. Un rectangle étiré promettait une bannière.
 * `align-self` est nécessaire, sans quoi la colonne étirerait le carré.
 */
.pane__dropzone {
  width: 64px;
  height: 64px;
  align-self: flex-start;
  border: 0.5px dashed var(--pane-border);
  border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  color: var(--ivory-600); background: var(--ivory-50);
}
/* Bord à bord sous la barre de fenêtre, et donc sans rayon : c'est le cadre du
   navigateur qui coupe, comme il coupe l'entête d'un vrai site. */
.pane__sitehead {
  padding: 14px 22px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--pane-placeholder);
  transition: background 600ms ease;
}
.pane__parish { font-family: 'Fraunces', var(--ff-display); font-size: 15px; font-weight: 500; color: var(--ivory-50); }
.pane__sitenav { display: flex; gap: 12px; }
.pane__sitenav span {
  font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase;
  color: rgba(249, 249, 248, 0.78);
}
/*
 * Le corps de la page. Il pousse le pied en bas de la fenêtre (`flex: 1`), ce
 * qui évite qu'à l'étape 2, où la page est courte, le pied ne flotte au milieu
 * du cadre en laissant du vide sous lui.
 */
.pane__body {
  flex: 1;
  display: flex; flex-direction: column; gap: 14px;
  padding: 20px 22px;
}

/*
 * Le bloc d'accueil n'est PAS une carte : c'est le contenu principal de la
 * page, posé sur elle. L'encadrer revenait à dessiner une boîte autour du seul
 * élément qui n'a rien à côté de quoi se distinguer.
 */
.pane__welcome { display: flex; flex-direction: column; gap: 10px; }

/* Les seules boîtes qui restent dans le site, et elles se justifient : deux
   blocs côte à côte, que rien d'autre ne sépare. */
.pane__card {
  border: 0.5px solid var(--pane-border);
  border-radius: 8px;
  background: var(--ivory-50);
  padding: 14px 16px;
}
.pane__card--row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.pane__card--stack { display: flex; flex-direction: column; gap: 5px; }
.pane__card--media { display: flex; align-items: center; gap: 14px; }
/* Un filet et un aplat, pas un cadre : un pied de page se sépare de ce qui le
   précède, il ne s'en détache pas. */
.pane__sitefoot {
  border-top: 0.5px solid var(--pane-border);
  padding: 12px 22px;
  display: flex; justify-content: space-between;
  background: var(--ivory-100);
}
.pane__serif { font-family: 'Fraunces', var(--ff-display); color: var(--slate-700); }
.pane__rule { height: 0.5px; background: var(--pane-border); width: 64px; }
/* Le logo déposé à l'étape 1, qu'on retrouve sur le site aux étapes 2 et 3. */
.pane__cross { display: inline-flex; }
.pane__cross--onhead { color: var(--ivory-50); opacity: 0.9; }
.pane__brand { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }

/* Deux blocs côte à côte, chacun la moitié de la largeur. */
.pane__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.pane__masses { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 12px; color: var(--color-text-secondary); }
.pane__masses span:last-child { font-weight: 500; }

.pane__thumb {
  width: 36px; height: 36px; border-radius: 8px;
  background: var(--pane-placeholder); flex: 0 0 auto;
  transition: background 600ms ease;
}

/* ------------------------------------------------------------- Tarifs --- */

.pricing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  border: 0.5px solid var(--color-border);
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--ivory-100);
}
.pricing__cell { padding: 40px; display: flex; flex-direction: column; gap: 16px; }
/*
 * `.pricing__cell--price` n'a volontairement aucune règle ici — la classe
 * existe pour être visée, la déclaration manquante est le choix.
 *
 * ⚠️ La colonne tarifaire reste alignée en haut, comme la liste : c'est un
 * choix, pas le comportement par défaut qu'on aurait oublié de corriger.
 *
 * Depuis que le bouton est parti pour la section finale, cette colonne est
 * plus courte que la liste « Inclus », et la grille les étire à la même
 * hauteur : il reste du vide sous le prix. Le centrage a été essayé pour
 * l'absorber, et comparé sur pièce le 2026-09-02 — il est moins bon. Alignées
 * en haut, les deux colonnes **commencent sur la même ligne** et la carte se
 * lit comme un tableau ; centré, le bloc tarifaire flotte et ce décalage coûte
 * plus cher que le déséquilibre qu'il corrige. Le vide du bas, lui, ne se lit
 * pas comme un défaut : la liste de droite continue visiblement plus bas, donc
 * l'asymétrie dit « cette liste est longue ».
 *
 * Ne pas y remettre un `justify-content` sans refaire la comparaison.
 */
.pricing__cell--included { border-left: 0.5px solid var(--color-border); }
.pricing__toggle {
  display: inline-flex; align-self: flex-start;
  padding: 3px; gap: 3px;
  background: var(--ivory-200);
  border: 0.5px solid var(--color-border-control);
  border-radius: 8px;
}
.pricing__toggle button {
  font-family: var(--ff-ui); font-size: var(--fs-ui-xs); font-weight: 500; letter-spacing: 0.02em;
  padding: 7px 14px; border: none; border-radius: var(--radius-button);
  cursor: pointer; background: transparent; color: var(--color-text-secondary);
  transition: background 200ms ease, color 200ms ease;
}
.pricing__toggle button[aria-pressed="true"] { background: var(--slate-700); color: var(--ivory-50); }
.pricing__amount { font-family: var(--ff-display); font-size: var(--fs-hero); font-weight: 500; margin: 0; line-height: 1.2; }
.pricing__period { font-family: var(--ff-ui); font-size: var(--fs-ui); font-weight: 400; color: var(--color-text-secondary); }
.pricing__note { color: var(--slate-800); min-height: 26px; }
/* Mention fiscale : présente sans peser, elle informe sans être un argument. */
.pricing__vat { margin-top: 10px; }
/* Anciennement `.pricing__cta`, renommée quand le bouton a quitté la cellule :
   un nom qui annonce un appel à l'action pour une ligne de réassurance
   enverrait le prochain lecteur chercher un bouton qui n'existe plus. */
.pricing__reassurance { margin-top: 8px; }
/*
 * Deux colonnes dès qu'il y a la place : à douze lignes, une seule colonne
 * étirait la carte au point de laisser la moitié tarifaire vide en face.
 */
.pricing__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0 24px;
}
.pricing__list li {
  font-family: var(--ff-ui); font-size: var(--fs-ui); color: var(--slate-800);
  padding: 12px 0; border-bottom: 0.5px solid var(--color-border);
}

/* ---------------------------------------------------------- Conclusion --- */

.closing {
  padding-top: 112px; padding-bottom: 112px;
  display: flex; flex-direction: column; align-items: center; gap: 24px; text-align: center;
}
.closing .t-lead { color: var(--slate-300); }

/* -------------------------------------------------------------- Footer --- */

.footer { border-top: 0.5px solid var(--color-border); }
.footer__top {
  padding-top: 72px; padding-bottom: 40px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 48px; align-items: start;
}
.footer__logo { padding: 28px 28px 28px 0; }
.footer__logo img { display: block; width: 140px; height: auto; }
.footer__col { display: flex; flex-direction: column; gap: 12px; padding-top: 28px; }
.footer__col a { font-family: var(--ff-ui); font-size: var(--fs-ui); transition: color 200ms ease; }
.footer__col .t-eyebrow { margin-bottom: 4px; }
.footer__bottom { padding-top: 24px; padding-bottom: 48px; border-top: 0.5px solid var(--color-border); }

/* ------------------------------------------------------------ Révélation --- */

.reveal { transition: opacity 700ms ease, transform 700ms ease; }
.reveal.is-hidden { opacity: 0; transform: translateY(10px); }

/* --------------------------------------------------------- Adaptatif --- */

/*
 * Sous 900px, le même dispositif qu'en grand écran, empilé : le texte de
 * l'étape est épinglé EN HAUT, sous l'entête ; l'aperçu est épinglé juste
 * DESSOUS ; entre les deux dispositions, ce sont les blocs d'étape qui
 * fournissent la course de défilement, et c'est elle qui fait changer le
 * texte et composer l'aperçu.
 *
 * Avant le 2026-09-17, tout se mettait simplement en pile : trois textes
 * collés, puis l'aperçu tout seul en bas, figé sur son dernier état. Demandé
 * par Sloan : « le texte de l'étape et en dessous la preview qui se met à
 * jour ».
 *
 * L'aperçu est épinglé par le HAUT, relativement au texte, et non par le bas
 * de la fenêtre : une première version le collait en bas, et la barre de
 * Safari, qui se déploie et se replie au défilement, le faisait monter et
 * descendre (relevé par Sloan sur son iPhone). Pour qu'un collant en haut
 * soit visible dès l'entrée de la section, il doit PRÉCÉDER les étapes dans la
 * mise en page (un collant n'apparaît qu'une fois son emplacement naturel
 * atteint) : d'où `order: -1`. Deux conséquences à connaître :
 *
 *   - en entrée, le premier texte remonte de sous l'aperçu jusqu'à sa place
 *     au-dessus ; c'est le fondu d'engagement (`.steps--armed`) qui couvre ce
 *     passage, l'aperçu ne paraissant qu'une fois le texte en place. Sans
 *     JavaScript, le texte glisse sur un aperçu visible — accepté ;
 *   - en sortie, l'aperçu ne doit pas être poussé par la fin de son conteneur
 *     avant que le dernier texte le soit par la fin de son bloc, sans quoi il
 *     remonterait dessus. Le conteneur (`.steps__grid`) garde donc sa hauteur
 *     en réserve sous les étapes : les deux quittent l'écran ensemble.
 *
 * 🔴 Texte et aperçu doivent tenir ensemble dans la fenêtre, d'où la hauteur
 * de l'aperçu bornée à `svh` (la fenêtre la plus petite, barres déployées).
 *
 * Le script lit la ligne de référence sur le `top` épinglé du texte
 * (`assets/app.js`, `referenceLine`), donc cette disposition et celle des
 * colonnes n'ont pas deux réglages à garder d'accord.
 */
@media (max-width: 900px) {
  .steps {
    /* Lu par le script (`assets/app.js`) : en pile, l'aperçu ne s'efface pas
       en fin de section, il part avec le dernier texte. Posé ici pour que le
       seuil de 900px n'existe qu'à un endroit. */
    --steps-layout: stacked;
    /* L'entête (78px, ou 60px en compacte sous 560px : `--header-h` suit),
       plus 32px d'air : à 16px le texte semblait accroché à l'entête (Sloan,
       2026-09-17). */
    --steps-text-top: calc(var(--header-h) + 32px);
    /* Le texte fait 101px sur deux lignes ; 140 laisse une troisième ligne
       et un peu d'air avant l'aperçu. */
    --steps-stage-top: calc(var(--steps-text-top) + 140px);
    --steps-stage-h: clamp(240px, calc(100svh - var(--steps-stage-top) - 16px), 400px);
  }
  .steps__grid { grid-template-columns: 1fr; gap: 0; padding-bottom: var(--steps-stage-h); }
  /* La jauge ne mesure plus rien de perceptible en pile. */
  .steps__left { grid-template-columns: 1fr; gap: 0; }
  .steps__track { display: none; }
  .step { min-height: 70vh; padding: 0; }
  .step__content { top: var(--steps-text-top); }
  /*
   * La marge négative retire l'aperçu du flux (sa rangée de grille fait zéro)
   * : sans elle, ses 400px vides précédaient le premier texte à l'entrée de la
   * section. La boîte collante, elle, garde sa hauteur, et le conteneur la
   * réserve en bas (`padding-bottom` ci-dessus) : c'est ce qui fait que texte
   * et aperçu quittent l'écran ensemble.
   */
  .mockup-col {
    order: -1;
    position: sticky;
    top: var(--steps-stage-top);
    margin-bottom: calc(-1 * var(--steps-stage-h));
  }
  .mockup { height: var(--steps-stage-h); }
}

/* ---------------------------------------------------------------------------
 * Écrans applicatifs : inscription, connexion, espace client, pages légales.
 *
 * Rien d'inventé ici : mêmes tokens que la vitrine, mêmes règles de charte —
 * bordures à 0.5px, aucune ombre, l'or réservé au bouton d'action. Ces pages
 * sont volontairement plus sobres que la vitrine : on y fait quelque chose, on
 * n'y est pas convaincu.
 * ------------------------------------------------------------------------- */

.page {
  /* L'entête est en position fixe sur la vitrine ; ce dégagement évite que le
     premier titre passe dessous. */
  padding-top: 120px;
  padding-bottom: 96px;
  min-height: 60vh;
}

/*
 * La largeur de lecture est portée par la colonne, jamais par le contenu.
 * Posé après coup : `.form-page` avait son propre `max-width` dans un `.shell`
 * de 1080px, donc sur écran large le formulaire se collait au bord gauche
 * pendant que les bandeaux flash s'étalaient sur toute la page — deux largeurs
 * qui se contredisaient, et qui se lisaient comme une page cassée.
 */
.page__shell { margin-left: auto; margin-right: auto; }
.page__shell--narrow { max-width: calc(34rem + 2 * var(--page-gutter)); }
.page__shell--wide   { max-width: calc(44rem + 2 * var(--page-gutter)); }

.form-page,
.legal { max-width: 100%; }

/*
 * Le formulaire ne se termine pas au ras du pied de page. Relevé par Sloan sur
 * la commande et sur la configuration du site : les deux finissent par un
 * bouton suivi de sa note, et cette note touchait le filet du pied.
 *
 * Posé sur `.form-page` plutôt que sur chaque gabarit : le manque n'appartient
 * à aucun des deux formulaires, il appartient à la mise en page qui les porte,
 * et le corriger deux fois en ferait deux valeurs à tenir d'accord.
 */
.form-page { padding-bottom: 32px; }

.legal h2 { margin-top: 32px; }

.legal .t-note { margin-top: 6px; }

/*
 * Les liens de ces écrans vivent dans de la prose, pas dans une barre de
 * navigation : sans soulignement ils sont indiscernables du texte. Le cas qui
 * l'impose est la case d'acceptation, où l'on demande d'accepter des contrats
 * — ne pas voir qu'ils sont consultables est un défaut de fond, pas de style.
 * Les boutons et le pied de page gardent le rendu de la vitrine.
 */
.page a:not(.btn) {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 0.5px;
}

.operator__mark { display: block; margin-bottom: 28px; }

/* L'écran d'attente d'avant l'ouverture (templates/launch/coming_soon.html.twig). */
.coming-soon__contact { margin-top: 28px; }

.form-page > .t-h2,
.legal > .t-h2 { margin-bottom: 12px; }

.form { margin-top: 36px; }

/* Les notes sous le bouton : de l'air, et de l'air entre elles. */
.form-page > .t-note { margin-top: 16px; }

.form__row,
.form > div {
  margin-bottom: 20px;
}

.form label {
  display: block;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  margin-bottom: 6px;
}

.form input[type="email"],
.form input[type="password"],
.form input[type="text"] {
  width: 100%;
  box-sizing: border-box;
  padding: 11px 14px;
  font-family: var(--ff-ui);
  /* Jamais sous 16px sur mobile : en dessous, Safari zoome à la mise au point
     et la page part de travers. */
  font-size: var(--fs-ui);
  color: var(--color-text-primary);
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
}

.form input:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* Case à cocher : la case garde sa taille native et le libellé revient à sa
   gauche typographique, sinon un libellé de deux lignes se replie sous elle. */
.form__check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 20px;
}

.form__check input { margin-top: 3px; flex: none; }

.form__check label {
  margin-bottom: 0;
  font-size: var(--fs-ui-sm);
  line-height: 1.5;
}

.form .help-text,
.form__help {
  font-size: var(--fs-micro);
  color: var(--color-text-secondary);
  margin-top: 6px;
}

.form ul,
.form-error {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: var(--fs-micro);
  color: #8C2F39;
}

/*
 * 🔴 Le retrait de 18px ci-dessus date des erreurs rendues en liste à puces ;
 * le thème les rend en paragraphes depuis, et il ne restait qu'un décalage qui
 * désaligne le message de son libellé et de son champ. Visible sur tous les
 * formulaires du produit, pas seulement celui-ci.
 */
.form-error { padding-left: 0; }

.form button[type="submit"] { margin-top: 8px; }

.notice {
  padding: 12px 16px;
  margin-bottom: 24px;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  border-radius: var(--radius-button);
  border: 0.5px solid var(--color-border);
  background: var(--color-bg-notice);
}

.notice--error {
  border-color: #C9A0A5;
  color: #8C2F39;
}

/*
 * L'entête porte deux choses de nature différente : trois ancres qui abrègent
 * un défilement dans la page, et une porte vers un compte. Sans séparation,
 * « Se connecter » se lit comme une quatrième ancre — même typo, mêmes
 * capitales, même écart — et rien ne dit qu'il mène ailleurs.
 *
 * Le filet les sépare en deux groupes. Il ne change pas l'ordre, et c'est
 * délibéré : ⚠️ **le bouton doit rester le dernier élément de la ligne.** Sa
 * force vient de sa position terminale, et son bord cale l'entête sur la
 * gouttière de page — un lien texte placé après lui enfermerait le bouton et
 * rendrait le bord droit optiquement mou. Essayé sur la page avant de trancher.
 */
.nav__link--persistent {
  --nav-filet-inset: 28px;

  margin-left: 20px;
  border-left: 0.5px solid var(--color-border);
}

@media (max-width: 860px) {
  /* Les ancres disparaissent : le filet n'aurait plus rien à séparer, et
     pendrait tout seul à gauche du lien. */
  .nav__link--persistent {
    --nav-filet-inset: 0px;

    margin-left: 0;
    border-left: none;
  }

  /*
   * Les ancres disparaissent sous 860px : sur une monopage, le défilement *est*
   * la navigation, et un menu déployable serait du travail pour trois ancres.
   * (Réserve ouverte, FON-81.)
   *
   * ⚠️ Seules les ancres. Le lien de connexion n'abrège pas un défilement,
   * c'est la seule porte de retour d'un client existant : le masquer le
   * laisserait avec « Créer mon site » pour seul choix, c'est-à-dire avec
   * l'invitation à s'inscrire une deuxième fois.
   *
   * 🔴 Ciblé par `--anchor` et non par « tout sauf `--persistent` » : la
   * seconde forme masquait aussi le lien de connexion des écrans applicatifs
   * (inscription, connexion, pages légales), qui ne porte pas `--persistent`
   * parce qu'il n'a pas de filet à séparer. Relevé le 2026-09-17 sur la passe
   * mobile : un visiteur sur /inscription n'avait plus « Se connecter » dans
   * l'entête, alors que la vitrine, elle, le gardait.
   */
  .nav__link--anchor { display: none; }
}

@media (max-width: 760px) {
  .pricing__cell--included { border-left: none; border-top: 0.5px solid var(--color-border); }
}

@media (max-width: 560px) {
  :root { --page-gutter: 24px; }

  /*
   * Entête compacte : pictogramme seul (le `<picture>` de
   * `layout/_header_logo.html.twig` sert l'autre image sous cette largeur),
   * lien et bouton resserrés. Budget mesuré le 2026-09-17 sur 360px de large,
   * la plus étroite des tailles courantes : 312px de contenu, 36 + 12 + 108 +
   * 10 + 126 = 292. Sans ce resserrement, l'entête se pliait sur deux lignes,
   * 130px de haut, collante, un tiers de l'écran.
   *
   * `flex-wrap` reste actif sur `.header__inner` : si une traduction plus
   * longue dépasse un jour, l'entête se plie au lieu de faire défiler la page
   * horizontalement, ce qui est le moindre des deux maux.
   */
  :root { --header-h: 60px; }
  .header__inner { gap: 12px; }
  .header__logo { padding: 16px 8px 16px 0; }
  .header__logo img { width: 28px; }
  .nav { gap: 10px; }
  .nav__link { font-size: var(--fs-micro); }
  .nav .btn { font-size: var(--fs-ui-xs); padding: 9px 12px; }

  .hero { padding-top: 72px; padding-bottom: 88px; }
  .hero__logo { padding: 24px 0 4px; }
  .hero__logo img { width: 220px; }
  .section { padding-top: 64px; padding-bottom: 64px; }
  .steps { padding-top: 64px; }
  .pricing__cell { padding: 28px; }
  .closing { padding-top: 80px; padding-bottom: 80px; }
}

/* ==========================================================================
   Menu du compte connecté (entête)
   ========================================================================== */

/*
 * L'entête ne montre qu'une chose à qui est connecté : son adresse. Le reste
 * s'ouvre au survol. Deux liens côte à côte disaient deux fois la même chose
 * et laissaient « Se déconnecter » à un clic de distraction du reste.
 */
.usermenu { position: relative; }

.usermenu__trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 15rem;
  padding: 10px 14px;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-xs);
  font-weight: 500;
  color: var(--color-text-primary);
  background: transparent;
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
  cursor: pointer;
}

.usermenu__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.usermenu__caret { flex: none; transition: transform 150ms ease; }

.usermenu__panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  padding: 6px;
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);

  /*
   * Fermé par défaut, et fermé de façon à sortir de l'ordre de tabulation :
   * `visibility: hidden` retire les liens du parcours clavier, ce que
   * `opacity: 0` seul ne fait pas. Un menu invisible mais tabulable envoie le
   * curseur nulle part.
   */
  visibility: hidden;
  opacity: 0;
  transition: opacity 120ms ease, visibility 120ms;
}

/*
 * Trois façons d'ouvrir, pour trois façons de naviguer : la souris survole, le
 * clavier met le focus dedans, le doigt appuie sur le bouton (et le survol
 * n'existe pas sur un écran tactile).
 */
.usermenu:hover .usermenu__panel,
.usermenu:focus-within .usermenu__panel,
.usermenu__trigger[aria-expanded="true"] + .usermenu__panel {
  visibility: visible;
  opacity: 1;
}

.usermenu:hover .usermenu__caret,
.usermenu:focus-within .usermenu__caret,
.usermenu__trigger[aria-expanded="true"] .usermenu__caret { transform: rotate(180deg); }

.usermenu__item {
  padding: 9px 12px;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  color: var(--color-text-primary);
  border-radius: calc(var(--radius-button) - 2px);
  white-space: nowrap;
  text-decoration: none;
}

.usermenu__item:hover,
.usermenu__item:focus-visible { background: var(--ivory-200); }

@media (prefers-reduced-motion: reduce) {
  .usermenu__panel,
  .usermenu__caret { transition: none; }
}

/* ==========================================================================
   Empilement vertical
   ========================================================================== */

/*
 * Les écrans applicatifs enchaînent titre, texte et bouton sans qu'aucun de ces
 * éléments ne porte de marge propre : sans cette règle ils se touchent, ce qui
 * s'est vu sur l'espace client. La marge vit dans le conteneur, comme partout
 * ailleurs dans cette feuille.
 */
.stack > * + * { margin-top: 20px; }
.stack > .t-h2 + * { margin-top: 14px; }
.stack > .notice + * { margin-top: 24px; }
.stack > .btn { margin-top: 32px; }

/* ==========================================================================
   Formulaires : sections, paires, options
   ========================================================================== */

/*
 * Un intertitre et de l'air, pas un encadré. Un `fieldset` bordé découpait la
 * commande en deux boîtes qui n'avaient aucune raison de paraître séparées, et
 * la Charte ne connaît pas la boîte.
 */
.form__section {
  margin: 40px 0 0;
  padding-top: 28px;
  border-top: 0.5px solid var(--color-border);
  font-family: var(--ff-ui);
  font-size: var(--fs-ui);
  font-weight: 500;
}

.form__aside {
  margin: 8px 0 0;
  font-family: var(--ff-ui);
  font-size: var(--fs-micro);
  color: var(--color-text-secondary);
  line-height: 1.55;
}

.form__section + .form__aside { margin-bottom: 24px; }

/* Deux champs qui se saisissent ensemble se posent sur une ligne. */
.form__pair { display: flex; gap: 16px; }
.form__pair > * { flex: 1; }
.form__pair > *:first-child { flex: 0 0 8rem; }

@media (max-width: 480px) {
  .form__pair { display: block; }
}

/*
 * Le choix d'une offre, en cartes plutôt qu'en pastilles de 13 pixels. Ce n'est
 * pas un réglage secondaire : c'est ce que le client va payer, et la cible est
 * la moins à l'aise avec un petit rond à viser.
 *
 * La carte reste un `<label>` autour d'un vrai `<input type="radio">` : clavier,
 * lecteur d'écran et validation native continuent de fonctionner seuls.
 */
.form__options { display: flex; flex-direction: column; gap: 10px; }

.form__option {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 0;
  padding: 16px 18px;
  font-size: var(--fs-ui-sm);
  line-height: 1.5;
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease;
}

.form__option:hover { background: var(--ivory-100); }

.form__option input { margin: 2px 0 0; flex: none; accent-color: var(--color-accent); }

/*
 * L'option retenue se voit à sa bordure, pas à une couleur de fond : l'or est
 * réservé au bouton d'appel à l'action et à un mot du titre (Charte, règle 2).
 */
.form__option:has(input:checked) {
  border-color: var(--color-text-primary);
  background: var(--ivory-100);
}

.form__option:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* ==========================================================================
   Champ mot de passe : bascule d'affichage
   ========================================================================== */

/*
 * Voir ce qu'on tape vaut mieux qu'une seconde saisie à l'aveugle : la
 * confirmation double la friction sans rien prouver, puisqu'on peut se tromper
 * deux fois de la même façon (ou coller la même erreur). Recommandation
 * courante depuis le NIST 800-63B.
 *
 * Le bouton n'existe que si le script tourne : c'est lui qui l'insère. Sans
 * JavaScript, le champ reste un champ mot de passe ordinaire, ce qu'il doit
 * être.
 */
/*
 * Champ d'adresse de site : le domaine, accolé et non saisissable.
 *
 * Le cadre passe sur l'enveloppe et l'`<input>` devient nu à l'intérieur.
 * Deux boîtes accolées se liraient comme deux champs, ce qui est exactement le
 * contresens à éviter : la partie fixe ne se saisit pas.
 */
.form__suffixed {
  display: flex;
  cursor: text;
  align-items: center;
  box-sizing: border-box;
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
}

/* Priorité sur la règle générale des champs texte, qui pose bordure et fond. */
.form .form__suffixed input[type="text"] {
  /*
   * Le champ se dimensionne sur ce qu'il contient, pour que « .nvbes.com »
   * suive la saisie au lieu de rester collé au bord droit : à dix centimètres
   * du texte tapé, il se lit comme une note et plus comme la fin de l'adresse.
   *
   * ⚠️ `field-sizing` n'existe pas partout (Safari ne l'a pas au 2026-09).
   * L'ailleurs retombe sur la largeur intrinsèque d'un champ texte, soit une
   * vingtaine de caractères : le suffixe est alors moins collé au texte, mais
   * toujours plus près que contre la bordure. Une dégradation, pas une casse,
   * et c'est pourquoi rien de fonctionnel n'en dépend.
   */
  field-sizing: content;
  /* 🔴 Indispensable : la règle générale des champs texte pose `width: 100%`,
     et une largeur explicite l'emporte sur `field-sizing`. Sans ce retour à
     `auto`, le champ occupe tout le cadre et le suffixe repart se coller à la
     bordure droite, exactement ce qu'on cherchait à éviter. */
  width: auto;
  flex: 0 1 auto;
  /*
   * 🔴 Presque zéro, et pas une largeur « raisonnable ». Un plancher de huit
   * caractères plaçait « .nvbes.com » à huit caractères du bord sur un champ
   * vide : accroché à rien, ni au bord ni à une saisie qui n'existe pas
   * encore. Réduit à la place du curseur, le champ vide se lit comme le
   * gabarit de l'adresse, et la saisie pousse le domaine vers la droite.
   *
   * ⚠️ Le champ devient donc minuscule quand il est vide, d'où le clic sur
   * l'enveloppe qui rend la main au champ (`forms.js`). Sans JavaScript, la
   * cible reste le début du champ : petite, mais c'est exactement là que
   * l'œil et le curseur vont.
   */
  min-width: 1ch;
  max-width: 100%;
  padding-right: 1px;
  background: transparent;
  border: 0;
  border-radius: 0;
}

/* L'anneau de mise au point passe sur l'enveloppe : porté par l'input seul,
   il ne cerclerait que la moitié gauche du champ. */
.form .form__suffixed input:focus-visible { outline: none; }

.form__suffixed:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.form__suffix {
  /* Occupe la place restante : le clic tombe alors sur l'enveloppe, qui rend
     la main au champ (voir `forms.js`), plutôt que dans un trou inerte. */
  flex: 1;
  padding-right: 14px;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui);
  color: var(--color-text-secondary);
  /* Une adresse ne se sélectionne pas à moitié : laisser copier « .nvbes.com »
     seul n'a aucun usage, et le curseur de texte suggérerait qu'on peut
     l'éditer. */
  user-select: none;
}

/* 🔴 Enroulé autour du seul champ, jamais autour de la rangée : celle-ci
   contient aussi l'étiquette, et le bouton s'y centrait sur les deux. Posé par
   `assets/forms.js`, qui crée ce conteneur. */
.form__password { position: relative; display: block; }

.form__password input { padding-right: 6.5rem; }

.form__password-toggle {
  position: absolute;
  /* `inset-block` plutôt qu'une hauteur : le bouton épouse le champ quelle que
     soit sa hauteur, et `align-items` centre son libellé dedans. */
  inset-block: 0;
  right: 10px;
  display: flex;
  align-items: center;
  padding: 0 6px;
  font-family: var(--ff-ui);
  font-size: var(--fs-micro);
  color: var(--color-text-secondary);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.form__password-toggle:hover { color: var(--color-text-primary); }

/* ==========================================================================
   Tom Select : mise à la Charte
   ========================================================================== */

/*
 * La feuille par défaut de Tom Select vient d'un autre monde visuel : bordures
 * à 1px, ombres, coins carrés, gris qui ne sont pas les nôtres. On ne la
 * remplace pas — la remplacer voudrait dire maintenir un thème complet — on
 * ramène seulement à la Charte ce qui se voit : la bordure à 0.5px, le fond
 * ivoire, l'absence d'ombre, et la même mise au point que les autres champs.
 *
 * ⚠️ À revérifier à toute montée de version de Tom Select : ces sélecteurs
 * suivent son balisage, qui n'est pas un contrat.
 */
/*
 * 🔴 La spécificité est le sujet, pas la couleur. Tom Select pose
 * `.ts-wrapper.single .ts-control { border-color: #b8b8b8 }` — trois classes —
 * alors qu'une règle `.ts-wrapper .ts-control` n'en a que deux. Une bordure
 * écrite en abrégé posait donc bien NOTRE largeur et LEUR couleur : un contour
 * qui n'était ni l'un ni l'autre, et qui ne ressemblait à aucun autre champ.
 * Les sélecteurs ci-dessous égalent la leur, à dessein.
 *
 * ⚠️ À revérifier à toute montée de version de Tom Select : ces sélecteurs
 * suivent son balisage et sa spécificité, qui ne sont pas un contrat.
 */
.ts-wrapper { font-family: var(--ff-ui); font-size: var(--fs-ui); }

.ts-wrapper .ts-control,
.ts-wrapper.single .ts-control {
  min-height: 0;
  padding: 11px 14px;
  /* Jamais sous 16px : en dessous, Safari zoome à la mise au point sur mobile
     et la page part de travers. Tom Select descend à 13px par défaut, ce qui
     rendait ce champ à la fois plus petit et plus court que ses voisins. */
  font-size: var(--fs-ui);
  /* Tom Select impose `line-height: 18px` là où un `<input>` suit la hauteur
     naturelle de sa police (~21px à 16px) : trois pixels d'écart, assez pour
     qu'un champ paraisse mal aligné à côté de son voisin sans qu'on sache
     dire pourquoi. `normal` remet les deux sur la même règle. */
  line-height: normal;
  color: var(--color-text-primary);
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
  box-shadow: none;
}

/* Le chevron a besoin de sa place, sinon une valeur longue passe dessous. */
.ts-wrapper.single .ts-control { padding-right: 34px; }

.ts-wrapper.focus .ts-control,
.ts-wrapper.single.focus .ts-control {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
  border-color: var(--color-border-control);
  box-shadow: none;
}

.ts-wrapper.single .ts-control,
.ts-wrapper.single.input-active .ts-control { background: var(--ivory-50); }

.ts-dropdown,
.ts-dropdown.single {
  margin-top: 6px;
  font-size: var(--fs-ui-sm);
  color: var(--color-text-primary);
  background: var(--ivory-50);
  border: 0.5px solid var(--color-border-control);
  border-radius: var(--radius-button);
  box-shadow: none;
}

.ts-dropdown .option { padding: 9px 14px; }

/* La surbrillance reprend l'ivoire des autres survols : l'or est réservé au
   bouton d'appel à l'action (Charte, règle 2). */
.ts-dropdown .active { background: var(--ivory-200); color: var(--color-text-primary); }

.ts-dropdown .optgroup-header {
  padding: 10px 14px 6px;
  font-size: var(--fs-micro);
  color: var(--color-text-secondary);
  background: transparent;
}

.ts-dropdown .no-results { padding: 9px 14px; color: var(--color-text-secondary); }

/* ==========================================================================
   Cartes de l'espace client
   ========================================================================== */

/*
 * Une carte par sujet — l'abonnement, le site — plutôt qu'une longue colonne :
 * ce sont des objets distincts, dont l'un aura bientôt sa propre liste. Bordure
 * à 0.5px et fond ivoire, sans ombre : la Charte n'en connaît qu'une, réservée
 * à l'aperçu de site.
 */
.card {
  padding: 24px 26px;
  background: var(--color-bg-card);
  border: 0.5px solid var(--color-border-card);
  border-radius: var(--radius-button);
}

.card > .t-h3 { margin: 0 0 18px; }

.card .btn { margin-top: 4px; }

.card > .t-note { margin-top: 10px; }

/*
 * File de jalons du déploiement (FON-90-E, écran client).
 *
 * 🔴 Nommée `milestones` et non `steps`, qui était DÉJÀ PRIS par la section
 * « Comment ça marche » de la vitrine. La première version a réutilisé ce nom :
 * les deux règles se sont additionnées sur le même sélecteur, la vitrine a
 * hérité d'un `display: flex` qui a couché son titre sur le côté, étiré sur
 * 1352px de haut, et d'un `padding: 0` qui a écrasé son `padding-top: 96px`.
 * Rien n'a échoué, aucun test n'a bronché, et c'est Sloan qui l'a vu.
 *
 * ⚠️ La feuille est unique et sans préfixe par écran : un nom de classe y est
 * un identifiant global. Avant d'en créer un, vérifier qu'il est libre.
 *
 * 🔴 Des jalons franchis, jamais un pourcentage : la première étape dure plus
 * de deux minutes quand les trois suivantes se comptent en secondes. Une barre
 * qui progresserait régulièrement paraîtrait bloquée pendant les deux tiers de
 * l'attente, et annoncerait un temps restant que personne ne connaît.
 *
 * Le trait de liaison est porté par chaque étape sauf la première, et non par
 * un fond de conteneur : il se colore alors étape par étape, sans qu'aucune
 * largeur n'ait à être calculée.
 */
.milestones {
  display: flex;
  margin: 4px 0 22px;
  padding: 0;
  list-style: none;
}

.milestones__step {
  position: relative;
  flex: 1;
  text-align: center;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  color: var(--color-text-secondary);
}

/* Le trait qui relie une étape à la précédente. */
.milestones__step + .milestones__step::before {
  content: '';
  position: absolute;
  top: 6px;
  right: 50%;
  left: -50%;
  height: 0.5px;
  background: var(--color-border-control);
}

.milestones__step.is-done::before { background: var(--color-accent); }

.milestones__dot {
  position: relative;
  z-index: 1;
  display: block;
  width: 13px;
  height: 13px;
  margin: 0 auto 8px;
  border: 0.5px solid var(--color-border-control);
  border-radius: 50%;
  background: var(--color-bg-page);
}

.milestones__step.is-done > .milestones__dot {
  border-color: var(--color-accent);
  background: var(--color-accent);
}

/*
 * L'étape en travail : un rond creux cerclé d'or, qui respire. C'est le seul
 * mouvement de l'écran, et il porte une information — quelque chose est en
 * cours — plutôt qu'une décoration.
 */
.milestones__step.is-current > .milestones__dot {
  border-color: var(--color-accent);
  animation: milestones-pulse 1.8s ease-in-out infinite;
}

.milestones__step.is-current > .milestones__label { color: var(--color-text-primary); }

@keyframes milestones-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgb(168 144 58 / 45%); }
  50%      { box-shadow: 0 0 0 5px rgb(168 144 58 / 0%); }
}

/*
 * ⚠️ Le mouvement disparaît pour qui l'a demandé, mais l'étape en cours doit
 * rester reconnaissable sans lui : le rond garde son cerclage d'or, et
 * `aria-current` dit la même chose à un lecteur d'écran.
 */
@media (prefers-reduced-motion: reduce) {
  .milestones__step.is-current > .milestones__dot { animation: none; }
}

/* Sous 30rem, quatre libellés côte à côte se coupent en deux lignes chacun. */
@media (max-width: 30rem) {
  .milestones__label { font-size: var(--fs-ui-xs); }
}


/*
 * Libellé et valeur en deux colonnes, alignés sur une grille commune : les
 * valeurs se lisent alors en colonne, ce qu'une liste de définitions par
 * défaut ne fait pas.
 */
.datalist {
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: 10px 24px;
  margin: 0 0 22px;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
}

.datalist dt { color: var(--color-text-secondary); }

.datalist dd { margin: 0; color: var(--color-text-primary); }

/* Sous 480px, la grille à deux colonnes écrase les valeurs longues (une raison
   sociale tient rarement en huit caractères) : on repasse en pile. */
@media (max-width: 480px) {
  .datalist { grid-template-columns: 1fr; gap: 4px; }
  .datalist dd { margin-bottom: 12px; }
}

/*
 * Un tableau de données pour l'administration (FON-97).
 *
 * 🔴 Nom pris dans le vocabulaire du domaine et vérifié libre avant d'être
 * créé : cette feuille n'a pas de préfixe par écran, donc un nom de classe y
 * est un identifiant global, et une collision ne produit aucune erreur — les
 * deux règles s'additionnent et l'écran qui n'attendait rien hérite de l'autre
 * (constaté le 2026-09-09, CLAUDE.md §20).
 *
 * Filets à 0.5px et aucune ombre, comme le reste : la Charte sépare par le
 * trait, jamais par l'élévation.
 */
.datatable {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--ff-ui);
  font-size: var(--fs-ui-sm);
  margin: 0 0 16px;
}

.datatable th,
.datatable td {
  text-align: left;
  padding: 10px 16px 10px 0;
  border-bottom: 0.5px solid var(--color-border);
  vertical-align: baseline;
}

.datatable th {
  color: var(--color-text-secondary);
  font-weight: 500;
}

.datatable td { color: var(--color-text-primary); }

/* Une taille (« 57,0 Ko ») ne se coupe pas entre le nombre et l'unité : vu
   dans Chrome le 2026-09-15 sur l'écran des sauvegardes, en colonne étroite,
   « 57,0 » sur une ligne et « Ko » sur la suivante. Nom tiré du domaine plutôt
   que `.nowrap`, générique et donc exactement ce qui finit par être pris. */
.datatable .quantity { white-space: nowrap; }

/* Le nom technique d'un environnement se recopie dans une console : il doit se
   lire caractère par caractère, d'où la chasse fixe. */
.datatable code {
  font-family: var(--ff-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.95em;
}
