/* Kizly — vitrine
   Palette claire sur :root NU — c'est aussi le repli des navigateurs qui ne
   connaissent pas light-dark(). Les deux thèmes sont ensuite écrits UNE SEULE
   FOIS dans le bloc @supports ci-dessous. */

:root {
  /* Prévient le navigateur que la page gère les deux thèmes. Sans ça, les
     éléments qu'il dessine lui-même — le curseur du calculateur, les barres de
     défilement — restent clairs sur une page sombre. */
  color-scheme: light dark;

  /* PALETTE DU LOGO. Les six teintes sont relevées sur le fichier lui-même
     (assets/marque/), pas approximées à l'œil : le rose profond de la barre
     du K, le bleu et le cyan de la lentille haute, l'ambre et le vert de la
     lentille basse.

     Une seule domine — le rose, qui est aussi la plus grosse masse du logo —
     et porte toutes les actions. Les autres ne servent qu'en touches : un
     graphique où six couleurs se valent ne dit plus laquelle compte.

     Tout ce qui porte du texte est calculé à 4,5:1 minimum sur son fond.
     MIROIR : frontend/static/style.css tient la même palette pour
     l'application. Les deux se modifient ensemble. */
  --bg: #FBF7F9;
  --surface: #FFFFFF;
  --text: #241A20;
  --text-muted: #6B5C64;
  --accent: #C4185C;
  --accent-text: #FFFFFF;
  --accent-soft: #FBE6EE;
  --success: #0B7A52;
  --border: #EBDDE4;

  /* Touches. Jamais une action, jamais un fond de bloc : un liseré, une
     icône, une pastille de statut. */
  --bleu: #1E5FB8;
  --cyan: #0E7C99;
  --ambre: #8A6410;

  --serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Flèche des boutons (composant 21st « flow-button »), dessinée ici plutôt
     que chargée : aucune requête réseau, et elle sert de MASQUE, donc sa
     couleur est celle du bouton et suit le thème. Le trait est noir sans que
     ça n'ait d'importance — seule son opacité compte dans un masque. */
  --fleche: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3Cpath d='m12 5 7 7-7 7'/%3E%3C/svg%3E");

  --measure: 68ch;
  --radius: 10px;
  --gap: clamp(1rem, 3vw, 1.75rem);
  --section-y: clamp(3.5rem, 9vw, 7rem);
}

/* LES DEUX THÈMES, ÉCRITS UNE SEULE FOIS — même mécanisme que
   frontend/static/style.css, et pour la même raison. Le thème sombre passait
   par un @media (prefers-color-scheme: dark) qui redéclarait les jetons ;
   depuis que le vendeur peut CHOISIR son thème dans l'application, et que ce
   choix traverse les deux domaines par un cookie, il aurait fallu un
   TROISIÈME jeu de valeurs pour le choix explicite.

   light-dark() met les deux valeurs côte à côte et lit `color-scheme` : le
   choix n'a donc plus qu'à FORCER color-scheme, deux lignes au lieu d'une
   palette entière.

   Pourquoi @supports et pas une déclaration de repli au-dessus : une propriété
   personnalisée n'est PAS validée à la déclaration. `--bg: light-dark(…)` est
   toujours accepté, même là où la fonction n'existe pas ; l'erreur ne sort
   qu'à la substitution, et `background: var(--bg)` tombe alors dans le vide.
   Les navigateurs d'avant mai 2024 gardent le thème clair : ils perdent le
   sombre, ils ne perdent pas la page. */
@supports (color: light-dark(#000, #fff)) {
  :root {
    --bg: light-dark(#FBF7F9, #171215);
    --surface: light-dark(#FFFFFF, #211A1F);
    --text: light-dark(#241A20, #F4ECF0);
    --text-muted: light-dark(#6B5C64, #B3A3AC);
    /* Le rose profond du thème clair tombe à 2,4:1 sur le fond sombre : la
       nuance haute du logo prend le relais. Et sur un accent clair, le texte
       du bouton doit être sombre. */
    --accent: light-dark(#C4185C, #F2789B);
    --accent-text: light-dark(#FFFFFF, #171215);
    --accent-soft: light-dark(#FBE6EE, #3A1C2A);
    --success: light-dark(#0B7A52, #3FD69B);
    --border: light-dark(#EBDDE4, #382C33);

    --bleu: light-dark(#1E5FB8, #6FA8E8);
    --cyan: light-dark(#0E7C99, #39C6E7);
    --ambre: light-dark(#8A6410, #EFB42D);
  }

  /* Le choix du vendeur, posé sur <html> par le script d'amorçage du <head>.
     Sans attribut, `color-scheme: light dark` laisse décider le système,
     exactement comme avant que ce réglage existe. */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }
}

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

/* `hidden` ne pose que `display: none` via la feuille du navigateur : toute
   règle d'auteur qui déclare `display` l'emporte. Cette ligne le rend fiable
   quoi qu'on ajoute plus tard. */
[hidden] { display: none !important; }

/* Contenu PRÉ-RENDU pour les moteurs (ops/vitrine_statique.py). La page arrive
   déjà remplie — c'est ce que lisent les robots qui n'exécutent pas le
   JavaScript, dont ceux de ChatGPT. main.js le remplace par la version animée
   et retire l'attribut ; en attendant, un visiteur ne doit pas voir la version
   statique se transformer sous ses yeux. `.js` est posée par theme.js : sans
   JavaScript, rien n'est masqué et la page reste lisible. */
.js [data-statique] > * { display: none !important; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 0.5em;
  /* Équilibre les lignes d'un titre au lieu de laisser un mot seul sur la
     dernière. Ignoré par les navigateurs qui ne connaissent pas la propriété. */
  text-wrap: balance;
}
h1 { font-size: clamp(2.1rem, 5.5vw, 3.4rem); font-variation-settings: 'opsz' 144; }
h2 { font-size: clamp(1.6rem, 3.6vw, 2.3rem); font-variation-settings: 'opsz' 72; }
h3 { font-size: 1.15rem; }
p { margin: 0 0 1rem; max-width: var(--measure); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }

img { max-width: 100%; height: auto; display: block; }

/* Un focus toujours visible, jamais retiré : c'est la seule boussole de qui
   navigue au clavier. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

.wrap { width: min(1120px, 100% - 2.5rem); margin-inline: auto; }
.narrow { max-width: 44rem; }
.muted { color: var(--text-muted); }
.center { text-align: center; margin-inline: auto; }
.center p { margin-inline: auto; }

section { padding-block: var(--section-y); }
.section-head { margin-bottom: clamp(2rem, 5vw, 3.25rem); }
.section-head p { font-size: 1.05rem; color: var(--text-muted); }

/* Passer d'un lien d'ancre à sa cible sans que l'entête fixe la recouvre. */
[id] { scroll-margin-top: 5rem; }

/* --- Liens d'évitement et entête --------------------------------------- */
.skip {
  position: absolute; left: -9999px;
  background: var(--surface); color: var(--text);
  padding: 0.75rem 1.25rem; border-radius: var(--radius); z-index: 100;
}
.skip:focus { left: 1rem; top: 1rem; }

header.site {
  position: sticky; top: 0; z-index: 50;
  /* Repli d'abord : un navigateur sans color-mix ignore la ligne suivante
     et garde un fond opaque plutôt qu'un entête transparent illisible. */
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
/* 2rem/1.1rem depuis le 05/09 (1.25rem/0.85rem avant) : le groupe de droite
   (langue, bouton, pastille) se lisait tassé contre la nav, avec un seul
   grand vide après le logo pour absorber tout l'espace. Le repli étroit
   ci-dessous reste à 0.75rem, la nav étant alors masquée. */
.bar { display: flex; align-items: center; gap: 2rem; padding-block: 1.1rem; }
/* La barre SORT de la mesure de 1120 px, le 06/09 : sur un écran large, tout
   se serrait au centre pendant que 400 px de vide restaient de chaque côté —
   et le remède du 05/09 (élargir les écarts) ne pouvait rien contre ça, il
   redistribuait de l'espace à l'intérieur d'un cadre trop étroit.

   La barre est la seule chose de la page à faire ça, exprès : elle ne se LIT
   pas, elle se pointe. Le reste garde sa mesure — une phrase à 1900 px de
   large ne se lit plus, ce qui est justement la raison d'être de `.wrap`.
   Un plafond reste posé à 1760 px : au-delà, le logo et le bouton d'appel se
   retrouvent à deux mètres l'un de l'autre sur un écran de bureau large. */
.bar.wrap {
  width: 100%; max-width: 1760px;
  padding-inline: clamp(1.25rem, 3.5vw, 3rem);
}

/* La barre n'avait AUCUN point de rupture : sur un téléphone, le logo, les
   cinq ancres et les deux boutons ne tiennent pas dans 390 px, et toute la
   vitrine défilait latéralement de 200 px. Constaté le 30/08 en regardant.

   Les ancres disparaissent, pas le bouton : c'est la seule porte d'entrée,
   et les sections restent atteignables en faisant défiler la page. */
/* 52rem jusqu'au 04/09. Avec SIX ancres au lieu de quatre, la barre passait sur
   deux lignes dès 1010 px et débordait latéralement vers 860 px — le défaut
   corrigé le 30/08, revenu par la porte du contenu. Mesuré : elle tient sur une
   ligne à partir de 1020 px, plus bas non. */
@media (max-width: 63.5rem) {
  nav.site { display: none; }
  .bar { gap: 0.75rem; }
  /* C'est la TAILLE DU TEXTE qui dégage de la place ici, plus la marge :
     depuis le bouton du composant 21st, la marge doit rester assez large
     pour contenir la flèche (1rem de flèche posée à 0.75rem du bord, soit
     1.75rem). À 0.85rem, comme avant, la flèche mordait sur le texte. */
  .bar .btn { --btn-px: 1.85rem; font-size: 0.9rem; }
  .logo span { font-size: 1.15rem; }
}
/* Connecté, le bouton d'appel et la pastille de compte mènent au même endroit.
   Sur un téléphone ils ne tiennent pas ensemble à côté du logo — « Ouvrir mon
   tableau de bord » passait sur deux lignes et recouvrait la marque. La
   pastille reste : elle est plus courte et dit en plus DE QUI il s'agit. */
@media (max-width: 34rem) {
  body.connecte .bar .btn { display: none; }
}

/* Sous 30rem il ne reste plus de place pour le code À CÔTÉ du drapeau : le
   drapeau seul suffit, l'option retenue restant marquée par sa pastille. */
@media (max-width: 30rem) {
  .lang-opt span { display: none; }
  .lang-opt { padding: 0.3rem 0.4rem; }
}
/* --- Pastille de compte, en haut à droite ---------------------------------
   MIROIR : landing/style.css tient la même. C'est la même pastille des deux
   côtés, elles se modifient ensemble.

   PAS la photo de profil Google : celle-ci vit sur lh3.googleusercontent.com,
   et l'afficher ferait sortir une requête vers un tiers depuis nos pages — ce
   que la vitrine s'interdit. L'initiale est dessinée ici, et le « G » officiel
   posé en médaillon quand le compte se connecte par Google. */
.compte-pastille {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--accent-soft); color: var(--accent);
  font: inherit; font-size: 14px; font-weight: 700; line-height: 1;
  text-decoration: none; cursor: pointer;
  transition: border-color .2s ease, transform .15s ease;
}
.compte-pastille:hover { border-color: var(--accent); color: var(--accent); }
.compte-pastille:active { transform: scale(.94); }
.compte-pastille:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Le médaillon déborde volontairement du disque : posé dedans, il rognerait
   l'initiale, qui est la seule chose qui distingue deux comptes. */
.compte-google {
  position: absolute; right: -3px; bottom: -3px;
  display: inline-flex; padding: 2px;
  background: var(--surface); border-radius: 50%;
  box-shadow: 0 0 0 1px var(--border);
}
.compte-google svg { display: block; }
@media (prefers-reduced-motion: reduce) {
  .compte-pastille { transition: none; }
}

/* Sélecteur de langue de la barre. Deux pastilles à drapeau : la langue est
   un choix qu'on fait une fois, elle n'a pas à peser autant qu'une ancre de
   navigation. Le drapeau seul serait ambigu (le Royaume-Uni ne dit pas
   « anglais » à tout le monde), le code seul serait muet — les deux ensemble
   se lisent d'un coup d'œil. */
.compte-vitrine { display: inline-flex; flex: none; }
.lang-switch { display: inline-flex; gap: 0.15rem; flex: none; }
.lang-opt {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid transparent; border-radius: 999px;
  background: none; color: var(--text-muted);
  font: inherit; font-size: 0.8rem; font-weight: 600; letter-spacing: .02em;
  /* Des liens depuis le 13/09 (un robot suit un lien, pas un bouton) : sans
     cette ligne, le soulignement par défaut d'un <a> revenait sous « FR ». */
  text-decoration: none;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease;
}
/* Le drapeau non retenu passe en gris : la FORME reste distincte — le
   croisillon britannique et les trois bandes françaises ne se confondent
   pas — mais l'option choisie se voit sans avoir à comparer. */
.lang-opt svg { border-radius: 2px; filter: grayscale(1); opacity: .75; transition: filter .2s, opacity .2s; }
.lang-opt:hover { color: var(--accent); background: var(--accent-soft); }
.lang-opt:hover svg { filter: none; opacity: 1; }
.lang-opt.actif {
  color: var(--accent); background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.lang-opt.actif svg { filter: none; opacity: 1; }
.lang-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .lang-opt, .lang-opt svg { transition: none; }
}

.logo {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--serif); font-weight: 600; font-size: 1.3rem;
  color: var(--text); text-decoration: none;
}
/* Hauteur fixée en CSS et dimensions posées dans le HTML : sans elles,
   l'entête sursaute au chargement de l'image. */
.logo img { width: 2rem; height: 2rem; display: block; }
nav.site ul { display: flex; gap: 1.35rem; list-style: none; margin: 0; padding: 0;
  flex-wrap: nowrap; }
/* Les six ancres (deux de plus depuis le 04/09) ne tiennent plus à 1024 px en
   français : « Comment ça marche » se coupait en deux et la barre doublait de
   hauteur. Elles RÉTRÉCISSENT au lieu de se replier — un libellé sur deux
   lignes se lit moins bien qu'un libellé plus petit, et la barre doit garder
   une seule hauteur. */
nav.site a { white-space: nowrap; }
@media (max-width: 76rem) {
  nav.site ul { gap: 0.9rem; }
  nav.site a { font-size: 0.88rem; }
}
@media (max-width: 66rem) {
  nav.site ul { gap: 0.65rem; }
  nav.site a { font-size: 0.82rem; }
  .bar .btn { --btn-px: 1.6rem; font-size: 0.86rem; }
  .bar { gap: 0.8rem; }
}
/* Aucun survol distinct n'existait (juste un texte souligné, --text) ; le
   clic n'avait aucun retour du tout. Reprise en tons roses aux deux états,
   le 31/08 : un filet qui s'ouvre depuis le centre au survol — repris de
   `.faq-panel` (grid-template-rows: 0fr → 1fr), ici sur scaleX — et un flash
   bref à l'appui, seul signal possible sur une ancre qui saute aussitôt à sa
   section. */
nav.site a {
  position: relative;
  color: var(--text-muted); text-decoration: none; font-size: 0.95rem;
  transition: color .25s ease;
}
nav.site a::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -0.3rem;
  height: 2px; border-radius: 2px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: center;
  transition: transform .35s cubic-bezier(.34, 1.56, .64, 1);
}
nav.site a:hover { color: var(--accent); }
nav.site a:hover::after { transform: scaleX(1); }
nav.site a:active { color: color-mix(in srgb, var(--accent) 80%, #000); }
@media (prefers-reduced-motion: reduce) {
  nav.site a, nav.site a::after { transition: none; }
}

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

/* Supprime le délai de 300 ms que les navigateurs mobiles s'accordent pour
   guetter un double-tap, et remplace le halo gris par défaut d'Android par un
   voile aux couleurs de la page. */
a, button, input, label, .btn {
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(196, 24, 92, 0.15);
}

/* Transposé du composant 21st « flow-button ». Au repos une pastille cerclée
   et vide ; au survol un disque part du centre et remplit le bouton, le rayon
   se resserre, et la flèche sort par la droite.

   DEUX écarts demandés le 31/08 : le TEXTE NE BOUGE PAS (l'original le
   décale de 12 px vers la droite) et il n'y a QU'UNE flèche (l'original en
   fait entrer une seconde par la gauche). Sans le décalage du texte, la
   flèche entrante n'aurait de toute façon plus eu de place à occuper : les
   deux vont ensemble.

   MIROIR : frontend/static/style.css tient le même bouton pour l'application.
   Les deux se modifient ensemble — c'est le même bouton des deux côtés.

   Écarté du composant : sa couleur (#111 sur blanc) au profit de la teinte de
   marque, et sa bordure à 40 % d'opacité — mesurée à 2,2:1 sur nos deux fonds,
   sous le seuil de 3:1 d'un composant d'interface. À 75 % elle passe partout
   (3,4:1 au pire, sur la variante « secondary » en thème clair).

   TOUT est en CSS, aucun élément à injecter dans le bouton : le disque et la
   flèche sont ses deux pseudo-éléments. C'est ce qui permet au bouton de valoir
   aussi pour ceux que main.js crée après coup (cartes de forfait, bandeau du
   calculateur) sans que rien n'ait à les « rattraper ». Même raison que
   l'accordéon de la FAQ, animé sans mesurer un seul pixel en JS. */
.btn {
  /* Une seule teinte à changer par variante, et le disque, la bordure, le
     texte et la flèche suivent. */
  --btn-teinte: var(--accent);
  --btn-encre: var(--accent-text);
  /* La marge doit CONTENIR la flèche : 2rem de marge pour une flèche de
     1rem posée à 0.75rem du bord, soit 1.75rem occupés. Sous cette valeur
     elle mord sur le texte — vu sur « Resend email », le bouton le plus
     court de l'application. */
  --btn-px: 2rem;

  position: relative;
  /* Le disque est en z-index négatif : `isolate` l'empêche de passer derrière
     le fond du bouton, `overflow` le taille à la pastille — et la flèche
     disparaît en sortant du bord au lieu de flotter à côté. */
  isolation: isolate;
  overflow: hidden;

  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font: inherit; font-weight: 600;
  padding: 0.8rem var(--btn-px);
  background: transparent;
  color: var(--btn-teinte);
  border: 1.5px solid color-mix(in srgb, var(--btn-teinte) 75%, transparent);
  border-radius: 100px;
  cursor: pointer; text-decoration: none;
  transition:
    color .6s cubic-bezier(.23, 1, .32, 1),
    border-color .6s cubic-bezier(.23, 1, .32, 1),
    border-radius .6s cubic-bezier(.23, 1, .32, 1),
    transform .15s;
}

/* `:focus-visible` en plus de `:hover` : au clavier, le bouton doit se
   remplir comme il le fait à la souris, sinon la seule marque du focus est le
   liseré du navigateur sur une pastille vide. */
.btn:hover, .btn:focus-visible {
  color: var(--btn-encre);
  border-color: transparent;
  border-radius: 12px;
}
.btn:active { transform: scale(.95); }

/* Le disque. Un `scale` plutôt que la largeur et la hauteur animées de
   l'original : la carte graphique le compose seule, sans recalculer la mise
   en page à chaque image. 130 % de la largeur en carré couvre la diagonale
   de tous nos boutons, du plus étroit au plus large. */
.btn::before {
  content: "";
  position: absolute; z-index: -1;
  left: 50%; top: 50%;
  width: 130%; aspect-ratio: 1;
  border-radius: 50%;
  background: var(--btn-teinte);
  transform: translate(-50%, -50%) scale(0);
  transition: transform .8s cubic-bezier(.19, 1, .22, 1);
}
.btn:hover::before, .btn:focus-visible::before { transform: translate(-50%, -50%) scale(1); }

/* La flèche : un masque et non une image de fond, pour que sa couleur soit
   celle du bouton et change avec lui au moment où le disque passe dessous.

   Sous @supports : sans masque, un bloc coloré couvrirait tout le bouton. Le
   reste (disque, bordure, rayon, texte) fonctionne sans, seule la flèche
   manque. */
@supports ((mask-image: none) or (-webkit-mask-image: none)) {
  .btn::after {
    content: "";
    position: absolute; inset: 0; z-index: 1;
    pointer-events: none;
    background-color: var(--btn-teinte);
    -webkit-mask-image: var(--fleche);
            mask-image: var(--fleche);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 1rem 1rem;
            mask-size: 1rem 1rem;
    /* En place à droite au repos, sortie du cadre au survol. */
    -webkit-mask-position: calc(100% - 0.75rem) center;
            mask-position: calc(100% - 0.75rem) center;
    transition:
      background-color .6s cubic-bezier(.23, 1, .32, 1),
      -webkit-mask-position .8s cubic-bezier(.34, 1.56, .64, 1),
              mask-position .8s cubic-bezier(.34, 1.56, .64, 1);
  }
  .btn:hover::after, .btn:focus-visible::after {
    background-color: var(--btn-encre);
    -webkit-mask-position: calc(100% + 1.5rem) center;
            mask-position: calc(100% + 1.5rem) center;
  }
}

.btn-primary { --btn-teinte: var(--accent); --btn-encre: var(--accent-text); }
.btn-ghost   { --btn-teinte: var(--text-muted); --btn-encre: var(--bg); }
.btn-lg { --btn-px: 2.5rem; padding-block: 1rem; font-size: 1.05rem; }

/* Rien ne bouge pour qui a demandé qu'on lui épargne le mouvement : le
   bouton se remplit quand même, d'un coup. La couleur reste, elle porte
   l'information ; c'est le glissement qui gêne. */
@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .btn::after { transition: none; }
}

.cta-block { display: flex; flex-direction: column; align-items: flex-start; gap: 0.6rem; }
.center .cta-block { align-items: center; }
/* La mention sur la carte reste COLLÉE au bouton : reléguée en bas de page,
   elle se découvre après le clic. */
.cta-note { font-size: 0.9rem; color: var(--text-muted); margin: 0; }

/* --- Fond de page ----------------------------------------------------------
   Transposé du composant 21st « light-theme-tailwind-css-background-snippet »,
   puis repris sur le principe du fond de la page d'entrée
   (frontend/static/style.css) : des masses de rose floutées qui dérivent
   lentement, plutôt qu'un dégradé plat.

   `position: fixed` et non `absolute` : ancré au DOCUMENT, le fond ne
   couvrait que le haut de la vitrine et disparaissait au premier défilement —
   c'est ce qui est reproché le 31/08. Fixé à la FENÊTRE, il reste identique
   partout et ce sont les sections qui défilent au travers, ce qui donne aussi
   au fond sa profondeur : il ne bouge pas au même rythme que le contenu.

   Donc plus de conteneur `.glow-halo` non plus : un élément fixe se cale sur
   la fenêtre, pas sur son parent, et la borne « jusqu'à la FAQ » n'avait plus
   de sens. Les deux couches sont posées sur `body`, comme sur la page
   d'entrée. Elles peignent au-dessus du fond du canevas (le fond de `body`
   est propagé au canevas, `html` n'en portant aucun) et sous tout le contenu.

   `inset: -18%` déborde volontairement de la fenêtre : la translation et le
   léger grossissement ne doivent jamais amener un bord de masse dans le
   champ. Un élément fixe n'allonge pas la zone défilable, donc ce
   débordement ne rouvre pas le défilement latéral corrigé le 30/08.

   Les masses sont réparties sur toute la HAUTEUR de la fenêtre, et non
   groupées en haut : le fond est désormais visible en permanence, une
   concentration sur le tiers haut se lirait comme un bandeau.

   Couleurs en `color-mix` sur --accent : le thème sombre suit tout seul,
   aucune seconde palette à tenir à jour. Durées reprises de la page d'entrée
   (37 s / 53 s), premières entre elles pour que le cycle ne se voie pas. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: -18%;
  z-index: -1;
  pointer-events: none;
}
body::before {
  background:
    radial-gradient(38% 42% at 18% 16%, color-mix(in srgb, var(--accent) 24%, transparent), transparent 70%),
    radial-gradient(34% 38% at 84% 34%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 68%);
  filter: blur(30px);
  animation: fond-derive-a 37s ease-in-out infinite alternate;
}
body::after {
  background:
    radial-gradient(40% 44% at 66% 84%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 70%),
    radial-gradient(30% 34% at 10% 72%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 66%);
  filter: blur(34px);
  animation: fond-derive-b 53s ease-in-out infinite alternate;
}
@keyframes fond-derive-a {
  from { transform: translate3d(-2%, -1%, 0) scale(1); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.1); }
}
@keyframes fond-derive-b {
  from { transform: translate3d(2%, 1%, 0) scale(1.06); }
  to   { transform: translate3d(-3%, -2%, 0) scale(1); }
}
/* Le mouvement s'arrête, la teinte reste : c'est le mouvement qui gêne. */
@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none; }
}

/* Les deux seules sections qui gardent un fond opaque, donc masquent le
   fond de page : le bandeau final et — plus bas — le bandeau sombre du
   calculateur. Dans les deux cas c'est délibéré, ce sont les respirations
   qui donnent son rythme à la page ; un fond continu du premier au dernier
   pixel n'aurait plus aucun relief. */

.hero { padding-top: clamp(2.5rem, 6vw, 4.5rem); }
.hero h1 { max-width: 16ch; }
.hero .lede { font-size: clamp(1.05rem, 2.1vw, 1.25rem); color: var(--text-muted); max-width: 46ch; }
.hero-grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: center; }
/* La colonne de droite l'emporte maintenant sur celle du titre : depuis le
   01/09 elle ne porte plus une réservation vide mais la démonstration
   elle-même, qui dit la promesse mieux que la phrase d'à côté. Le titre garde
   de quoi tenir sur deux lignes — vérifié, il n'en prend pas une troisième.

   1.45 et pas plus : c'est le point où l'image est la plus grande sans coûter
   une ligne de plus au sous-titre. Mesuré — 1.3 donne 580 px et 4 lignes,
   1.45 donne 609 px et 4 lignes toujours, 1.6 donne 634 px mais passe à 5. */
@media (min-width: 62rem) { .hero-grid { grid-template-columns: 1fr 1.45fr; } }

/* Emplacement de démonstration : vide et SIGNALÉ tant qu'aucune image réelle
   n'existe. Rien n'est généré pour faire joli. */
.demo-slot {
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--surface);
  padding: clamp(1.5rem, 4vw, 2.5rem); text-align: center;
}
.demo-slot .demo-title { font-weight: 600; margin-bottom: 0.35rem; }
.demo-slot p { color: var(--text-muted); font-size: 0.92rem; margin: 0 auto; max-width: 34ch; }

/* Une fois les deux photos posées, l'encart n'est plus une réservation : le
   trait pointillé et le fond de carte s'effacent, l'image devient le bloc. */
.demo-slot-plein { border-style: solid; padding: 0.6rem; }
.demo-slot-plein .demo-title { margin-bottom: 0.6rem; }

/* --- Comparateur avant / après --------------------------------------------
   Transposé du composant 21st « image-comparison-slider-horizontal ».

   La position est tenue par un `<input type="range">` transparent posé sur
   l'image, et non par un état JS avec ses écouteurs sur `document`. Le
   navigateur donne alors gratuitement le glissement souris ET doigt, les
   flèches du clavier, Origine/Fin, le rôle ARIA et l'annonce de la valeur —
   dont l'original ne fournit que le rôle, sans jamais écouter une touche.

   **Le pouce doit avoir une largeur NULLE.** Un navigateur place le pouce
   d'un `range` à `valeur × (largeur − largeurDuPouce) + largeurDuPouce / 2` :
   dès qu'il a une largeur, la valeur ne correspond plus au trait qu'on dessine
   à `valeur %`, et l'écart se voit aux deux extrémités. À zéro, les deux
   coïncident exactement et rien n'est à compenser. Le pouce reste
   attrapable : un `range` suit le pointeur dès l'appui sur la piste. */
.cmp {
  position: relative;
  aspect-ratio: 5 / 4;          /* format demandé, et celui des deux fichiers */
  overflow: hidden;
  border-radius: calc(var(--radius) - 2px);
  background: var(--accent-soft);   /* le temps que les images arrivent */
  user-select: none;
}
.cmp-couche { position: absolute; inset: 0; }
.cmp-couche img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Seule la couche du dessus est découpée. `inset()` plutôt que le `polygon()`
   de l'original : quatre valeurs au lieu de quatre points, pour le même
   rectangle. */
.cmp-couche-avant { clip-path: inset(0 calc((100 - var(--pos)) * 1%) 0 0); }

/* Étiquettes posées HORS des couches, donc jamais découpées — voir main.js.
   Fond opaque plutôt qu'un voile : le contraste est alors garanti quelle que
   soit la photo dessous, ce qu'un texte posé à même l'image ne peut pas
   promettre. */
.cmp-etiquette {
  position: absolute; top: 0.6rem;
  padding: 0.25rem 0.6rem; border-radius: 999px;
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em;
  background: var(--surface); color: var(--text);
  box-shadow: 0 2px 8px rgba(36, 26, 32, .18);
  white-space: nowrap;
  /* Elles passent AU-DESSUS du trait, donc devant la saisie : sans ça, elles
     lui voleraient le pointeur et l'image ne serait plus attrapable sous
     elles. Elles ne servent qu'à nommer, jamais à agir. */
  pointer-events: none;
}
/* Chaque étiquette s'efface quand SON CÔTÉ disparaît — pas quand le texte
   commencerait à être coupé, ce qui dépendrait de sa longueur. `opacity`
   étant bornée à [0,1] par le navigateur, une règle de trois suffit : aucun
   JS, aucune mesure, et rien à revoir si le propriétaire rallonge un
   libellé. */
.cmp-avant {
  left: 0.6rem;
  opacity: calc((var(--pos) - 4) / 12);
}
.cmp-apres {
  right: 0.6rem;
  opacity: calc((96 - var(--pos)) / 12);
}

.cmp-trait {
  position: absolute; top: 0; bottom: 0;
  left: calc(var(--pos) * 1%);
  width: 2px; margin-left: -1px;
  background: var(--surface);
  box-shadow: 0 0 12px rgba(36, 26, 32, .35);
  pointer-events: none;
}
.cmp-poignee {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  /* `align-items` ne centre que sur l'axe VERTICAL. Sans `justify-content`,
     les deux chevrons se tassaient au bord gauche du disque — ils occupent
     24 px dans un cercle de 44. */
  display: flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; border-radius: 50%;
  background: var(--surface); color: var(--accent);
  box-shadow: 0 4px 16px rgba(36, 26, 32, .28);
  transition: transform .25s ease, box-shadow .25s ease;
}
.cmp-poignee svg { margin: -2px; }
.cmp:hover .cmp-poignee { transform: translate(-50%, -50%) scale(1.08); }

/* La saisie recouvre l'image et reste invisible : c'est elle qu'on manipule,
   la poignée n'est que son portrait. `height: 100%` pour qu'on puisse
   l'attraper n'importe où sur la photo, comme dans l'original. */
.cmp-saisie {
  position: absolute; inset: 0;
  width: 100%; height: 100%; margin: 0;
  appearance: none; -webkit-appearance: none;
  background: none; cursor: ew-resize;
}
.cmp-saisie::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 0; height: 100%;
}
.cmp-saisie::-moz-range-thumb {
  width: 0; height: 100%; border: none; background: none;
}
/* Le focus se pose sur la POIGNÉE, pas sur le rectangle de la saisie : un
   liseré autour de toute la photo ne dit pas où l'on agit. */
.cmp-saisie:focus { outline: none; }
.cmp-saisie:focus-visible ~ .cmp-trait .cmp-poignee {
  outline: 2px solid var(--accent); outline-offset: 3px;
}


@media (prefers-reduced-motion: reduce) {
  .cmp-poignee, .cmp:hover .cmp-poignee { transition: none; transform: translate(-50%, -50%); }
}


/* --- Grilles de cartes -------------------------------------------------- */
.grid { display: grid; gap: var(--gap); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.5rem;
}
.card p { margin-bottom: 0; color: var(--text-muted); }
.card .icon { color: var(--accent); margin-bottom: 0.9rem; }
.step-n {
  font-family: var(--serif); font-size: 0.85rem; font-weight: 600;
  color: var(--accent); letter-spacing: 0.06em;
}

.callout {
  margin-top: var(--gap);
  border-left: 3px solid var(--accent); background: var(--accent-soft);
  border-radius: 0 var(--radius) var(--radius) 0; padding: 1.25rem 1.5rem;
}
.callout h3 { margin-bottom: 0.35rem; }
.callout p { margin-bottom: 0; }

/* --- Les cinq visuels --------------------------------------------------- */
.roles { display: grid; gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
@media (min-width: 48rem) { .roles { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .roles { grid-template-columns: repeat(3, 1fr); } }
.role { background: var(--surface); padding: 1.5rem; }
.role .num {
  font-family: var(--serif); font-size: 2rem; font-weight: 600;
  color: var(--accent); opacity: .35; line-height: 1;
}
.role h3 { margin: 0.4rem 0 0.35rem; }
.role p { color: var(--text-muted); margin: 0; font-size: 0.95rem; }

/* --- Galerie ------------------------------------------------------------ */
.filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: var(--gap); }
/* Filtres de la galerie : n'avaient aucun état de survol, et la sélection au
   clic changeait de couleur d'un coup sec, sans transition. Repris le 31/08
   en tons roses aux deux états — survol et clic — comme demandé.

   Le survol teinte et soulève très légèrement (1px) ; le clic remplit en
   douceur et ajoute une ombre portée de la même teinte, pour que la pastille
   choisie se détache vraiment des autres plutôt que de simplement changer de
   couleur. `:active` donne le retour d'appui immédiat (tassement bref) que
   `[aria-pressed]`, qui ne se pose qu'après le clic, ne peut pas donner. */
.chip {
  font: inherit; font-size: 0.9rem; padding: 0.4rem 0.9rem;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-muted);
  border-radius: 999px; cursor: pointer;
  transition: background-color .25s ease, border-color .25s ease,
              color .25s ease, box-shadow .25s ease, transform .15s ease;
}
.chip:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  transform: translateY(-1px);
}
.chip:active { transform: translateY(0) scale(.94); }
.chip[aria-pressed="true"] {
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 35%, transparent);
}
.chip[aria-pressed="true"]:hover {
  background: color-mix(in srgb, var(--accent) 86%, #000);
  color: var(--accent-text);
}
@media (prefers-reduced-motion: reduce) {
  .chip { transition: none; }
}

.case { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.25rem; margin-bottom: var(--gap); }
.case-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.75rem; margin-bottom: 1rem; }
.case-head h3 { margin: 0; }
.tag {
  font-size: 0.78rem; padding: 0.2rem 0.6rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--text-muted); border: 1px solid var(--border);
}
.case-imgs { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); }
.case figure { margin: 0; }
.case figcaption { font-size: 0.8rem; color: var(--text-muted); margin-top: 0.35rem; }

/* --- Calculateur ------------------------------------------------------------
   Repris d'un calculateur ListifyAI vu par le propriétaire, mais sans ses
   hypothèses modifiables : un champ éditable par hypothèse rassure sur le
   papier et se traduit, à l'usage, par un curseur que personne ne touche. Les
   valeurs sont FIXÉES (`calculator.assumptions` dans le JSON, pas des
   `<input>`) et données en toutes lettres dans la note de bas de calcul —
   rien n'est caché, seulement plus rien à remplir.

   Le curseur s'arrête où le plus grand forfait s'arrête : au-delà, il
   faudrait soit inventer un forfait qui n'existe pas, soit afficher un
   message d'excuse, et aucune des deux options ne vend quoi que ce soit. La
   borne se calcule en JS depuis les forfaits réels, jamais écrite en dur ici. */
.calc { max-width: 46rem; margin: 0 auto; }
.calc-slide {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.75rem 1.5rem;
}
.calc-slide-head {
  display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  text-align: center; margin-bottom: 1.25rem;
}
.calc-slide-head > label {
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-muted);
}
/* Chiffres à chasse fixe : sans ça, la valeur sautille latéralement à chaque
   déplacement du curseur, les chiffres n'ayant pas tous la même largeur. */
.calc-value {
  font-variant-numeric: tabular-nums;
  font-family: var(--serif); font-size: 3rem; font-weight: 700; color: var(--accent);
  line-height: 1.1;
}
.calc input[type="range"] { width: 100%; accent-color: var(--accent); }
.calc-scale { display: flex; justify-content: space-between;
  font-size: 0.8rem; color: var(--text-muted); margin-top: 0.35rem; }

.results { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin-top: var(--gap); }
.result { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.1rem; text-align: center; }
.result .k { font-size: 0.8rem; color: var(--text-muted); }
.result .v { font-variant-numeric: tabular-nums;
  font-family: var(--serif); font-size: 1.75rem; font-weight: 600; margin-top: 0.15rem; }

/* Bandeau de conclusion : seul endroit de la page qui reste sombre dans les
   DEUX thèmes, à dessein — comme le bouton Google, c'est une exception
   délibérée et non une couleur tirée au hasard. Le calcul se referme sur une
   réponse, elle mérite de trancher visuellement sur le reste de la section. */
.calc-cta {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap;
  margin-top: var(--gap); padding: 1.25rem 1.5rem; border-radius: var(--radius);
  background: #241A20; color: #FFFFFF;
}
.calc-cta strong { font-family: var(--serif); font-size: 1.15rem; }
.calc-cta span { display: block; font-size: 0.85rem; color: rgba(255, 255, 255, .68); }
/* Ce bandeau est sombre dans les DEUX thèmes : la teinte du bouton y est
   donc écrite en clair, comme le fond du bandeau lui-même, et non tirée de
   `--accent`. Le rose profond du thème clair tomberait à 1,6:1 ici ; c'est la
   nuance haute qui est reprise (6,4:1 au repos comme au survol). */
.calc-cta .btn { flex: none; --btn-teinte: #F2789B; --btn-encre: #241A20; }

/* `p` (règle de base) pose max-width: 68ch sans marge automatique — un bloc
   plafonné mais non centré reste collé au bord GAUCHE de son parent, quel
   que soit le text-align posé dessus (même défaut que .final h2, corrigé le
   31/08 : le premier ne centre que le TEXTE dans le bloc, pas le bloc
   lui-même). .calc fait 46rem de large, largement au-delà de 68ch : l'écart
   se voyait franchement. */
#calc-note {
  margin-top: 1rem; margin-inline: auto;
  font-size: 0.78rem; font-style: italic; text-align: center;
}

/* --- Tarifs ------------------------------------------------------------- */
/* --- Grille tarifaire ----------------------------------------------------
   Transposée du composant 21st « pricing » : carte centrée, nom discret,
   prix en très grand, filet, bouton pleine largeur, argument en tout petit.
   Le forfait mis en avant se détache en hauteur et les deux autres reculent
   d'un cran — sans la rotation 3D de l'original, qui penche le texte et le
   rend plus dur à lire. */

/* Bascule mensuel / annuel. Deux boutons nommés là où l'original pose un
   interrupteur : sur un interrupteur, rien ne dit si la position basse vaut
   mensuel ou annuel. */
.billing { display: flex; align-items: center; justify-content: center;
  gap: 0.75rem; flex-wrap: wrap; margin-bottom: 2.5rem; }
.billing-switch { display: inline-flex; padding: 0.25rem;
  background: var(--accent-soft); border-radius: 999px; }
.billing-opt {
  font: inherit; font-weight: 600; font-size: 0.95rem;
  padding: 0.5rem 1.25rem; border: none; border-radius: 999px;
  background: none; color: var(--text-muted); cursor: pointer;
  transition: background-color .18s, color .18s;
}
.billing-opt[aria-pressed="true"] { background: var(--surface); color: var(--text); }
.billing-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.billing-save { font-size: 0.9rem; font-weight: 600; color: var(--accent); }

/* stretch et non start : margin-top:auto ne pousse le bouton en bas de
   la carte que si celle-ci occupe toute la hauteur de sa rangée. */
#plans { align-items: stretch; }

.plan {
  position: relative;                     /* pour le fanion, posé dans le coin */
  display: flex; flex-direction: column;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 1rem;
  padding: 1.75rem 1.5rem;
}
.plan.featured { border-color: var(--accent); border-width: 2px; }

/* Le fanion mord sur la bordure au lieu de pousser le contenu vers le bas :
   les trois cartes gardent la même ligne de départ. */
.badge {
  position: absolute; top: -1px; right: -1px;
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.78rem; font-weight: 600;
  background: var(--accent); color: var(--accent-text);
  padding: 0.3rem 0.7rem;
  border-radius: 0 0.95rem 0 0.75rem;
}
/* Étoile pleine : à 14 px, un contour se lit comme une tache. */
.badge svg { fill: currentColor; stroke: none; flex: none; }

.plan-name {
  margin: 0; font-size: 0.95rem; font-weight: 600;
  color: var(--text-muted);
}
.price {
  display: flex; align-items: baseline; justify-content: center; gap: 0.4rem;
  margin-top: 1.4rem;
  font-family: var(--serif); font-size: 3rem; font-weight: 600; line-height: 1;
  letter-spacing: -0.02em;
  /* Les chiffres gardent la même largeur d'un tarif à l'autre : sans ça, la
     carte se réajuste à chaque bascule et le regard perd sa ligne. */
  font-variant-numeric: tabular-nums;
}
.price-unit {
  font-family: var(--sans); font-size: 0.9rem; font-weight: 600;
  color: var(--text-muted);
}
.billed {
  margin: 0.5rem 0 0; font-size: 0.78rem; line-height: 1.4;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}

.features { list-style: none; margin: 1.5rem 0 0; padding: 0; }
.features li {
  display: flex; gap: 0.55rem; align-items: flex-start; text-align: left;
  font-size: 0.95rem; margin-bottom: 0.5rem; color: var(--text-muted);
}
.features svg { color: var(--accent); flex: none; margin-top: 0.2rem; }

/* margin-top:auto sur le FILET, pas sur le bouton : sinon le filet reste
   en haut du vide qu'une liste plus courte laisse derrière elle. */
.plan hr { width: 100%; border: none; border-top: 1px solid var(--border);
  margin: 1.25rem 0; margin-top: auto; }
/* Le bouton est poussé en bas : les trois cartes l'alignent quelles que
   soient leurs listes. */
.plan .btn-plan { width: 100%; }
.plan .cta-note { margin: 0.7rem 0 0; font-size: 0.8rem; color: var(--text); text-align: center; }
.plan-desc { margin: 0.5rem 0 0; font-size: 0.78rem; color: var(--text-muted); }

/* Les trois situations écrites par le propriétaire. Le composant d'origine
   n'a pas de place pour elles ; repliées, elles restent disponibles sans
   allonger une carte déjà dense. */
.fits { margin-top: 1rem; text-align: left; }
.fits summary {
  font-size: 0.82rem; font-weight: 600; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--text-muted);
  cursor: pointer; list-style: none;
}
.fits summary::-webkit-details-marker { display: none; }
.fits summary::after { content: " +"; }
.fits[open] summary::after { content: " –"; }
.fits summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fits ul { list-style: none; margin: 0.6rem 0 0; padding: 0; }
.fits li { font-size: 0.9rem; margin-bottom: 0.3rem; color: var(--text); }
.fits li::before { content: "— "; color: var(--text-muted); }

/* Entrée en scène, à la place de framer-motion. La classe n'est posée que si
   l'animation est acceptée : sans elle, les cartes sont visibles d'emblée. */
.a-entrer .plan {
  opacity: 0; transform: translateY(1.5rem);
  transition: opacity .5s ease-out, transform .55s cubic-bezier(.2, .8, .25, 1);
}
.a-entrer.entre .plan { opacity: 1; transform: none; }
.a-entrer.entre .plan:nth-child(2) { transition-delay: .08s; }
.a-entrer.entre .plan:nth-child(3) { transition-delay: .16s; }

/* Le relief du composant d'origine : la carte mise en avant monte, les deux
   autres reculent. Seulement à partir de trois colonnes — empilées, une carte
   décalée ressemble à un défaut d'alignement. */
@media (min-width: 58rem) {
  .plan { transform: scale(.96); transition: transform .3s ease-out; }
  .plan.featured { transform: translateY(-1.25rem) scale(1); }
  .a-entrer .plan { transform: translateY(1.5rem) scale(.96); }
  .a-entrer .plan.featured { transform: translateY(2.75rem) scale(1); }
  .a-entrer.entre .plan { transform: scale(.96); }
  .a-entrer.entre .plan.featured { transform: translateY(-1.25rem) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .plan, .a-entrer .plan { transition: none; }
}

/* --- Questions ------------------------------------------------------------
   Transposé de `faq-tabs` de 21st, SANS ses onglets de catégorie : nos
   questions ne se répartissent pas en familles, les enfermer dans quatre
   onglets aurait caché trois questions sur quatre au premier coup d'œil.
   Gardé du composant : le panneau qui se déplie en douceur et le « + » qui
   pivote en croix. Écarté : le halo dégradé derrière le titre et le sous-titre
   en dégradé de texte, qu'aucune autre section de cette page ne porte — la
   page reste sobre partout ailleurs, une exception ici aurait l'air d'un
   oubli plutôt que d'un choix.

   Ouverture par `grid-template-rows: 0fr → 1fr` : la hauteur réelle du texte
   n'a jamais besoin d'être mesurée en JS, contrairement au composant d'origine
   qui s'appuie sur l'« auto » animable de Framer Motion. Repli naturel si le
   navigateur ne sait pas animer les lignes de grille : le panneau bascule
   sans transition, mais bascule quand même. */
.faq { max-width: 46rem; }
.faq-item {
  border-bottom: 1px solid var(--border);
}
.faq-q {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; padding: 1.1rem 0.25rem; border: none; background: none;
  font-family: var(--serif); font-size: 1.1rem; font-weight: 600;
  color: var(--text); text-align: left; cursor: pointer;
}
.faq-plus {
  flex: none; display: flex;
  color: var(--text-muted);
  transition: transform .3s ease, color .2s ease;
}
.faq-item.open .faq-plus { transform: rotate(45deg); color: var(--accent); }
.faq-panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .3s ease;
}
.faq-item.open .faq-panel { grid-template-rows: 1fr; }
.faq-panel-int { overflow: hidden; }
.faq-panel p { margin: 0 0.25rem 1.1rem; color: var(--text-muted); }
@media (prefers-reduced-motion: reduce) {
  .faq-plus, .faq-panel { transition: none; }
}

/* --- Appel final et pied ------------------------------------------------ */
.final { background: var(--surface); border-block: 1px solid var(--border); }
/* `.center` centre le TEXTE (text-align) et pose `margin-inline: auto` sur le
   conteneur et les `<p>` — jamais sur un h2 à qui personne n'a pensé. Un h2
   plafonné à 20ch sans marge automatique reste collé au bord gauche de son
   parent, pourtant bien centré, lui : sur une page large ça passe presque
   inaperçu, sous ~44rem l'écart devient franc — vu en capture le 31/08. */
.final h2 { max-width: 20ch; margin-inline: auto; }

footer.site { padding-block: clamp(2.5rem, 5vw, 4rem) 2rem; }
.foot-grid { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }
footer.site h2 { font-family: var(--sans); font-size: 0.8rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.75rem; }
footer.site ul { list-style: none; margin: 0; padding: 0; }
footer.site li { margin-bottom: 0.4rem; }
footer.site a { color: var(--text-muted); text-decoration: none; font-size: 0.95rem; }
footer.site a:hover { color: var(--accent); text-decoration: underline; }
.foot-bottom { border-top: 1px solid var(--border); margin-top: 2.5rem; padding-top: 1.5rem;
  display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; justify-content: space-between; }
.foot-bottom p { font-size: 0.85rem; color: var(--text-muted); margin: 0; max-width: 52ch; }

/* Une animation ne doit jamais s'imposer à qui a demandé qu'on la lui
   épargne — y compris le défilement doux. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* --- Plan des étapes ------------------------------------------------------
   Transposé du composant 21st « how-it-works ».

   Sa signature, reprise telle quelle : des cartes légèrement de travers,
   épinglées d'une punaise, disposées en zigzag et reliées par un fil pointillé
   qui défile en boucle. Une grille de quatre cartes alignées disait « quatre
   choses » ; le zigzag dit « d'abord ceci, ensuite cela », ce qui est le sujet
   même de la section.

   Écarté : framer-motion (la vitrine n'a pas de build) — le défilé du fil est
   une animation CSS sur stroke-dashoffset. Et le Comic Sans du numéro, qui
   contredirait la marque : Fraunces, déjà chargée, fait le même office.

   Le zigzag n'existe qu'à partir de trois colonnes de large. En dessous les
   cartes s'empilent droites : de travers et à la file, elles ressembleraient à
   un défaut de mise en page. */
.etapes-plan { display: grid; gap: var(--gap); }

.etape-carte { position: relative; }
.etape-papier {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 1.5rem;
  padding: 0.6rem;
  box-shadow: 0 10px 24px rgba(36, 26, 32, .10);
}
.etape-punaise {
  display: block; margin: 0.35rem auto 0.9rem;
  color: var(--teinte);
}
.etape-fond {
  /* La teinte est dérivée du jeton, jamais saisie en dur : le thème sombre
     suit alors tout seul, et la palette reste celle du logo. */
  background: color-mix(in srgb, var(--teinte) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--teinte) 22%, var(--surface));
  border-radius: 0.9rem;
  padding: 1.1rem 1.15rem 1.25rem;
}
.etape-num {
  display: block;
  font-family: var(--serif); font-weight: 600;
  font-size: 2.4rem; line-height: 1;
  color: var(--teinte);
  margin-bottom: 0.7rem;
}
.etape-carte h3 { margin: 0 0 0.4rem; font-size: 1.25rem; }
.etape-carte p { margin: 0; color: var(--text-muted); font-size: 0.95rem; line-height: 1.5; }

/* Le fil : présent seulement quand le zigzag l'est. */
.etapes-fil { display: none; }

@media (min-width: 62rem) {
  .etapes-plan {
    display: block; position: relative;
    height: var(--hauteur, 820px);
    max-width: 1000px; margin-inline: auto;
  }
  .etapes-fil {
    display: block; position: absolute; inset: 0;
    width: 100%; height: 100%; pointer-events: none; z-index: 0;
    /* La couleur de bordure disparaissait sur le fond de page : le fil ne se
       voyait qu'en connaissant son existence. */
    color: color-mix(in srgb, var(--accent) 40%, var(--border));
  }
  .etapes-fil path {
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-dasharray: 8 6; stroke-linecap: round;
    vector-effect: non-scaling-stroke;
    /* -140 est un multiple de 14 (8 + 6) : la boucle se referme sans saut. */
    animation: etapes-defile 3s linear infinite;
  }
  @keyframes etapes-defile { to { stroke-dashoffset: -140; } }

  .etape-carte {
    position: absolute; width: 280px; z-index: 1;
    transform: rotate(var(--pivot));
    transition: transform .3s ease-out;
  }
  /* Le calage en colonne n'existe QUE dans le zigzag : hors de cette requête
     les cartes sont empilées par la grille et ne doivent rien décaler. */
  .etape-carte.a-gauche { left: 1%; }
  .etape-carte.a-droite { right: 1%; }
  .etape-carte:hover { z-index: 3; transform: rotate(var(--pivot)) scale(1.05); }
}

@media (prefers-reduced-motion: reduce) {
  .etapes-fil path { animation: none; }
  .etape-carte, .etape-carte:hover { transition: none; transform: rotate(var(--pivot)); }
}

/* --- Carrousel des prises de vue ------------------------------------------
   Transposé du composant 21st « coverflow-carousel ».

   Sa mécanique est reprise entière parce qu'elle est bonne : une position
   FRACTIONNAIRE sert de seule source de vérité, chaque carte calcule son
   décalage par le chemin le plus court sur l'anneau — donc la boucle est
   infinie sans cloner un seul nœud — et tout est peint directement dans le DOM
   plutôt que par un état re-rendu soixante fois par seconde.

   Pourquoi un carrousel plutôt que la liste d'avant : la promesse de cette
   section est que les vingt images montrent LE MÊME OBJET. Une liste de vingt
   encadrés le dit ; un carrousel où les prises de vue voisines restent
   visibles de part et d'autre le montre.

   Ajouté au composant : la rotation autonome. Elle s'arrête dès que le
   visiteur touche à quoi que ce soit, et ne repart pas — reprendre la main sur
   quelqu'un qui lit est une gêne, pas une animation. */
.cf {
  --cf-carte: clamp(150px, 21vw, 270px);
  margin-top: 0.5rem;
}

.cf-cadre {
  position: relative;
  overflow: hidden;
  padding-block: 2.5rem;
  perspective: calc(var(--cf-carte) * 3);
  outline: none;
  /* Le glissement horizontal est à nous, le défilement vertical reste à la
     page : sans ça, un doigt qui balaie de biais bloque le scroll. */
  touch-action: pan-y;
}
.cf-cadre:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

.cf-piste {
  position: relative;
  height: var(--cf-carte);
  transform-style: preserve-3d;
  user-select: none;
}

.cf-carte {
  position: absolute; left: 50%; top: 0;
  width: var(--cf-carte); aspect-ratio: 1;
  border-radius: 1rem; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 16px 34px rgba(36, 26, 32, .16);
  will-change: transform;
}
.cf-carte img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Emplacement en attente de photo. Il porte le nom de la prise de vue plutôt
   qu'un rectangle gris : la section reste lisible avant que les images
   existent, et rien n'est inventé pour combler le vide. */
.cf-vide {
  width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.45rem; padding: 1rem; text-align: center;
  /* 5 % de teinte ne se voyait pas : les cartes paraissaient vides plutôt
     qu'en attente, et la section entière semblait cassée. */
  background: color-mix(in srgb, var(--accent) 11%, var(--surface));
  border: 1px dashed color-mix(in srgb, var(--accent) 32%, var(--surface));
  border-radius: inherit;
}
.cf-vide svg { color: var(--accent); opacity: .55; }
.cf-vide .cf-rang {
  font-family: var(--serif); font-size: 1.5rem; font-weight: 600;
  line-height: 1; color: var(--accent); opacity: .75;
}
.cf-vide span.cf-titre {
  font-family: var(--serif); font-size: 0.95rem; font-weight: 600;
  color: var(--text); line-height: 1.25;
}

/* Le nom sur la carte : le visiteur doit pouvoir nommer ce qu'il regarde sans
   descendre à la légende. */
.cf-etiquette {
  position: absolute; inset: auto 0 0 0;
  padding: 1.6rem 0.75rem 0.6rem;
  font-size: 0.8rem; font-weight: 600; text-align: center;
  color: #FFF;
  background: linear-gradient(to top, rgba(0, 0, 0, .72), transparent);
}
.cf-carte.cf-attente .cf-etiquette { display: none; }

.cf-legende {
  width: min(38rem, 100% - 2.5rem); margin: 0.75rem auto 0;
  text-align: center; min-height: 5.5rem;
}
.cf-nom { font-family: var(--serif); font-size: 1.35rem; font-weight: 600; margin: 0 0 0.3rem; }
.cf-texte { color: var(--text-muted); font-size: 0.95rem; margin: 0; }
.cf-legende.change .cf-nom, .cf-legende.change .cf-texte { animation: cf-entre .28s ease-out both; }
@keyframes cf-entre {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.cf-points {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  justify-content: center; margin-top: 1rem;
}
.cf-point {
  width: 0.5rem; height: 0.5rem; padding: 0;
  border: none; border-radius: 50%;
  background: var(--text-muted); opacity: .28;
  cursor: pointer;
  transition: opacity .2s, transform .2s;
}
.cf-point[aria-current="true"] { opacity: 1; background: var(--accent); transform: scale(1.25); }
.cf-point:hover { opacity: .7; }

.cf-note { text-align: center; margin: 1.5rem auto 0; max-width: 40rem; }

@media (prefers-reduced-motion: reduce) {
  .cf-legende.change .cf-nom, .cf-legende.change .cf-texte { animation: none; }
}

/* --- Les deux grilles de cartes ----------------------------------------- */
/* Grille 1 (sous « How it works ») : composant 21st `features-grid`.
   Grille 2 (après le carrousel) : composant 21st `GlowCard`.
   Les deux partagent .carte — même balisage, même mesure ; seule la peau
   change. Transposés dans
   composants-21st/traites/2026-09-04-grilles-de-cartes.txt */

.cartes {
  display: grid; gap: 1rem;
  grid-template-columns: 1fr;
}
@media (min-width: 40rem) { .cartes { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .cartes { grid-template-columns: repeat(4, 1fr); } }

.carte {
  position: relative; top: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.4rem;
  /* Le soulèvement passe par `top` et NON par `transform` (composant 21st
     `feature-card-1`, dont le ressort est repris ici). Une carte transformée
     devient un bloc conteneur : le halo de la grille 2, peint en
     `background-attachment: fixed`, se recalerait alors sur la carte au lieu
     de la fenêtre et s'éteindrait sur celle qu'on pointe. `top` déplace sans
     rien contenir, donc le même geste sert les deux grilles. */
  transition: top .42s cubic-bezier(.34, 1.56, .64, 1),
              box-shadow .42s cubic-bezier(.34, 1.56, .64, 1),
              border-color .28s, background-color .28s;
}
.carte h3 {
  margin: 0.9rem 0 0.35rem;
  font-size: 1.05rem; letter-spacing: -0.01em;
}
.carte p {
  margin: 0; max-width: none;
  color: var(--text-muted); font-size: 0.92rem; line-height: 1.55;
}

/* La tuile de l'icône. Au survol, un disque rose part du centre et la remplit
   — le geste du `flow-button`, pour que la page n'ait qu'un seul vocabulaire
   d'animation. Le disque est un pseudo-élément : rien n'est injecté, et les
   cartes construites par main.js après le chargement en héritent. */
.carte-icone {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem;
  border-radius: 10px;
  background: var(--accent-soft); color: var(--accent);
  transition: color .3s, transform .42s cubic-bezier(.34, 1.56, .64, 1);
}
.carte:hover .carte-icone,
.carte:focus-within .carte-icone { transform: scale(1.16) translateY(-3px); }
.carte-icone::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: 50%; background: var(--accent);
  transform: scale(0);
  transition: transform .42s cubic-bezier(.34, 1.2, .64, 1);
}
/* 1.5 et non 1 : le disque est inscrit dans le carré, il lui faut la moitié de
   la diagonale (√2 ≈ 1.42) pour en couvrir les coins. */
.carte:hover .carte-icone::before,
.carte:focus-within .carte-icone::before { transform: scale(1.5); }
.carte:hover .carte-icone,
.carte:focus-within .carte-icone { color: var(--accent-text); }

.carte:hover, .carte:focus-within {
  top: -10px;
  box-shadow: 0 22px 28px -14px rgb(0 0 0 / .18), 0 8px 12px -8px rgb(0 0 0 / .10);
}
/* La bordure rose reste à la grille 1 : sur la grille 2 c'est le halo qui
   répond au survol, et une bordure pleine passerait par-dessus. */
.cartes:not(.cartes-seules) .carte:hover,
.cartes:not(.cartes-seules) .carte:focus-within { border-color: var(--accent); }

/* --- Grille 2 : le halo qui suit le curseur ------------------------------
   Peint en `background-attachment: fixed`, donc positionné sur la FENÊTRE :
   `clientX/clientY` s'y posent tels quels, sans mesurer une seule carte.
   `--pointeur-xp` (0 → 1) fait glisser la teinte du rose au corail, les deux
   bouts du dégradé du logo — l'original balayait du bleu au violet, deux
   couleurs qui ne sont nulle part dans la marque. */
.cartes-seules .carte {
  --halo-taille: 420px;
  /* 280 -> 375 : violet à gauche, rose au milieu, corail à droite. Le violet
     est une demande du 04/09 ; les deux autres bouts sont ceux du dégradé du
     logo. L'original balayait du bleu au violet. */
  --halo-teinte: calc(280 + var(--pointeur-xp, .5) * 95);
  background-color: var(--surface);
  background-image: radial-gradient(
    var(--halo-taille) var(--halo-taille)
    at var(--pointeur-x, 50vw) var(--pointeur-y, 45vh),
    hsl(var(--halo-teinte) 85% 60% / .22), transparent 100%);
  background-attachment: fixed;
}


/* Le liseré lumineux. Deux masques composés n'en gardent que le pourtour :
   sans eux, le dégradé recouvrirait la carte entière. Là où la composition de
   masques n'existe pas, les cartes gardent leur bordure ordinaire — elles
   perdent le halo, elles ne perdent pas la page. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .cartes-seules .carte { position: relative; }
  .cartes-seules .carte::before {
    content: ''; position: absolute; inset: 0;
    border-radius: inherit; padding: 2px; pointer-events: none;
    background: radial-gradient(
      var(--halo-taille) var(--halo-taille)
      at var(--pointeur-x, 50vw) var(--pointeur-y, 45vh),
      hsl(var(--halo-teinte) 92% 58%), transparent 100%);
    background-attachment: fixed;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
  }
}

/* Le mouvement gêne, pas la couleur : le halo se fige en haut de chaque carte
   et le disque de l'icône arrive sans transition. */
@media (prefers-reduced-motion: reduce) {
  .carte, .carte-icone, .carte-icone::before { transition: none; }
  .carte:hover, .carte:focus-within { top: 0; box-shadow: none; }
  .carte:hover .carte-icone, .carte:focus-within .carte-icone { transform: none; }
  .cartes-seules .carte,
  .cartes-seules .carte::before {
    background-attachment: scroll;
    background-position: 50% -40%;
  }
}

/* --- Vitrine IA : neuf objets, un seul marquee ---------------------------
   Composant 21st `hero-3` (AnimatedMarqueeHero), repris pour SA SEULE bande
   d'images qui défile — le reste du composant (titre en toutes lettres qui
   compose lettre par lettre, bouton d'action) ne sert à rien ici : cette
   section illustre un propos, elle ne vend rien de plus que ce que la page
   vend déjà. framer-motion est remplacé par une seule animation CSS ;
   `[mask-image]` fait le même fondu horizontal que l'original faisait à la
   verticale. */
.vitrine-ia {
  overflow: hidden; margin-top: 2.5rem;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.vitrine-ia-piste {
  display: flex; gap: 1.1rem; width: max-content;
  animation: vitrine-ia-defile 46s linear infinite;
}
.vitrine-ia:hover .vitrine-ia-piste,
.vitrine-ia:focus-within .vitrine-ia-piste { animation-play-state: paused; }
@keyframes vitrine-ia-defile {
  from { transform: translateX(0); }
  /* -50% et non -100% : la piste contient CHAQUE image deux fois, donc sa
     moitié équivaut exactement à un tour complet — la jonction ne se voit
     pas. */
  to { transform: translateX(-50%); }
}
.vitrine-ia-carte {
  flex: none; width: clamp(9rem, 16vw, 13rem); aspect-ratio: 3 / 4;
  border-radius: 16px; overflow: hidden; box-shadow: 0 10px 24px -12px rgb(0 0 0 / .28);
}
/* La légère rotation alternée de l'original : jamais sur la carte pointée
   par la souris, sinon l'image se redresse pile au moment où on la regarde. */
.vitrine-ia-carte:nth-child(odd) { transform: rotate(-2deg); }
.vitrine-ia-carte:nth-child(even) { transform: rotate(2deg); margin-top: 1.4rem; }
.vitrine-ia-carte img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Aucune interaction ne dépend de ce défilement : un mouvement réduit fige la
   piste sans rien retirer, et la barre de défilement natives reste
   disponible pour qui veut regarder les neuf images une par une. */
@media (prefers-reduced-motion: reduce) {
  .vitrine-ia { overflow-x: auto; }
  .vitrine-ia-piste { animation: none; }
}

/* --- Mur d'avis : trois colonnes qui défilent ----------------------------
   Composant 21st « testimonials-columns-1 ». Écarté : motion/react (une
   animation CSS suffit) et le badge « Testimonials » en dur, qui vit
   désormais dans le JSON comme tout le reste du texte. */
.temoin-etiquette {
  display: inline-block; margin: 0 auto 1rem;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 0.3rem 0.9rem; font-size: 0.85rem; color: var(--text-muted);
}
.temoins {
  display: flex; justify-content: center; gap: 1.5rem;
  max-height: 740px; overflow: hidden; margin-top: 2.5rem;
  -webkit-mask-image: linear-gradient(transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(transparent, #000 12%, #000 88%, transparent);
}
/* Deux colonnes de moins sur un écran étroit : trois colonnes de 300 px n'y
   tiennent pas, et les réduire rendrait les citations illisibles. */
.temoins-colonne-2 { display: none; }
.temoins-colonne-3 { display: none; }
@media (min-width: 48rem) { .temoins-colonne-2 { display: block; } }
@media (min-width: 68rem) { .temoins-colonne-3 { display: block; } }

.temoins-piste {
  display: flex; flex-direction: column; gap: 1.5rem;
  animation: temoins-defile 15s linear infinite;
}
/* Sur la COLONNE, pas sur `.temoins` : ce dernier fait toute la largeur de la
   section pour centrer les colonnes, et un survol posé dessus figeait les
   trois pistes même depuis les marges latérales, où il n'y a aucune carte.
   Poser le survol sur chaque colonne laisse ces marges neutres — seule une
   carte réellement pointée arrête sa propre piste. */
.temoins-colonne:hover .temoins-piste,
.temoins-colonne:focus-within .temoins-piste { animation-play-state: paused; }
@keyframes temoins-defile {
  from { transform: translateY(0); }
  /* -50 % : la piste porte chaque avis deux fois, sa moitié vaut donc un tour
     complet et la jonction ne se voit pas. */
  to { transform: translateY(-50%); }
}

.temoin {
  width: min(20rem, 80vw); margin: 0;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 20px; padding: 1.75rem;
  box-shadow: 0 18px 30px -24px color-mix(in srgb, var(--accent) 55%, transparent);
}
.temoin blockquote { margin: 0; }
.temoin blockquote p { margin: 0; font-size: 0.95rem; line-height: 1.6; max-width: none; }
.temoin figcaption { display: flex; align-items: center; gap: 0.7rem; margin-top: 1.15rem; }
.temoin-portrait {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  object-fit: cover; display: grid; place-items: center;
}
.temoin-initiale {
  background: var(--accent-soft); color: var(--accent);
  font-weight: 700; font-size: 1rem;
}
.temoin-qui { display: flex; flex-direction: column; line-height: 1.3; }
.temoin-nom { font-weight: 600; }
.temoin-metier { color: var(--text-muted); font-size: 0.88rem; }

/* Le défilement ne porte aucune information : on le fige, et la colonne
   redevient parcourable à la main. */
@media (prefers-reduced-motion: reduce) {
  .temoins { overflow-y: auto; }
  .temoins-piste { animation: none; }
}

/* --- Bandeau de chiffres (21st « counting-number ») -----------------------
   Pas de titre, pas d'intro : c'est une respiration entre la promesse du hero
   et son mode d'emploi. Il se lit d'un balayage, ou pas du tout.

   Il pince son padding vertical au lieu de prendre --section-y : posé juste
   sous le hero, à pleine hauteur de section il se serait détaché en bloc
   autonome au lieu de le prolonger. */
#stats { padding-block: clamp(2rem, 4.5vw, 3rem); }
.chiffres {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: clamp(1.25rem, 3vw, 2rem) clamp(1rem, 3vw, 2rem);
  border: 1px solid var(--border); border-radius: 20px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
}
.chiffre { text-align: center; }
.chiffre-valeur {
  display: block;
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(1.9rem, 4.2vw, 2.9rem); line-height: 1.1;
  color: var(--accent);
  /* Sans chasse fixe, la ligne tremble à chaque incrément du décompte : les
     chiffres d'Inter n'ont pas tous la même largeur. */
  font-variant-numeric: tabular-nums;
}
.chiffre-libelle {
  margin: 0.5rem 0 0; max-width: none;
  font-size: 0.88rem; line-height: 1.4; color: var(--text-muted);
}
/* Deux par deux plutôt que quatre écrasés : à 390 px, « Photos in, listing
   out » sur une colonne de 80 px tombe sur quatre lignes. */
@media (max-width: 46rem) {
  .chiffres { grid-template-columns: repeat(2, 1fr); gap: 1.5rem 1rem; }
}

/* --- Tableau comparatif ---------------------------------------------------
   Le cadre défile HORIZONTALEMENT plutôt que de laisser six colonnes écraser
   la page : c'est la seule mise en page qui garde les mots lisibles sous
   700 px. La première colonne reste collée à gauche pendant ce défilement —
   sans elle, on lit une ligne de ✓ sans savoir de quoi elle parle. */
.comparatif-cadre {
  overflow-x: auto; overscroll-behavior-x: contain;
  border: 1px solid var(--border); border-radius: 18px;
  background: var(--surface);
}
.comparatif { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.comparatif th, .comparatif td {
  padding: 0.85rem 0.9rem; text-align: center; vertical-align: middle;
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
/* Le pied porte le prix : il lui faut un vrai filet, plus marqué que ceux qui
   séparent deux fonctionnalités. Sans lui, « Start free, no card » et « From »
   se lisaient comme un seul bloc à deux étages. */
.comparatif tfoot th, .comparatif tfoot td {
  border-top: 2px solid color-mix(in srgb, var(--border) 100%, var(--text) 12%);
}
.comparatif thead th { font-size: 0.9rem; font-weight: 600; color: var(--text-muted); }
.comparatif tbody th, .comparatif tfoot th {
  text-align: left; font-weight: 500; color: var(--text);
  white-space: normal; min-width: 15rem;
}
/* Collée à gauche : le fond doit être OPAQUE, sinon les colonnes défilent au
   travers et les deux textes se superposent. */
.comparatif tbody th, .comparatif tfoot th, .comparatif .cmp-vide {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--border);
}
.comparatif .cmp-vide { border-bottom: 1px solid var(--border); }

/* La colonne Kizly, tenue par un liseré plein sur toute sa hauteur. Elle ne
   change PAS de fond par rapport aux autres : un tableau dont notre colonne
   est la seule éclairée se lit comme une publicité, pas comme une
   comparaison — c'est le contenu des cases qui doit convaincre. */
.comparatif .cmp-nous {
  background: color-mix(in srgb, var(--accent-soft) 55%, transparent);
  box-shadow: inset 1px 0 0 color-mix(in srgb, var(--accent) 30%, transparent),
              inset -1px 0 0 color-mix(in srgb, var(--accent) 30%, transparent);
}
.comparatif .cmp-tete-nous { color: var(--accent); font-weight: 700; font-size: 1rem; }
.comparatif thead th:first-of-type { border-top-left-radius: 0; }

.cmp-marque { display: block; font-size: 1.15rem; line-height: 1; }
.cmp-yes .cmp-marque { color: var(--success); }
.cmp-part .cmp-marque { color: var(--ambre); }
.cmp-no .cmp-marque { color: var(--text-muted); opacity: .55; }
/* Le mot double la forme : ni la couleur ni le glyphe seuls ne suffisent —
   ✓ et ✕ se ressemblent de loin, et le vert et le gris ne se distinguent pas
   pour tout le monde. */
.cmp-mot { display: block; margin-top: 0.2rem; font-size: 0.72rem; color: var(--text-muted); }
.cmp-no .cmp-mot { opacity: .7; }

/* La ligne qui porte tout : celle que personne d'autre ne coche. */
/* La ligne que personne d'autre ne coche doit se voir SANS être lue : c'est
   elle qui porte la différence, et un tableau se parcourt en diagonale. Elle
   est donc la seule à changer de graisse ET de fond, sur toute sa largeur. */
.comparatif .cmp-cle th { font-weight: 700; font-size: 1.02rem; }
.comparatif .cmp-cle .cmp-yes .cmp-marque { font-size: 1.5rem; }
.comparatif .cmp-cle td {
  background-color: color-mix(in srgb, var(--accent-soft) 70%, transparent);
}
/* La cellule de gauche est COLLÉE : son fond doit rester opaque, sinon les
   colonnes défilent au travers. Le teint est donc mélangé à --surface et non
   à transparent — même couleur à l'œil, mais rien ne passe derrière. */
.comparatif .cmp-cle th {
  background: color-mix(in srgb, var(--accent-soft) 70%, var(--surface));
}
.comparatif .cmp-cle .cmp-nous {
  background-color: color-mix(in srgb, var(--accent) 16%, transparent);
}

.comparatif tfoot th, .comparatif tfoot td { padding-top: 1rem; padding-bottom: 1rem; }
.cmp-prix { font-weight: 600; }
.comparatif tfoot .cmp-nous { color: var(--accent); }

.comparatif-note {
  margin: 0.9rem 0 0; max-width: none;
  font-size: 0.8rem; font-style: italic; color: var(--text-muted);
}
.comparatif-appel { margin-top: 1.75rem; text-align: center; }
/* L'unité de période est plus discrète que le montant : c'est le chiffre qu'on
   compare d'une colonne à l'autre, pas le mot « mois » répété cinq fois. */
.cmp-periode { font-weight: 400; font-size: 0.82em; color: var(--text-muted); }

/* Sur un téléphone, la colonne des intitulés à 15rem ne laissait que 110 px
   pour le reste : on voyait UNE colonne à la fois, et il fallait dépasser
   Kizly pour atteindre un concurrent. Elle rétrécit, le tableau se resserre,
   et l'écran montre les intitulés, Kizly et le début du suivant — donc de
   quoi comprendre qu'il y a quelque chose à faire défiler. */
@media (max-width: 46rem) {
  .comparatif { font-size: 0.88rem; }
  .comparatif th, .comparatif td { padding: 0.7rem 0.6rem; }
  .comparatif tbody th, .comparatif tfoot th { min-width: 10.5rem; }
  .comparatif-note { font-size: 0.75rem; }
}

/* --- Comparatif : la case porte un MOT (revu le 06/09) --------------------
   Le mot n'est plus une légende discrète sous un signe, c'est l'information
   principale de la case : « Les 13 écrits » face à « Suggère » dit ce que
   deux ✓ identiques cachaient. Le signe passe donc DEVANT, sur la même
   ligne, et le mot prend la taille du texte courant. */
.cmp-etat { line-height: 1.35; }
.cmp-marque { display: inline; margin-right: 0.3rem; font-size: 1em; font-weight: 700; }
.cmp-mot {
  display: inline; margin-top: 0; font-size: 0.88rem; font-weight: 500;
  color: var(--text);
}
.cmp-no .cmp-mot { color: var(--text-muted); opacity: .75; font-weight: 400; }
.cmp-part .cmp-mot { color: var(--text-muted); }
.cmp-yes .cmp-mot { color: var(--text); }
/* Notre colonne : le mot y est le seul à porter la couleur d'action. C'est
   la seule chose qui la distingue typographiquement — pas une taille plus
   grosse, qui déséquilibrerait la ligne. */
.cmp-nous.cmp-yes .cmp-mot { color: var(--accent); font-weight: 600; }
.cmp-part .cmp-marque { font-style: normal; }

/* L'en-tête de notre colonne est PLEIN, comme la capture de référence : sur
   huit lignes, un simple liseré ne suffit plus à dire où regarder. */
.comparatif .cmp-tete-nous {
  background: var(--accent); color: var(--accent-text);
  font-weight: 700; font-size: 1.02rem;
  border-top-left-radius: 10px; border-top-right-radius: 10px;
}

/* --- Forfait Ultra : une carte en pleine largeur SOUS les trois autres ----
   Il ne rentre pas dans la rangée, et c'est voulu : quatre cartes de front
   étouffent les trois premières, qui restent le chemin de la plupart des
   boutiques. Posé dessous et couché, il se lit comme une SUITE de la gamme
   plutôt que comme une quatrième option à comparer d'un coup d'œil.

   `grid-column: 1 / -1` le fait traverser toutes les colonnes, quel qu'en
   soit le nombre — la grille étant en auto-fit, ce nombre change avec la
   largeur de la fenêtre et ne peut pas s'écrire en dur.

   Le partage gauche/droite n'est PAS celui d'une carte debout coupée en deux :
   à gauche l'identité et le prix, à droite tout ce sur quoi on agit — la
   liste, le filet, le bouton. Le premier essai gardait le bouton à gauche
   sous le prix, et la colonne de gauche devenait deux fois plus haute que
   celle de droite : la carte mesurait 570 px pour sept lignes de texte. */
.plan-large {
  grid-column: 1 / -1;
  display: grid; align-content: start;
  grid-template-columns: minmax(11rem, 0.7fr) minmax(0, 1.3fr);
  column-gap: clamp(1.5rem, 4vw, 3.5rem);
  text-align: left;
}
/* Les blancs parasites venaient de la GRILLE PARTAGÉE : `.plan-name` tombait
   dans la même rangée que la liste des sept avantages, donc cette rangée
   prenait la hauteur de la liste et creusait 200 px sous le nom. Chaque
   colonne reçoit maintenant ses propres rangées explicites — le nom, le prix
   et l'argument s'empilent serré à gauche, la liste occupe toute la hauteur à
   droite, et plus rien ne s'aligne sur rien. */
.plan-large .plan-name  { grid-column: 1; grid-row: 1; text-align: left; }
.plan-large .price      { grid-column: 1; grid-row: 2; justify-content: flex-start; text-align: left; }
.plan-large .billed     { grid-column: 1; grid-row: 3; text-align: left; }
.plan-large .plan-desc  { grid-column: 1; grid-row: 4; margin: 0.9rem 0 0; }
.plan-large .fits       { grid-column: 1; grid-row: 5; margin-top: 0.9rem; }
.plan-large .features   { grid-column: 2; grid-row: 1 / span 3; columns: 2; column-gap: 2rem; margin: 0; }
.plan-large hr          { grid-column: 2; grid-row: 4; margin: 1.1rem 0 0; }
.plan-large .btn-plan   { grid-column: 2; grid-row: 5; justify-self: stretch; margin-top: 1.1rem; }
.plan-large .cta-note   { grid-column: 2; grid-row: 6; text-align: center; }
.plan-large .features li { break-inside: avoid; }
/* L'argument porte la promesse de croissance : à cette place il se lit juste
   sous le prix, pas relégué en note de bas de carte comme sur les trois
   cartes debout, où il ferme la colonne. */
.plan-large .plan-desc {
  font-size: 1.02rem; font-weight: 600; color: var(--accent); max-width: 22ch;
}

/* La ligne du pay-as-you-go, sous la grille : c'est une CONDITION de prix,
   elle n'a pas sa place dans une liste de ce qui est inclus. */
.payg {
  margin-top: 1.5rem; padding: 1rem 1.15rem;
  border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius: 14px;
  background: color-mix(in srgb, var(--accent-soft) 40%, transparent);
}
.payg strong { color: var(--accent); }
.payg p { margin: 0; font-size: 0.9rem; color: var(--text-muted); max-width: none; }

/* Sous 52rem la carte couchée se redresse : deux colonnes de 14rem ne
   tiennent pas, et la liste en deux colonnes coupe les lignes en deux. */
/* Sous 52rem la carte couchée se redresse : deux colonnes de 12rem ne
   tiennent pas, et la liste en deux colonnes coupe les lignes en deux. */
/* Sous 52rem la carte couchée se redresse : deux colonnes de 11rem ne
   tiennent pas, et la liste en deux colonnes coupe les lignes en deux.
   `grid-row: auto` sur TOUS les enfants annule les rangées explicites
   ci-dessus — sans ça, la pile garde les trous qu'elles créaient. */
@media (max-width: 52rem) {
  .plan-large { grid-template-columns: 1fr; }
  .plan-large > * { grid-column: 1; grid-row: auto; }
  .plan-large .features { columns: 1; margin-top: 1rem; }
  .plan-large .plan-desc { max-width: none; }
}
/* SIX chiffres depuis le 06/09. `auto-fit` a été essayé d'abord, et écarté :
   à 1120 px il en plaçait cinq sur la première rangée et laissait le sixième
   seul sur la seconde, décentré. Un nombre de colonnes qui DIVISE six est la
   seule façon d'avoir des rangées pleines — 6, puis 3, puis 2. */
.chiffres { grid-template-columns: repeat(6, 1fr); gap: clamp(0.75rem, 2vw, 1.75rem); }
.chiffre-valeur { font-size: clamp(1.6rem, 3.2vw, 2.4rem); }
.chiffre-libelle { font-size: 0.82rem; }
@media (max-width: 70rem) { .chiffres { grid-template-columns: repeat(3, 1fr); row-gap: 1.75rem; } }

/* La navigation au CENTRE de la barre (06/09) ----------------------------
   `.logo` portait `margin-right: auto` : il poussait les ancres, la langue et
   le bouton contre le bord droit, et laissait un vide de plusieurs centaines
   de pixels après la marque. Invisible tant que la barre tenait dans 1120 px,
   criant depuis qu'elle prend toute la largeur.

   Deux marges automatiques AUTOUR de la nav au lieu d'une seule après le
   logo : l'espace restant se partage alors également des deux côtés, donc les
   ancres tombent au centre de la barre quelle que soit la largeur du groupe
   de droite. */
nav.site { margin-inline: auto; }
