/* ═══════════════════════════════════════════════════════════════════════
   simsms — feuille unique.

   Structure : un RAIL D'ACHAT a gauche (service -> pays -> reseau, trois
   etapes numerotees) et le contenu a droite. C'est le modele du marche, et il
   tient debout pour une raison : le visiteur arrive en sachant ce qu'il veut
   valider, pas d'ou il veut son numero. Le rail lui pose les questions dans
   l'ordre ou il peut y repondre, et ne le fait jamais changer de colonne.

   THEME CLAIR PAR DEFAUT, en toutes circonstances. Le sombre n'existe que si
   le visiteur le demande (data-theme="dark" pose par le bouton). Il n'y a
   volontairement AUCUN bloc prefers-color-scheme : un poste regle en sombre
   doit quand meme voir le site en clair.

   ⚠️ TROIS PIEGES DEJA PAYES, TOUS MUETS :
   1. La CSP interdit les styles EN LIGNE. Un style="..." est ignore sans
      erreur : toute valeur dynamique passe par une CLASSE enumeree.
   2. Dans une fonction CSS, les operateurs veulent des ESPACES :
      clamp(1rem,2vw,2rem) est invalide et meurt en silence.
   3. `1fr` vaut `minmax(auto, 1fr)` : un enfant large fait deborder sa case.
      On ecrit minmax(0, 1fr), et min-width: 0 sur ce qui defile.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1. jetons ────────────────────────────────────────────────────────────

   L'IDENTITE, et le raisonnement derriere.

   QUATRE roles, quatre couleurs, et jamais l'inverse :

     --ink       le bandeau      gris-encre tres sombre
     --rail-ink  LA LATERALE     bleu roi — sa propre teinte, pas celle du
                                 bandeau : c'est ce qui la donne a lire comme
                                 « la colonne ou l'on achete »
     --brand     l'identite      bleu : le focus, ce qui est choisi
     --cta       L'ACTION        orange, la SEULE couleur chaude du site

   C'est ce dernier point qui fait converger : dans un champ entierement
   froid, un unique element chaud ne se cherche pas, il se voit. Le vermillon
   n'apparait donc QUE sur « Buy » et « Get started ». Le jour ou on le pose
   ailleurs — un badge, un lien, un titre — le bouton d'achat cesse d'etre le
   point le plus saillant de la page, et c'est tout l'interet qui tombe.

   Le vert et l'ambre ne sont pas des couleurs d'interface ici : ce sont des
   couleurs de DONNEE, reservees a l'echelle des taux de reussite.
-------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  /* ⚠️ Assez CLAIR pour se lire comme une couleur. Premier essai a #171334 :
     un indigo parfaitement applique (verifie au navigateur, rgb(23,19,52)),
     et que tout le monde lisait « noir » — a cette profondeur, 33 points
     d'ecart entre le rouge et le bleu ne se voient plus. Une identite qu'il
     faut expliquer n'en est pas une. */
  --ink:         #111827;
  --bg:          #f1f5f9;
  --bg-2:        #ffffff;
  --surface:     #ffffff;
  --surface-2:   #f8fafc;
  --surface-3:   #edf1f7;
  --border:      #e2e8f0;
  --border-2:    #c3cddb;
  --fg:          #111827;
  --fg-2:        #3e4a5e;
  --fg-3:        #586576;

  /* identite */
  --brand:       #2563eb;
  --brand-hi:    #1d4ed8;
  --brand-soft:  #e7eefd;
  --brand-line:  #b9d0fa;

  /* action — et rien d'autre */
  --cta:         #c2410c;
  --cta-hi:      #9a3412;
  --cta-fg:      #ffffff;

  /* donnee : l'echelle des taux */
  --ok:          #0a7d55;
  --warn:        #94400a;
  --warn-soft:   #fdf1e5;
  --danger:      #c0392f;

  /* alias historiques : tout ce qui n'est pas une action porte l'identite */
  --accent:      var(--brand);
  --accent-hi:   var(--brand-hi);
  --accent-fg:   #ffffff;
  --accent-soft: var(--brand-soft);
  /* ⚠️ L'encre a poser SUR --accent-soft. `--accent` lui-meme y tombe a
     4,44:1 — sous AA d'un cheveu, et parfaitement invisible a l'oeil. Toute
     pastille douce qui porte du TEXTE (et non une icone, qui n'exige que 3:1)
     doit utiliser celle-ci. */
  --accent-ink:  color-mix(in oklab, var(--accent) 82%, #000);
  --accent-line: var(--brand-line);
  --ring:        var(--brand);

  /* Une VRAIE echelle d'elevation, a trois marches nettes. L'ancienne etait
     si discrete que tout flottait au meme plan : bordures partout, aucune
     hierarchie. Chaque marche combine une ombre de contact (courte, nette) et
     une ombre d'ambiance (longue, diffuse) — c'est ce couple qui fait lire une
     surface comme posee au-dessus d'une autre plutot que dessinee dessus. */
  --shadow-1: 0 1px 2px rgba(15, 23, 42, .06);
  --shadow-2: 0 4px 6px -1px rgba(15, 23, 42, .07), 0 2px 4px -2px rgba(15, 23, 42, .05);
  --shadow-3: 0 20px 25px -5px rgba(15, 23, 42, .10), 0 8px 10px -6px rgba(15, 23, 42, .07);
  --shadow-4: 0 25px 50px -12px rgba(15, 23, 42, .20);
  /* Lueur teintee : sert au survol de l'action et aux surfaces flottantes. */
  --glow-brand: 0 4px 30px rgba(37, 99, 235, .16);
  --glow-cta:   0 8px 20px -6px rgba(194, 65, 12, .45);

  /* Rayons relevés sur le modèle du marché : 32 sur la grande carte, 24 sur
     les cartes de contenu, 16 sur les surfaces flottantes, 12 sur les
     boutons. Des rayons trop petits font « brouillon d'admin » ; c'est le
     premier signal de finition qu'on lit sans savoir qu'on le lit. */
  --r-sm: 8px;
  --r:    12px;
  --r-md: 16px;
  /* ⚠️ LE MOTIF DE LA MAISON. La meme silhouette que brand_mark(), en masque :
     elle prend la couleur de l'element, donc un seul jeton sert au bleu des
     surtitres comme au filigrane blanc des sections sombres. `data:` est
     autorise par la CSP (`img-src 'self' data:`) — une URL externe, non. */
  /* ⚠️ La puce est EVIDEE (`fill-rule='evenodd'`), pas juste la silhouette.
     Pleine, la carte se lit comme un rectangle arrondi des qu'elle passe
     sous ~20 px — le coin coupe seul ne suffit pas a la faire reconnaitre.
     Les quatre plots evides la rendent identifiable a 14 px. */
  --sim-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill-rule='evenodd' d='M7.7 1h14.05L28.5 6.7v20.1a4.2 4.2 0 0 1-4.2 4.2H7.7a4.2 4.2 0 0 1-4.2-4.2V5.2A4.2 4.2 0 0 1 7.7 1ZM8 10.3h7.15v7.1H8zM16.85 10.3H24v7.1h-7.15zM8 19.1h7.15v7.1H8zM16.85 19.1H24v7.1h-7.15z'/%3E%3C/svg%3E");
  --r-lg: 24px;
  --r-xl: 32px;

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --mono-font: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  --wrap: 1536px;
  --top-h: 3.5rem;      /* bande sombre  : compte, theme, action */
  --sub-h: 3.5rem;      /* bande claire  : navigation */
  --rail-w: 26.25rem;   /* 420 px, comme le modele */
  --opt-h: 3rem;        /* hauteur d'une ligne de liste : 48 px */
  --top-bg: var(--ink);
  /* ⚠️ La barre laterale a SA teinte, franchement distincte de l'en-tete :
     c'est ce qui la donne a lire comme « la colonne ou l'on achete » plutot
     que comme un prolongement du bandeau. */
  --rail-ink: #1e3a8a;
  --top-fg: #aebbd4;
  --sub-bg: #e8edf7;

  /* Pourquoi la pile SYSTEME et pas une police telechargee : sur ce parc, une
     variable font a fait tomber Lighthouse de 99 a 76-82 — le telechargement
     est facture au premier affichage, meme avec un blocage a 0 ms. */
}

/* Le sombre est OPT-IN. Aucune requete de media ne l'active. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --ink:         #070b14;   /* meme raison qu'en clair : de l'indigo, pas du noir */
  --bg:          #0a0f18;
  --bg-2:        #0e141f;
  --surface:     #121a26;
  --surface-2:   #182231;
  --surface-3:   #20293a;
  --border:      #232e3f;
  --border-2:    #37455c;
  --fg:          #e8eef7;
  --fg-2:        #a3b0c2;
  --fg-3:        #8b98ab;

  --brand:       #6ba3ff;
  --brand-hi:    #8bb8ff;
  --brand-soft:  #12203a;
  --brand-line:  #26406b;

  --cta:         #f2701c;
  --cta-hi:      #ff8a3d;
  --cta-fg:      #1a0800;

  --ok:          #3cdda8;
  --warn:        #f5a524;
  --warn-soft:   #2a2113;
  --danger:      #ff6b5e;

  --accent-fg:   #0d0820;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-2: 0 2px 8px rgba(0, 0, 0, .5), 0 16px 40px rgba(0, 0, 0, .35);
  --shadow-3: 0 10px 30px rgba(0, 0, 0, .55), 0 30px 70px rgba(0, 0, 0, .45);
  --top-bg: var(--ink);
  --rail-ink: var(--ink);
  --top-fg: #94a3bb;
  --sub-bg: #0c121c;
}

/* ── 2. remise a zero ─────────────────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 1rem;
}

body {
  margin: 0;
  /* Un aplat de gris sur 4 000 px de haut se lit « page non finie ». Un halo
     tres doux sous l'en-tete suffit a donner de la profondeur sans que
     personne ne remarque qu'il est la. */
  background:
    radial-gradient(80rem 32rem at 50% -8rem,
                    color-mix(in oklab, var(--brand) 7%, transparent), transparent 70%),
    var(--bg);
  background-attachment: fixed;
  color: var(--fg);
  font-family: var(--font);
  font-size: 0.9375rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* Le corps ne defile JAMAIS lateralement : un bloc trop large defile dans
     SON conteneur, pas dans la page. */
  overflow-x: clip;
  /* Une lueur de marque derriere le haut de page : le fond cesse d'etre un
     aplat gris, sans qu'aucun bloc n'ait a porter la couleur. */
  background-image:
    radial-gradient(70rem 34rem at 62% -6rem,
      color-mix(in oklab, var(--brand) 9%, transparent) 0%,
      transparent 70%);
  background-repeat: no-repeat;
}

h1, h2, h3, h4 { margin: 0; font-weight: 650; line-height: 1.2; letter-spacing: -0.02em; }
p { margin: 0; }
ul, ol, dl, dd { margin: 0; padding: 0; }
li { list-style: none; }
img, svg, video { max-width: 100%; height: auto; display: block; }

/* Visible pour un lecteur d'écran, invisible à l'œil.
   ⚠️ Pas `display: none` ni `visibility: hidden` : les deux le retirent de
      l'arbre d'accessibilité aussi, donc le texte ne serait lu par personne.
      Un rectangle de 1 px, découpé et sorti du flux, reste annoncé. */
.u-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

:where(a, button, summary, input, [tabindex]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

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

.skip {
  position: absolute; inset-inline-start: 1rem; top: -4rem; z-index: 100;
  padding: .7rem 1rem; border-radius: var(--r);
  background: var(--accent); color: var(--accent-fg); font-weight: 600;
  transition: top .18s ease;
}
.skip:focus { top: 1rem; }
main:focus { outline: none; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  /* ⚠️ Le retrait lateral vit ICI et nulle part ailleurs. Un `padding:`
     raccourci pose sur un conteneur interieur l'ecrase et colle le contenu
     aux bords sous 1 200 px — invisible en bureau, flagrant sur telephone. */
  padding-inline: clamp(0.875rem, 0.5rem + 1.6vw, 1.75rem);
}
.wrap--narrow { max-width: 850px; }

.i { width: 1.25em; height: 1.25em; flex: none; }

/* ── 3. typographie utilitaire ────────────────────────────────────────── */

.eyebrow {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .28rem .68rem .28rem .5rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg-2);
  font-size: .78125rem; font-weight: 500;
}
.eyebrow__dot {
  width: .45rem; height: .45rem; border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ok) 16%, transparent);
  animation: pulse 2.6s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

.kicker {
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: .75rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent);
  margin-block-end: .55rem;
}
/* Chaque section s'ouvre par la meme marque. C'est ce qui fait qu'une suite de
   blocs se lit comme UN site et non comme sept boites empilees. */
.kicker::before {
  content: ""; flex: none;
  width: .875rem; height: 1rem;
  background: currentColor;
  -webkit-mask: var(--sim-mask) center / contain no-repeat;
          mask: var(--sim-mask) center / contain no-repeat;
}

.chip {
  display: inline-block; margin-inline-start: .35rem;
  padding: .06rem .35rem; border-radius: 999px;
  font-size: .5625rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  background: var(--surface-3); color: var(--fg-3);
}

code.inline {
  font-family: var(--mono-font); font-size: .9em;
  background: var(--surface-3); border: 1px solid var(--border);
  padding: .1em .4em; border-radius: var(--r-sm);
}

/* ── 4. boutons ───────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .55rem 1rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  font-size: .875rem; font-weight: 620;
  cursor: pointer; white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease,
              color .15s ease, transform .12s ease;
}
.btn .i { width: 1.05em; height: 1.05em; }
/* ⚠️ LE SEUL ELEMENT CHAUD DU SITE. Ne pas reutiliser --cta ailleurs : dans un
   champ entierement froid, un unique element chaud se voit sans etre cherche,
   et c'est exactement ce qu'on veut du bouton d'achat. */
.btn--primary {
  background: var(--cta); color: var(--cta-fg);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), var(--shadow-1);
}
.btn--primary:hover {
  background: var(--cta-hi);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), var(--glow-cta);
  transform: translateY(-1px);
}
.btn--primary:active { transform: translateY(0); box-shadow: var(--shadow-1); }
.btn--ghost {
  background: var(--surface); color: var(--fg); border-color: var(--border);
  box-shadow: var(--shadow-1);
}
.btn--ghost:hover {
  border-color: var(--brand-line); background: var(--surface);
  color: var(--brand); box-shadow: var(--shadow-2);
}
/* 🔴 L'action d'un formulaire de compte est BLEUE, pas orange. L'orange
   n'apparait que sur « Buy » et « Get started » : c'est ce qui fait qu'un
   unique element chaud, dans un champ entierement froid, ne se cherche pas.
   Sur /signin la bande du haut porte deja « Get started » en orange — un second
   bouton chaud sur le meme ecran, et il n'y en a plus aucun qui saute aux yeux. */
.btn--accent {
  background: var(--accent); color: var(--accent-fg);
  box-shadow: var(--shadow-2);
}
.btn--accent:hover { background: var(--accent-hi); }

.btn--sm { padding: .32rem .65rem; font-size: .78125rem; border-radius: var(--r-sm); }
.btn--lg { padding: .78rem 1.3rem; font-size: .9688rem; border-radius: var(--r-md); }
.btn--block { width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }

.iconbtn {
  display: inline-grid; place-items: center;
  width: 2.25rem; height: 2.25rem;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); color: var(--fg-2);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.iconbtn:hover { color: var(--fg); border-color: var(--border-2); }

/* Le site est CLAIR par defaut : le bouton propose donc la lune. Il ne montre
   le soleil que lorsque le sombre a ete demande. */
.iconbtn__sun  { display: none; }
.iconbtn__moon { display: block; }
:root[data-theme="dark"] .iconbtn__sun  { display: block; }
:root[data-theme="dark"] .iconbtn__moon { display: none; }

/* ── 5. pastilles ─────────────────────────────────────────────────────── */

.g-messaging { --m: #2f7fe8; }
.g-social    { --m: #db2777; }
.g-tech      { --m: #0d9488; }
.g-transport { --m: #d97706; }
.g-shopping  { --m: #9333ea; }
.g-finance   { --m: #059669; }
.g-media     { --m: #dc2626; }
.g-dating    { --m: #e11d48; }
.g-travel    { --m: #0891b2; }
.g-work      { --m: #4f46e5; }
.g-other     { --m: #64748b; }

.mono {
  display: grid; place-items: center;
  width: 2rem; height: 2rem; flex: none;
  border-radius: 9px;
  font-size: .7188rem; font-weight: 700;
  background: var(--surface-3); color: var(--fg-2);
  background: color-mix(in oklab, var(--m, #64748b) 13%, transparent);
  color: color-mix(in oklab, var(--m, #64748b) 82%, var(--fg));
}

.iso {
  display: grid; place-items: center;
  min-width: 1.875rem; height: 1.5rem; padding-inline: .3rem; flex: none;
  border-radius: 5px;
  background: var(--surface-3); color: var(--fg-2);
  font-size: .6563rem; font-weight: 750; letter-spacing: .04em;
}

/* ── 6. taux de reussite ──────────────────────────────────────────────── */

.rate {
  display: inline-flex; align-items: center; gap: .28rem;
  font-size: inherit; font-weight: 620; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rate__dot { width: .375rem; height: .375rem; border-radius: 50%; background: currentColor; flex: none; }
.rate.is-high { color: var(--ok); }
.rate.is-mid  { color: var(--warn); }
.rate.is-low  { color: var(--fg-3); }

/* ── 7. recherche ─────────────────────────────────────────────────────── */

.srch { position: relative; }
.srch__icon {
  position: absolute; inset-inline-start: .7rem; top: 50%; translate: 0 -50%;
  width: 1.05rem; height: 1.05rem; color: var(--fg-3); pointer-events: none;
}
.srch__in {
  width: 100%;
  padding: .58rem .7rem .58rem 2.3rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: .875rem;
}
.srch__in::placeholder { color: var(--fg-3); }
.srch__in:focus {
  outline: none;
  border-color: var(--brand);
  background: var(--surface);
  /* Un anneau plutot qu'un liseré : le champ actif se voit du coin de l'oeil. */
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand) 16%, transparent);
}
.srch__in::-webkit-search-cancel-button { cursor: pointer; }

/* ── 7bis. la marque ──────────────────────────────────────────────────────
   Regles de base, communes a ses trois emplacements : la bande sombre
   (telephone), le bloc du rail (bureau) et le pied de page. Sans la taille
   posee ici, le SVG s'effondre a zero et le logo DISPARAIT — sans erreur,
   sans trou visible, juste un mot sans son signe. */

.brand { display: inline-flex; align-items: center; gap: .5rem; flex: none; }
.brand__mark { display: block; width: 1.75rem; height: 1.75rem; flex: none; }
.brand__mark svg { width: 100%; height: 100%; display: block; }
/* Le corps de la carte, et les plots. Les vides entre les plots laissent voir
   le corps : une seule couleur a redefinir par contexte.
   ⚠️ `#fff` en dur pour les plots, pas `var(--surface)` : sur la carte bleue,
   des plots qui virent au sombre avec le theme ne se lisent plus comme une
   puce, ils se lisent comme un defaut d'affichage. */
.brand__bg    { fill: var(--brand); }
.brand__card  { fill: #fff; }
.brand__word  { font-size: 1.0313rem; font-weight: 700; letter-spacing: -.032em; color: var(--fg-2); }
.brand__word-b { color: var(--fg); }

/* ══ 8. LA COQUE : deux bandes, et un rail qui les coiffe ════════════════
   Grille de 2 colonnes sur 3 rangees :

       rangee 1 (--top-h)  [ RAIL ]  bande sombre  — compte, theme, action
       rangee 2 (--sub-h)  [ RAIL ]  bande claire  — navigation
       rangee 3 (le reste) [ RAIL ]  contenu

   Le rail occupe la colonne 1 sur TOUTE la hauteur : il demarre a y=0 et passe
   PAR-DESSUS les deux bandes au lieu de commencer en dessous. C'est ce
   chevauchement qui donne au bloc de marque son ancrage en haut a gauche.

   ⚠️ Les bandes sont peintes par `.bands`, un calque en position absolue pose
      DERRIERE la grille. Il faut qu'elles filent d'un bord a l'autre de la
      FENETRE : peintes sur la grille elle-meme, la couleur s'arreterait net a
      1 360 px sur un grand ecran, avec deux marges blanches de part et d'autre.
-------------------------------------------------------------------------- */

.bands {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: var(--top-h);
  background: var(--top-bg);
  z-index: 0;
  pointer-events: none;
}

.shell {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(0.875rem, 0.5rem + 1.6vw, 1.75rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 68rem) {
  .bands {
    height: calc(var(--top-h) + var(--sub-h));
    background: linear-gradient(
      var(--top-bg) 0 var(--top-h),
      var(--sub-bg) var(--top-h) 100%);
  }
  .shell {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    grid-template-rows: var(--top-h) var(--sub-h) auto;
    column-gap: 1.5rem;
  }
  .rail { grid-column: 1; grid-row: 1 / -1; }
  .top  { grid-column: 2; grid-row: 1; }
  .sub  { grid-column: 2; grid-row: 2; }
  .main { grid-column: 2; grid-row: 3; }

  /* Sans rail (404, pages simples) : une seule colonne, tout aligne a gauche. */
  .shell--norail { grid-template-columns: minmax(0, 1fr); }
  .shell--norail .top,
  .shell--norail .sub,
  .shell--norail .main { grid-column: 1; }
}

/* ── 9. la bande sombre ───────────────────────────────────────────────── */

.top {
  display: flex;
  align-items: center;
  gap: .75rem;
  min-height: var(--top-h);
  /* ⚠️ La barre porte AUSSI son fond, alors que `.bands` le peint deja
     derriere elle. Ce n'est pas une redondance : sans lui, le texte clair
     flotte sur un fond transparent dont l'ancetre opaque est le corps de page
     — donc CLAIR. Le contraste calcule tombait a 1,6:1 la ou l'oeil en voyait
     8. Un jour ou `.bands` serait rogne ou masque, c'est ce qu'on lirait
     vraiment. `.bands` ne sert qu'a prolonger la couleur jusqu'aux bords de
     la fenetre. */
  background: var(--top-bg);
  color: var(--top-fg);
  min-width: 0;
}
/* Le bandeau projette : sans cela il est peint sur la page, pas pose dessus. */
.bands { box-shadow: 0 1px 3px rgba(15, 23, 42, .10); }
@media (min-width: 68rem) { .top { padding-inline-start: .25rem; } }

.top__brand { display: inline-flex; align-items: center; gap: .5rem; flex: none; }
.top__brand .brand__word   { color: rgba(255, 255, 255, .62); font-size: 1.0313rem; font-weight: 700; letter-spacing: -.032em; }
.top__brand .brand__word-b { color: #fff; }
.top__brand .brand__bg     { fill: #fff; }
.top__brand .brand__card   { fill: var(--top-bg); }

.top__act { display: flex; align-items: center; gap: .4rem; margin-inline-start: auto; }
.top__signin {
  display: none;
  padding: .4rem .65rem; border-radius: var(--r-sm);
  font-size: .875rem; font-weight: 560; color: var(--top-fg);
  transition: color .15s ease, background-color .15s ease;
  /* ⚠️ « Sign out » est un BOUTON (la deconnexion est un POST), pas un lien :
     sans ces trois lignes il heritait du bouton par defaut du navigateur — un
     rectangle gris clair sur la bande sombre, a cote d un « Sign in » qui,
     lui, est un lien nu. Deux etats du meme emplacement ne doivent pas avoir
     deux apparences. */
  background: none; border: 0; cursor: pointer; font-family: inherit;
}
.top__signin:hover { color: #fff; background: rgba(255, 255, 255, .08); }
.top__start { display: none; }

/* Un bouton d'icone POSE SUR LE SOMBRE : les jetons de surface claire y
   deviendraient un carre blanc. */
.iconbtn--onDark {
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .14);
  color: var(--top-fg);
}
.iconbtn--onDark:hover { color: #fff; border-color: rgba(255, 255, 255, .3); }

.top__burger-close { display: none; }
.top__burger[aria-expanded="true"] .top__burger-open  { display: none; }
.top__burger[aria-expanded="true"] .top__burger-close { display: block; }

@media (min-width: 68rem) {
  .top__brand  { display: none; }   /* la marque vit dans le rail */
  .top__signin { display: inline-flex; }
  .top__start  { display: inline-flex; }
  .top__burger { display: none; }
}

/* ── 10. la bande claire ──────────────────────────────────────────────── */

.sub {
  display: none;
  align-items: center;
  gap: .15rem;
  min-height: var(--sub-h);
  background: var(--sub-bg);      /* meme raison que .top */
  min-width: 0;
}
@media (min-width: 68rem) { .sub { display: flex; padding-inline-start: .25rem; } }

.sub__a {
  padding: .45rem .75rem; border-radius: var(--r-sm);
  font-size: 1rem; font-weight: 560; color: var(--fg-2);
  transition: color .15s ease, background-color .15s ease;
}
.sub__a:hover { color: var(--fg); background: rgba(15, 23, 42, .06); }
/* Un filet entre les entrees. ⚠️ Il est dessine par le CSS et porte
   `aria-hidden` de fait — un « · » ecrit dans le contenu se ferait lire
   « point » entre chaque lien par un lecteur d'ecran.
   ⚠️ `:not(:first-child)` plutot que `+` : les liens sont generes en boucle et
   le premier ne doit pas porter de separateur a sa gauche. */
.sub__a { position: relative; }
.sub__a:not(:first-child)::before {
  content: ""; position: absolute;
  inset-inline-start: -.15rem; top: 50%; translate: 0 -50%;
  width: 1px; height: 1.1rem;
  background: var(--border-2);
}
.sub__spacer { flex: 1 1 auto; }
.sub__note {
  display: inline-flex; align-items: center; gap: .4rem; flex: none;
  font-size: .875rem; font-weight: 550; color: var(--fg-3);
}
.sub__lock { width: 1rem; height: 1rem; }

/* ── 11. menu mobile ──────────────────────────────────────────────────── */

.mnav {
  position: fixed; inset: var(--top-h) 0 0 0; z-index: 45;
  background: var(--bg-2); overflow-y: auto;
}
/* ⚠️ CE GARDE N'EST PAS DECORATIF. Une classe qui pose display:grid bat la
   regle [hidden]{display:none} de la feuille du navigateur : sans lui, le
   panneau reste ouvert en permanence, sans qu'aucune erreur ne se leve. */
.mnav[hidden] { display: none !important; }
.mnav__in { padding: 1rem clamp(0.875rem, 0.5rem + 1.6vw, 1.75rem) 2rem; display: grid; gap: .2rem; }
.mnav__a {
  display: flex; align-items: center; justify-content: space-between;
  padding: .9rem .2rem;
  border-block-end: 1px solid var(--border);
  font-size: 1rem; font-weight: 550;
}
.mnav__arrow { color: var(--fg-3); width: 1.05em; height: 1.05em; }
.mnav__foot { display: grid; gap: .55rem; margin-block-start: 1.25rem; }
body.is-locked { overflow: hidden; }

/* ── 12. le rail ──────────────────────────────────────────────────────── */

.rail { min-width: 0; }

/* Le bloc de marque, qui coiffe les deux bandes. Masque sur telephone : la
   marque y vit dans la barre du haut, l'afficher aux deux endroits la
   doublerait. */
.rail__brand { display: none; }
@media (min-width: 68rem) {
  .rail__card { border-radius: var(--r-xl) var(--r-xl) var(--r-md) var(--r-md); }

  /* ⚠️ Le rail ne monte PAS jusqu'en haut, et il ne descend pas non plus au
     ras du contenu. Il occupe toujours les trois rangees de la grille — c'est
     ce retrait TRANSPARENT qui fait demarrer son contenu plus bas, pas un
     changement de placement.

     Le bloc de marque part du MILIEU de la bande sombre et s'arrete au bas de
     la bande claire : il mord sur l'en-tete juste assez pour s'y ancrer, sans
     l'ecraser. Les deux valeurs sont derivees des hauteurs de bande, donc
     l'alignement tient si l'une d'elles change. */
  .rail { padding-block-start: calc(var(--top-h) / 2); }

  .rail__brand {
    display: flex; align-items: center; gap: .875rem;
    height: calc(var(--top-h) / 2 + var(--sub-h));
    padding-inline: 1.25rem;
    /* Un degre de degrade et un filet de lumiere sur l'arete haute : c'est
       peu, et c'est la difference entre une surface peinte et une surface
       eclairee.
       ⚠️ La COULEUR est posee separement de l'IMAGE. Avec le raccourci
       `background:` + degrade, `backgroundColor` reste transparent : le texte
       blanc se retrouve alors calcule sur le premier ancetre opaque — la carte
       BLANCHE — soit 1,0:1. Et le jour ou le degrade ne peint pas, c'est
       exactement ce qu'on lirait. */
    background-color: var(--rail-ink);
    background-image:
      linear-gradient(158deg,
        color-mix(in oklab, var(--rail-ink) 82%, #ffffff),
        var(--rail-ink) 62%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14);
  }
}
/* Le bloc de marque fait 84 px de haut et n'en utilisait que 43 : la marque
   pouvait grandir sans toucher a la hauteur du bloc, fixee par le dessin de
   l'en-tete (moitie de la bande sombre + bande claire). */
.brand__mark--lg { width: 2.75rem; height: 2.75rem; }
.rail__brand .brand__bg   { fill: #fff; }
.rail__brand .brand__card { fill: var(--rail-ink); }
.rail__brandtx { min-width: 0; }
.rail__brandname {
  display: block; font-size: 1.3125rem; font-weight: 730; letter-spacing: -.032em;
  color: rgba(255, 255, 255, .62); line-height: 1.22;
}
.rail__brandname .brand__word-b { color: #fff; }
.rail__brandsub {
  /* .5 tombait pile sur 4,4999:1 — sous le seuil AA d'un cheveu. On ne se pose
     pas sur une limite : elle change au moindre ajustement de l'encre. */
  display: block; font-size: .7813rem; line-height: 1.35;
  color: rgba(255, 255, 255, .7);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.rail__card {
  min-width: 0;
  background: var(--surface);
  border-radius: var(--r-lg) var(--r-lg) var(--r-md) var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-3);
  margin-block: 1rem;
}
.rail__panel { min-width: 0; }
/* Le rail en colonne : sa mise en page, et rien de plus. */
@media (min-width: 68rem) {
  /* ⚠️ LE RAIL DEFILE AVEC LA PAGE, exactement comme le contenu. Il n'est ni
     colle ni epingle : il est ANCRE A L'EN-TETE — son bloc de marque coiffe
     les deux bandes du haut, et il part avec elles.

     C'est bien un choix, pas un oubli. Une carte `position: sticky` se
     DETACHE de l'en-tete des le premier pixel de defilement et vient flotter
     seule au-dessus du contenu : ce decrochage est precisement ce qu'on ne
     veut pas. « Fixe » veut dire fixe PAR RAPPORT A L'EN-TETE.

     Consequence : plus aucune contrainte de hauteur de fenetre ne pese sur le
     rail. La carte prend sa taille naturelle, chaque liste montre exactement
     six lignes a toutes les tailles d'ecran, et le rail n'a jamais de barre a
     lui — seules les deux listes defilent. */
  .rail__card {
    margin-block: 0 1.5rem;
    border-radius: var(--r-xl) var(--r-xl) var(--r-md) var(--r-md);
  }

  /* SIX lignes par liste, a toutes les hauteurs d'ecran.
     ⚠️ Le `+ 2px` est la bordure de `.opts`, en border-box : sans lui la
     hauteur utile vaut 6 x ligne - 2, la sixieme ligne est rognee de 2 px et
     la liste en montre 5,8.
     `max-height` et non `height` : une recherche qui ne ramene que trois
     resultats doit rendre la place, pas laisser un trou. */
  .rail .opts { max-height: calc(var(--opt-h) * 6 + 2px); }
}

.main { min-width: 0; padding-block: 1rem clamp(2rem, 1.5rem + 2vw, 3.5rem); }
@media (min-width: 68rem) { .main { padding-block: 1.25rem clamp(2rem, 1.5rem + 2vw, 3.5rem); } }
.col { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }

.rail__head {
  padding: .875rem 1rem .75rem;
  background: var(--surface-2);
  border-block-end: 1px solid var(--border);
}
.rail__title { font-size: 1rem; }
.rail__sub { margin-block-start: .15rem; font-size: .75rem; color: var(--fg-3); }

.rail__tabs {
  display: flex; gap: .25rem;
  padding: 0 1rem;
  background: var(--surface-2);
  border-block-end: 1px solid var(--border);
}
.rail__tab {
  padding: .65rem .1rem; margin-inline-end: 1.15rem;
  border: 0; border-block-end: 2px solid transparent;
  background: none; color: var(--fg-3);
  /* ⚠️ Ce sont les deux PRODUITS du site : c'est le premier choix du rail, et
     il se lisait plus petit que le nom des services listes en dessous. */
  font-size: .9375rem; font-weight: 680; cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.rail__tab.is-on { color: var(--accent); border-block-end-color: var(--accent); }
.rail__tab[disabled] { cursor: not-allowed; opacity: .6; }

/* ── 12. une etape ────────────────────────────────────────────────────── */

.stp { padding: .7rem 1rem; border-block-end: 1px solid var(--border); }
.stp:last-child { border-block-end: 0; }

/* 🔴 C'était un <h3>, et ça n'en est plus un — pas par goût de balise.
   Le rail est rendu AVANT <main> dans la grille de la coque : ses trois
   étapes ouvraient donc le plan de CHAQUE page du site au niveau 3, le <h1>
   arrivant en quatrième position. Mesuré le 23/08/2026 sur les 18 gabarits :
   18 sur 18. Un libellé de commande d'un widget d'achat n'est pas un titre de
   document ; la section porte désormais un `aria-label`, ce qui en fait une
   région nommée — navigable au lecteur d'écran, ce que le <h3> ne faisait pas
   davantage.
   ⚠️ `line-height` et `letter-spacing` étaient hérités de la règle
      `h1,h2,h3,h4` : sans les reporter ici, le passage en <p> les remplacerait
      par ceux du corps (1.55 / normal) et changerait la hauteur du rail. */
.stp__h {
  display: flex; align-items: center; gap: .55rem;
  font-size: .9375rem; font-weight: 650;
  line-height: 1.2; letter-spacing: -0.02em;
  margin-block-end: .6rem;
  min-width: 0;
}
.stp__n {
  display: grid; place-items: center;
  width: 1.375rem; height: 1.375rem; flex: none;
  border-radius: 50%;
  background: var(--surface-3); color: var(--fg-3);
  font-size: .7188rem; font-weight: 700;
}
.stp__n .i { width: .85rem; height: .85rem; }
.stp.is-open .stp__n {
  /* `#fff` en dur : en theme sombre --brand est un bleu CLAIR, et le chiffre
     y devenait illisible (2,5:1). --accent-fg suit le theme. */
  background: var(--brand); color: var(--accent-fg);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand) 15%, transparent);
}
.stp.is-done .stp__n { background: var(--accent-soft); color: var(--accent-ink); }
.stp__label { flex: none; }
.stp.is-done .stp__label { color: var(--fg-3); font-weight: 550; }
.stp__pick {
  flex: 1 1 auto; min-width: 0;
  color: var(--fg); font-weight: 650;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: end;
}
.stp__change {
  flex: none; padding: .15rem .45rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--fg-2);
  font-size: .6875rem; font-weight: 620; cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.stp__change:hover { color: var(--fg); border-color: var(--border-2); }
.stp__count {
  margin-inline-start: auto; flex: none;
  font-size: .6875rem; font-weight: 500; color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.stp.is-done .stp__count { display: none; }
.stp__body[hidden] { display: none !important; }

/* ── 13. la liste d'une etape ─────────────────────────────────────────── */

.opts {
  margin-block-start: .45rem;
  /* ⚠️ Aucune largeur minimale fixe sur le conteneur qui DEFILE : une
     min-width ici et la liste deborde lateralement sur telephone. */
  /* ⚠️ La hauteur est un MULTIPLE DEMI de la hauteur de ligne. A 13 rem on
     coupait au tiers d'une ligne : ca ne se lit pas comme « il y en a
     d'autres », ca se lit comme un defaut de calcul. Une demi-ligne visible
     est la seule coupure qui dise clairement qu'on peut defiler. */
  max-height: calc(var(--opt-h) * 4.5 + 2px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.opt {
  position: relative;
  display: flex; align-items: center; gap: .65rem;
  height: var(--opt-h);
  padding: .4rem .7rem;
  border-block-end: 1px solid var(--border);
  transition: background-color .14s ease;
}
/* Le choix courant porte un liseré vertical : un fond teinte seul se confond
   avec un survol, et on ne sait plus ce qui est choisi de ce qui est pointe. */
.opt.is-on::before {
  content: "";
  position: absolute; inset-block: 0; inset-inline-start: 0;
  width: 3px; border-radius: 0 3px 3px 0;
  background: var(--brand);
}
.opts li:last-child .opt { border-block-end: 0; }
.opt:hover { background: var(--surface-2); }
.opt.is-on { background: var(--accent-soft); }
/* 🔴 `--accent-ink`, PAS `--accent` : sur `--accent-soft` l'accent tombe a
   4,44:1 — sous AA d'un cheveu, et parfaitement invisible a l'oeil. Mesure sur
   la ligne selectionnee d'un menu de page. La regle est deja ecrite dans les
   jetons : toute pastille douce qui porte du TEXTE prend l'encre. */
.opt.is-on .opt__name { color: var(--accent-ink); }
.opt__body { flex: 1 1 auto; min-width: 0; }
.opt__name {
  display: block; font-size: .8438rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opt__sub {
  display: block; font-size: .6875rem; color: var(--fg-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opt__price { flex: none; text-align: end; }
.opt__from { display: block; font-size: .625rem; color: var(--fg-3); line-height: 1; }
.opt__amt {
  display: block; font-size: .8125rem; font-weight: 700;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.opts__empty { padding: 1.25rem .5rem; text-align: center; color: var(--fg-3); font-size: .8125rem; }
.opts__empty[hidden] { display: none !important; }
.opts.is-busy { opacity: .5; transition: opacity .1s ease; }

/* ── 14. l'etape 3 : acheter ──────────────────────────────────────────── */

.buy__idle {
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .55rem .7rem;
  background: var(--surface-2);
  border: 1px dashed var(--border-2);
  border-radius: var(--r);
  font-size: .8125rem; color: var(--fg-3);
}
.buy__idleicon { width: 1.05rem; height: 1.05rem; flex: none; color: var(--fg-3); margin-block-start: .1rem; }

.buy__warn {
  padding: .65rem .75rem;
  background: var(--warn-soft); color: var(--warn);
  border-radius: var(--r);
  font-size: .8125rem; font-weight: 550;
}
.buy__swap {
  display: flex; align-items: center; gap: .5rem;
  margin-block-start: .5rem; padding: .55rem .65rem;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-2);
  font-size: .8125rem;
  transition: border-color .15s ease;
}
.buy__swap:hover { border-color: var(--accent); }
.buy__swaptext { flex: 1 1 auto; min-width: 0; }
.buy__swapprice { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; }

.buy__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
  padding: .8rem .85rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: inset 0 1px 2px rgba(15, 23, 42, .04);
}
.buy__cell { min-width: 0; }
.buy__k {
  display: flex; align-items: center; gap: .25rem;
  font-size: .625rem; font-weight: 700; letter-spacing: .045em;
  text-transform: uppercase; color: var(--fg-3);
  margin-block-end: .2rem;
}
.buy__v {
  margin: 0; font-size: .8438rem; font-weight: 650;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.buy__num {
  display: flex; align-items: baseline; justify-content: space-between; gap: .5rem;
  margin-block: .6rem;
  font-size: .8125rem;
}
.buy__numk { color: var(--fg-3); }
.buy__numv { font-weight: 650; }
.buy__cta { margin-block-start: .1rem; }
.buy__fine { margin-block-start: .45rem; font-size: .6875rem; color: var(--fg-3); text-align: center; }
.buy.is-busy { opacity: .5; }

/* ── 14bis. le choix du reseau ─────────────────────────────────────────
   Une fois sur deux, un couple (service, pays) est servi par PLUSIEURS
   reseaux, a des prix et des taux qui vont du simple au triple. La liste
   remplace la ligne « Network : … » qui imposait le meilleur en silence.
   -------------------------------------------------------------------------- */

.ops { margin-block-end: .75rem; }
.ops__h {
  display: flex; align-items: center; gap: .4rem;
  margin-block-end: .4rem;
  font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-3);
}
.ops__n {
  display: inline-grid; place-items: center;
  min-width: 1.05rem; height: 1.05rem; padding-inline: .25rem;
  border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: .625rem; letter-spacing: 0;
}
/* ⚠️ AUCUN defilement ici. La liste montait a dix lignes et defilait, ce qui
   cachait la moitie des reseaux derriere un geste que rien n'annonce. Le rail
   defile avec la page depuis qu'il n'est plus epingle : rien n'oblige plus
   cette liste a tenir dans une hauteur donnee. */
.ops__l {
  border: 1px solid var(--border); border-radius: var(--r);
}
.op {
  display: flex; align-items: center; gap: .5rem;
  min-height: 2.125rem; padding: .3rem .55rem;
  color: inherit;
  border-block-end: 1px solid var(--border);
  transition: background-color .12s ease;
}
.ops__l li:last-child .op { border-block-end: 0; }
.op:hover { background: var(--surface-2); }
.op__n {
  flex: 1 1 auto; min-width: 0;
  font-size: .8125rem; font-weight: 560;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.op__p {
  flex: none; font-size: .8125rem; font-weight: 680;
  color: var(--accent-ink); font-variant-numeric: tabular-nums;
}
.op.is-on { background: var(--brand-soft); }
.op.is-on .op__n { font-weight: 680; }
/* Le liseré du choix courant : c'est le meme signal que dans les listes du
   rail, pour que « retenu » se lise pareil partout. */
.op.is-on { position: relative; }
.op.is-on::before {
  content: ""; position: absolute;
  inset-block: .25rem; inset-inline-start: 0;
  width: 3px; border-radius: 0 3px 3px 0;
  background: var(--accent);
}

/* ── 15. l'accroche : un seul bloc, celui qu'on voit en arrivant ──────── */

.hero {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-3);
  /* 🔴 L'ACCROCHE EST SUR L'ENCRE. Elle etait une dalle blanche sur une page
     blanche, avec un degrade bleu si pale qu'il ne se voyait pas : rien n'y
     accrochait l'oeil, et la premiere chose qu'on voyait du site etait du gris
     sur du blanc. L'encre est deja le ton des sections fortes (`.blk--ink`) —
     l'accroche prend le meme, donc le site gagne un contraste sans gagner une
     couleur.
     ⚠️ COULEUR et IMAGE separement : avec le raccourci `background:` + degrade,
     `backgroundColor` reste transparent et tout le texte blanc se mesure sur le
     premier ancetre opaque — le fond clair de la page, soit 1,0:1. */
  /* ⚠️ `--ink`, pas `--rail-ink`. Le bleu roi de la laterale, etale sur un bloc
     de cette taille, donne une grande dalle bleue saturee — le meme defaut que
     la dalle blanche, en plus criard. L'encre du bandeau tient la surface, et
     c'est la lueur bleue qui apporte la couleur, par touche. */
  background-color: var(--ink);
  background-image:
    radial-gradient(78% 110% at 86% 6%,
      color-mix(in oklab, var(--brand) 34%, transparent) 0%,
      transparent 58%),
    radial-gradient(60% 90% at 4% 96%,
      color-mix(in oklab, var(--brand) 16%, transparent) 0%,
      transparent 60%),
    linear-gradient(158deg,
      color-mix(in oklab, var(--ink) 88%, #ffffff),
      var(--ink) 62%);
}
/* Le filigrane : la carte SIM, en grand, au coin. Le meme dessin que le logo et
   que les surtitres — un motif, pas une decoration de plus.
   ⚠️ Le cadrage compte autant que le motif : coupe trop haut, il ne reste que
   quatre carres flottants que personne ne relie a un logo. Le coin coupe, seul
   detail qui la rend identifiable, doit rester dans le cadre. */
.hero::after {
  content: ""; position: absolute;
  inset-block-start: -3.5rem; inset-inline-end: -4rem;
  width: 20rem; height: 20rem;
  /* ⚠️ .04, pas .06 : la colonne de chiffres est en verre, le filigrane se voit
     donc AU TRAVERS. Plus marque, ses plots dessinaient des rectangles pales
     derriere les nombres et l'on n'y lisait plus un motif mais un artefact. */
  background: #fff; opacity: .04;
  -webkit-mask: var(--sim-mask) center / contain no-repeat;
          mask: var(--sim-mask) center / contain no-repeat;
  pointer-events: none;
}

/* 🔴 DEUX COLONNES : le discours a gauche, la PREUVE a droite.
   C'etait une dalle d'un seul tenant, tout aligne a gauche, la moitie droite
   vide — sur 1 440 px un bloc large qui ne porte du contenu que d'un cote ne se
   lit pas comme une composition mais comme un gabarit inacheve. */
.hero__grid {
  position: relative; z-index: 1;
  display: grid;
  gap: clamp(1.25rem, .8rem + 1.6vw, 2.25rem);
  padding: clamp(1.6rem, 1.1rem + 2.2vw, 2.75rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}
/* ⚠️ Une largeur FIXE pour la colonne des chiffres, pas une fraction : en
   `1fr 1fr` elle grandissait avec la fenetre et le bloc de chiffres finissait
   plus large que la phrase qu'il est cense appuyer. */
@media (min-width: 62rem) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) 19rem; }
}
.hero__main { min-width: 0; }

/* ⚠️ Couleurs FIXES sur fond encre, pas des jetons. `--fg-2` et `--brand-line`
   basculent avec le theme : en sombre ils donneraient du fonce sur du fonce.
   Un bloc dont le FOND ne change jamais pose son encre en dur. */
/* 🔴 Selecteurs a DEUX classes. Ecrites `.hero__h1` / `.hero__p`, ces couleurs
   etaient battues par les regles de base plus bas dans la feuille : le
   paragraphe restait en `--fg-2`, du gris sombre sur de l'encre — 2,1:1, un
   texte qu'on devine sans pouvoir le lire. */
.hero .kicker   { color: #a8c7fa; }
.hero .hero__h1 { color: #ffffff; }
.hero .hero__p  { color: rgba(255, 255, 255, .80); }

/* La rangee de logos : ce que le site vend, montre. Les vignettes gardent un
   fond CLAIR — un logo de marque pose directement sur l'encre perd ses
   couleurs et se lit comme une tache. */
.hero__withk {
  margin-block-start: 1.4rem;
  font-size: .6875rem; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: rgba(255, 255, 255, .74);
}
.hero__icons {
  display: flex; flex-wrap: wrap; gap: .4rem;
  margin-block-start: .55rem;
}
.hero__icons .mono {
  width: 2.125rem; height: 2.125rem;
  background: rgba(255, 255, 255, .94);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.hero__icons .mono--img img { width: 1.25rem; height: 1.25rem; }
/* 🔴 Chaque logo est un LIEN vers la page du service. Il doit donc se comporter
   comme tel : il se soulève au survol, il montre un anneau au clavier, et son
   nom accessible est un vrai texte (masqué à l'œil, voir `.u-sr`).
   ⚠️ `display: block` sur le lien : en `inline`, sa zone cliquable dépassait
      sous la ligne de base et le survol s'allumait à côté du logo. */
.hero__ilink {
  display: block; border-radius: var(--r-sm);
  transition: transform .14s ease, filter .14s ease;
}
.hero__ilink:hover { transform: translateY(-2px); filter: brightness(1.06); }
.hero__ilink:focus-visible { outline: 2px solid #a8c7fa; outline-offset: 3px; }

/* La colonne de preuve, en verre sur l'encre. Elle n'est pas decorative : elle
   repond a la phrase d'a cote, et c'est ce qui la rend legitime a occuper la
   place. */
.hero__side {
  min-width: 0;
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, .07);
  overflow: hidden;
}
.hero__sideh {
  display: flex; align-items: center; gap: .4rem;
  padding: .55rem .85rem;
  border-block-end: 1px solid rgba(255, 255, 255, .14);
  background: rgba(255, 255, 255, .05);
  font-size: .6875rem; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: rgba(255, 255, 255, .76);
}
.hero__sideh .i { width: .85rem; height: .85rem; color: #a8c7fa; flex: none; }

/* ⚠️ Les filets viennent d'une GOUTTIERE de 1 px qui laisse voir le fond du
   conteneur — une bordure par cellule aurait demande un `nth-child` different
   par palier, et il en manque toujours un. */
.figs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: rgba(255, 255, 255, .16);
}
/* ⚠️ `column-reverse` : le balisage garde `<dt>` avant `<dd>`, comme la norme
   l'impose, et c'est l'affichage qui remonte la valeur au-dessus du libelle. */
.fig {
  display: flex; flex-direction: column-reverse; justify-content: flex-end;
  min-width: 0; padding: .7rem .85rem .75rem;
  /* 🔴 PAS de `color-mix()` sur une surface qui porte du texte. `getComputedStyle`
     la rend en `oklab(...)`, que la sonde de contraste ne sait pas lire : elle
     la prend pour du transparent, remonte, et annonce 1,00:1 sur un texte
     parfaitement lisible. Un blanc translucide sur l'encre se compose, lui,
     sans ambiguite. */
  background: rgba(255, 255, 255, .05);
}
.fig dd {
  font-size: 1.375rem; font-weight: 760; letter-spacing: -.03em;
  line-height: 1.1; color: #ffffff;
  font-variant-numeric: tabular-nums;
}
.fig dt {
  margin-block-start: .15rem;
  /* 🔴 Mesure : .62 donne 4,10:1 sur 11 px, sous AA. Un libelle de chiffre est
     du TEXTE, pas un ornement — il prend le seuil du texte. */
  font-size: .6875rem; line-height: 1.25; color: rgba(255, 255, 255, .78);
}

.hero__h1 {
  font-size: clamp(1.75rem, 1.25rem + 2.1vw, 2.875rem);
  line-height: 1.08;
  letter-spacing: -.038em;
  text-wrap: balance;
}
.hero__p {
  margin-block-start: .85rem;
  max-width: 44rem;
  font-size: clamp(.9375rem, .9rem + .22vw, 1.0625rem);
  color: var(--fg-2);
  text-wrap: pretty;
}


/* ── 18. blocs de contenu ─────────────────────────────────────────────── */

/* ══ LES TROIS TONS D'UNE SECTION ═══════════════════════════════════════
   Toutes les sections etaient la MEME carte blanche : sept boites identiques
   empilees, sans respiration ni hierarchie. Trois tons alternent maintenant —
   carte, nu, encre — et le rythme (carte, nu, carte, nu, carte, encre, carte,
   encre) fait qu'on sait toujours ou l'on en est.
   -------------------------------------------------------------------------- */
.blk {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  padding: clamp(1.5rem, 1.1rem + 1.4vw, 2.5rem);
}

/* Ton 2 — NU : pas de carte du tout, le contenu respire sur le fond de page.
   C'est la respiration entre deux surfaces, et elle ne coute rien. */
.blk--bare {
  background: none;
  box-shadow: none;
  padding: clamp(1.25rem, .8rem + 1.6vw, 2rem) 0 clamp(.75rem, .4rem + 1vw, 1.25rem);
}
/* Sur le fond de page, une carte a fond `--surface-2` disparait : les elements
   posés dans une section nue prennent le blanc plein et leur propre ombre. */
.blk--bare .step {
  background: var(--surface);
  border-color: transparent;
  box-shadow: var(--shadow-1);
}

/* Ton 3 — ENCRE : la section pour developpeurs. Le bloc de code y est chez
   lui, et le contraste avec les cartes claires marque la fin de la partie
   « vitrine ». */
.blk--ink {
  position: relative;
  overflow: hidden;
  /* ⚠️ COULEUR et IMAGE separement : avec le raccourci `background:` + degrade,
     `backgroundColor` reste transparent et tout le texte blanc se mesure sur
     le premier ancetre opaque — le fond clair de la page, soit 1,0:1. */
  background-color: var(--rail-ink);
  background-image:
    linear-gradient(152deg,
      color-mix(in oklab, var(--rail-ink) 84%, #ffffff),
      var(--rail-ink) 58%);
  box-shadow: var(--shadow-3);
}
/* Le filigrane : la carte SIM, en grand, au coin. Le meme dessin que le logo
   et que les surtitres — un motif, pas une decoration de plus. */
.blk--ink::after,
.cta::after {
  content: ""; position: absolute;
  /* ⚠️ Le cadrage compte autant que le motif. Coupe a -14 % en haut, il ne
     restait de la carte que ses quatre plots : quatre carres flottants que
     personne ne relie a un logo. Ici elle deborde legerement des DEUX cotes,
     mais son coin coupe — le seul detail qui la rend identifiable — reste
     dans le cadre. */
  inset-block-start: -2.5rem; inset-inline-end: -3rem;
  width: 19rem; height: 19rem;
  background: #fff; opacity: .06;
  -webkit-mask: var(--sim-mask) center / contain no-repeat;
          mask: var(--sim-mask) center / contain no-repeat;
  pointer-events: none;
}
.blk--ink > * { position: relative; z-index: 1; }
/* ⚠️ Couleurs FIXES sur fond encre, pas des jetons. `--brand-line` bascule
   avec le theme : en sombre il devient #26406b, soit du bleu fonce sur du bleu
   fonce — 1,90:1, illisible. Un bloc dont le FOND ne change jamais doit poser
   son encre en dur.
   ⚠️ Et les couleurs du code etaient reglees pour le fond CLAIR de `.code` :
   posees sur l'encre, le commentaire tombait a 2,48:1 et la chaine a 2,85:1. */
.blk--ink .kicker    { color: #a8c7fa; }
.blk--ink .code__pre { color: rgba(255, 255, 255, .92); }
.blk--ink .c-c       { color: rgba(255, 255, 255, .62); }
.blk--ink .c-s       { color: #9ec5fe; }
.blk--ink .blk__h2   { color: #fff; }
.blk--ink .blk__lede { color: rgba(255, 255, 255, .82); }
.blk--ink .code {
  background: rgba(3, 10, 28, .45);
  border-color: rgba(255, 255, 255, .14);
}
.blk--ink .code__bar   { border-block-end-color: rgba(255, 255, 255, .14); }
.blk--ink .code__file  { color: rgba(255, 255, 255, .6); }
.blk--ink .code__dots i { background: rgba(255, 255, 255, .26); }
.blk--ink .code__copy {
  background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .2); color: #fff;
}
.blk--ink .code__copy:hover { background: rgba(255, 255, 255, .18); }
.blk--ink .code__pre  { color: rgba(255, 255, 255, .92); }
.blk--ink .api__note  { color: rgba(255, 255, 255, .72); }
.blk__h2 { font-size: clamp(1.25rem, 1.1rem + .8vw, 1.625rem); text-wrap: balance; }
.blk__lede { margin-block-start: .55rem; color: var(--fg-2); font-size: .9375rem; max-width: 60ch; }

/* ══ LA FRISE DES TROIS ETAPES ══════════════════════════════════════════
   Trois encarts poses cote a cote ne racontaient pas un parcours : ils se
   lisaient comme trois paragraphes. Ici un rail relie trois noeuds, une
   impulsion le parcourt en boucle, et les cartes se levent l'une apres
   l'autre a l'entree dans le champ. Le mouvement dit l'ORDRE — c'est le seul
   endroit de la page ou l'ordre compte.

   🔴 L'ENTREE EST ECRITE EN CSS PUR (`animation-timeline: view()`), PAS avec
      un IntersectionObserver. Un observateur impose un etat de depart
      `opacity: 0` dans la feuille : le jour ou le script ne s'execute pas —
      erreur de syntaxe, blocage, navigateur ancien — la section reste VIDE,
      en HTTP 200, et rien ne le signale. C'est exactement la panne muette qui
      vient de couter un app.js entier. Hors `@supports`, l'etat par defaut
      reste VISIBLE : au pire, le mouvement manque.
   -------------------------------------------------------------------------- */
.steps__wrap { position: relative; margin-block-start: 1.35rem; }

.steps {
  display: grid; gap: .875rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

/* ⚠️ Le rail et les noeuds n'existent QUE quand les trois etapes tiennent sur
   une ligne. En dessous la grille passe a deux colonnes puis a une, et le
   rail traverserait le vide. */
.steps__rail,
.step__node { display: none; }

.step {
  /* ⚠️ PAS d'`overflow: hidden` ici. Il servait a rogner le filigrane, et il
     rognait aussi le NOEUD de la frise — pose a -2,25rem au-dessus de la carte,
     donc hors de ses limites. Resultat : un rail sans aucun noeud, et rien
     pour le signaler. Le filigrane tient desormais dans la carte. */
  position: relative; z-index: 1;
  min-width: 0; padding: 1.15rem 1.15rem 1.3rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.step__h { font-size: 1rem; margin-block-end: .4rem; }
.step__p { color: var(--fg-2); font-size: .8438rem; }
.step > h3, .step > p { position: relative; z-index: 1; }

/* Le grand numero en filigrane porte l'ordre, a la place d'une pastille de
   plus en gouttiere. */
.step__ghost {
  position: absolute; z-index: 0;
  inset-block-start: 0; inset-inline-end: .55rem;
  font-family: var(--mono-font);
  font-size: 4.25rem; font-weight: 800; line-height: 1;
  letter-spacing: -.05em;
  color: color-mix(in oklab, var(--brand) 13%, transparent);
  pointer-events: none;
}

@media (min-width: 62rem) {
  .steps__wrap { padding-block-start: 3rem; }
  .steps__rail {
    display: block; position: absolute; z-index: 0;
    inset-inline: 16.6%; inset-block-start: calc(1.5rem - 1px);
    height: 2px; border-radius: 2px;
    background: linear-gradient(90deg,
      var(--brand-line), var(--brand) 50%, var(--brand-line));
    transform-origin: 0 50%;
  }
  .step__node {
    display: block; position: absolute; z-index: 2;
    inset-block-start: -2.25rem; inset-inline-start: 50%;
    width: 1.5rem; height: 1.5rem; border-radius: 999px;
    background: var(--surface);
    border: 2px solid var(--brand);
    box-shadow: 0 0 0 .35rem var(--bg);
    transform: translateX(-50%);
  }
  .step__node::after {
    content: ""; position: absolute; inset: .3125rem;
    border-radius: 999px; background: var(--brand);
  }
}

/* L'impulsion qui court d'un bout a l'autre du rail.
   ⚠️ Elle se deplace en `translateX`, pas en `inset-inline-start` : le
   pourcentage se calcule alors sur la largeur du RAIL, et rien ne repasse par
   la mise en page a chaque image. */
@media (min-width: 62rem) and (prefers-reduced-motion: no-preference) {
  .steps__rail::after {
    content: ""; position: absolute;
    inset-block-start: 50%; inset-inline-start: 0;
    width: .5rem; height: .5rem; margin-block-start: -.25rem;
    border-radius: 999px; background: var(--brand);
    box-shadow: 0 0 0 .3rem color-mix(in oklab, var(--brand) 20%, transparent);
    animation: stepRun 4.2s ease-in-out .8s infinite;
  }
}

/* L'entree dans le champ. Etat par defaut : VISIBLE. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .step {
      animation: stepIn linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 20%;
    }
    .step:nth-child(2) { animation-range: entry 4% cover 26%; }
    .step:nth-child(3) { animation-range: entry 4% cover 32%; }
    .steps__rail {
      animation: railIn linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 28%;
    }
    .step__node {
      animation: nodeIn linear both;
      animation-timeline: view();
      animation-range: entry 6% cover 24%;
    }
  }
}
@keyframes stepRun {
  0%       { transform: translateX(0);                  opacity: 0; }
  12%, 88% {                                            opacity: 1; }
  100%     { transform: translateX(calc(100% - .5rem)); opacity: 0; }
}
@keyframes stepIn { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes railIn { from { transform: scaleX(0); }                   to { transform: scaleX(1); } }
@keyframes nodeIn {
  from { opacity: 0; transform: translateX(-50%) scale(.3); }
  to   { opacity: 1; transform: translateX(-50%) scale(1); }
}

.feats {
  display: grid; gap: 1.15rem 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  margin-block-start: 1.35rem;
}

/* ⚠️ L'icone est SUR LA LIGNE DU TITRE, pas au-dessus. Posee en bloc, elle
   mangeait une ligne entiere par carte — six cartes, six lignes de hauteur
   pour zero mot — et cassait l'alignement des titres entre colonnes. En
   gouttiere, elle ancre le titre et la carte se lit d'un bloc.

   Deux colonnes : l'icone occupe la premiere sur la ligne 1, le titre la
   seconde sur la meme ligne, le texte la seconde sur la ligne 2 — donc le
   texte s'aligne sous le TITRE, pas sous l'icone. */
.feat {
  min-width: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: .7rem;
  row-gap: .3rem;
}
.feat__ico {
  grid-column: 1; grid-row: 1; align-self: center;
  display: grid; place-items: center;
  width: 2.125rem; height: 2.125rem; flex: none;
  border-radius: 10px;
  background: var(--accent-soft); color: var(--accent);
}
.feat__ico .i { width: 1.1rem; height: 1.1rem; }
.feat__h {
  grid-column: 2; grid-row: 1; align-self: center;
  font-size: .9375rem; text-wrap: balance;
}
.feat__p { grid-column: 2; grid-row: 2; color: var(--fg-2); font-size: .8438rem; }

/* ── 18bis. dernier appel ─────────────────────────────────── */

.cta {
  position: relative;
  overflow: hidden;
  /* ⚠️ COULEUR et IMAGE posees separement. Avec le raccourci `background:` +
     degrade, `backgroundColor` reste transparent : le texte blanc se retrouve
     alors calcule sur le premier ancetre opaque — le fond clair de la page —
     soit 1,0:1. Et le jour ou le degrade ne peint pas, c'est ce qu'on lirait. */
  background-color: var(--rail-ink);
  background-image:
    linear-gradient(158deg,
      color-mix(in oklab, var(--rail-ink) 82%, #ffffff),
      var(--rail-ink) 62%);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
}
.cta__in { position: relative; z-index: 1; padding: clamp(1.75rem, 1.2rem + 2vw, 3rem); text-align: center; }
.cta__h2 {
  font-size: clamp(1.375rem, 1.15rem + 1vw, 1.875rem);
  color: #fff; text-wrap: balance;
}
.cta__p {
  margin: .65rem auto 0; max-width: 52ch;
  font-size: .9375rem; color: rgba(255, 255, 255, .82);
}
.cta__btns {
  display: flex; flex-wrap: wrap; gap: .625rem;
  justify-content: center; margin-block-start: 1.35rem;
}
.cta .btn--ghost {
  background: transparent; color: #fff;
  border-color: rgba(255, 255, 255, .36);
}
.cta .btn--ghost:hover {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .62);
  color: #fff;
}

/* ── 18ter. les pages produit : fil, configurateur, grille, tunnel ─────
   Tout ce qui n'existe que sur /rental et /one-time. Le reste — coque,
   en-tete, rail, pied — est STRICTEMENT le meme que sur l'accueil : c'est le
   contrat de ce site, une seule coque, seul le contenu change.
   -------------------------------------------------------------------------- */

.crumb {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  font-size: .8125rem; color: var(--fg-3);
  padding-block: .1rem .15rem;
}
.crumb a { color: var(--fg-2); }
.crumb a:hover { color: var(--accent); }

/* ── le configurateur de location ─────────────────────────────────────── */

.rent { margin-block-start: 1.5rem; }
.rent__grid {
  display: grid; gap: .875rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.rent__f { min-width: 0; }
.rent__l {
  display: block; margin-block-end: .35rem;
  font-size: .75rem; font-weight: 650; letter-spacing: .03em;
  text-transform: uppercase; color: var(--fg-3);
}
/* ⚠️ La fleche est dessinee par le conteneur, pas par le <select> : une
   apparence native remplacee sans fleche a soi donne un menu qui ne se lit
   plus comme un menu. */
.rent__sel { position: relative; display: block; }
.rent__sel::after {
  content: ""; position: absolute; z-index: 1;
  inset-inline-end: .85rem; inset-block-start: 50%;
  width: .45rem; height: .45rem; margin-block-start: -.32rem;
  border-inline-end: 2px solid var(--fg-3);
  border-block-end: 2px solid var(--fg-3);
  transform: rotate(45deg);
  pointer-events: none;
}
.rent__i {
  appearance: none; -webkit-appearance: none;
  width: 100%; min-width: 0;
  padding: .7rem 2.25rem .7rem .85rem;
  font: inherit; font-size: .9375rem;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  cursor: pointer;
  text-overflow: ellipsis;
}
.rent__i:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rent__i:disabled { color: var(--fg-3); background: var(--surface-2); cursor: not-allowed; }

.rent__dur { border: 0; padding: 0; margin-block-start: 1.1rem; }
.rent__dur .rent__l { margin-block-end: .45rem; }
.rent__dur { display: flex; flex-wrap: wrap; gap: .5rem; }
.rent__dur legend { float: left; width: 100%; }

.dur {
  display: inline-flex; flex-direction: column; align-items: center;
  min-width: 5.25rem; padding: .55rem .9rem .5rem;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.dur:hover { border-color: var(--brand-line); }
/* ⚠️ Le bouton radio n'est pas `display:none` : masque ainsi, il sort de
   l'ordre de tabulation et la duree devient inatteignable au clavier. */
.dur input {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.dur:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.dur.is-on, .dur:has(input:checked) {
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: var(--shadow-1);
}
.dur__n { font-size: 1.25rem; font-weight: 720; letter-spacing: -.03em; line-height: 1.1; }
.dur.is-on .dur__n, .dur:has(input:checked) .dur__n { color: var(--accent-ink); }
.dur__u { font-size: .6875rem; color: var(--fg-3); }

.rent__go { margin-block-start: .9rem; }

.rent__out { margin-block-start: 1.1rem; }
.rent__idle {
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .8rem .85rem;
  background: var(--surface-2);
  border: 1px dashed var(--border-2);
  border-radius: var(--r);
  font-size: .8438rem; color: var(--fg-3);
}
.rent__idleicon { width: 1rem; height: 1rem; flex: none; color: var(--fg-3); }
.rent__warn {
  padding: .8rem .85rem;
  background: color-mix(in oklab, var(--warn, #b45309) 8%, transparent);
  border: 1px solid color-mix(in oklab, var(--warn, #b45309) 26%, transparent);
  border-radius: var(--r);
  font-size: .8438rem; color: var(--fg-2);
}

.rent__quote {
  padding: 1rem 1.1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}
.rent__sum {
  display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
  font-size: .875rem; color: var(--fg-2);
}
.rent__sumtx { min-width: 0; }
/* Le palier du reseau : trois valeurs, trois teintes. Un numero sur SIM
   physique ne vaut pas un numero virtuel, et le prix seul ne le dit pas. */
.tier {
  flex: none;
  padding: .1rem .45rem; border-radius: 999px;
  font-size: .625rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
}
.tier--virtual  { background: var(--surface-3); color: var(--fg-2); }
.tier--physical { background: var(--accent-soft); color: var(--accent-ink); }
.tier--premium  { background: var(--warn-soft); color: var(--warn); }
.rent__price {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  margin-block-start: .6rem;
}
.rent__amt {
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.375rem);
  font-weight: 730; letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
}
.rent__day { font-size: .8125rem; color: var(--fg-3); }
.rent__act { margin-block-start: .9rem; }
.rent__fine { margin-block-start: .5rem; font-size: .6875rem; color: var(--fg-3); text-align: center; }

/* ── un tableau de prix ───────────────────────────────────────────────── */

/* ⚠️ Le conteneur qui DEFILE ne porte aucune largeur minimale : une
   `min-width` ici et le tableau deborde lateralement sur telephone. */
.tblx { margin-block-start: 1.35rem; overflow-x: auto; }
.grid { width: 100%; border-collapse: collapse; font-size: .875rem; }
.grid th, .grid td { padding: .7rem .8rem; text-align: start; }
.grid thead th {
  font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-3);
  border-block-end: 1px solid var(--border);
}
.grid tbody tr { border-block-end: 1px solid var(--border); }
.grid tbody tr:last-child { border-block-end: 0; }
.grid tbody th { font-weight: 650; white-space: nowrap; }
.grid tbody tr.is-on { background: var(--brand-soft); }
/* ⚠️ `--accent-ink`, pas `--brand` : la ligne retenue a un fond
   `--brand-soft`, et le bleu de marque y tombe a 4,44:1 — sous AA d'un
   cheveu, exactement comme le numero d'etape avant lui. */
.grid__p { font-weight: 680; color: var(--accent-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.grid__d { color: var(--fg-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.grid__wide { color: var(--fg-2); }
@media (max-width: 40rem) { .grid__wide { display: none; } }

/* ── le tunnel deplie ─────────────────────────────────────────────────── */

/* 🔴 UNE BARRE DE COMMANDE, pas trois cartes.
   Trois cartes cote a cote, chacune ne portant qu'un menu de 64 px, laissaient
   les deux tiers de leur surface VIDES pendant que la carte de resultat, elle,
   debordait : un formulaire qui occupe toute la largeur de la page pour dire
   deux mots. Ici : deux segments separes d'un filet, et le resultat en pleine
   largeur dessous. */
.wide { display: grid; gap: .9rem; grid-template-columns: minmax(0, 1fr); }


/* ⚠️ Les filets entre les segments viennent d'une GOUTTIERE de 1 px qui laisse
   voir le fond du conteneur. Une bordure par segment aurait demande un
   `nth-child` different par palier — une colonne sur telephone, deux ensuite —
   et il en manque toujours un. */
.bar {
  display: grid; gap: 1px;
  grid-template-columns: minmax(0, 1fr);
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
@media (min-width: 48rem) { .bar { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.bar__seg {
  position: relative; z-index: 1; min-width: 0;
  padding: .65rem .8rem .8rem;
  background: var(--surface);
  transition: background-color .15s ease;
}
.bar__seg:first-child  { border-radius: var(--r-lg) var(--r-lg) 0 0; }
.bar__seg:last-of-type { border-radius: 0 0 var(--r-lg) var(--r-lg); }
@media (min-width: 48rem) {
  .bar__seg:first-child  { border-radius: var(--r-lg) 0 0 var(--r-lg); }
  .bar__seg:last-of-type { border-radius: 0 var(--r-lg) var(--r-lg) 0; }
}
/* 🔴 Le segment qui porte un menu OUVERT passe devant ses voisins. Ils sont
   plus loin dans le document, donc peints par-dessus : sans cette regle le
   panneau s'ouvre bel et bien, et disparait SOUS le segment d'a cote. */
.bar__seg:has(.dd[open]) { z-index: 50; background: var(--surface); }

.bar__k {
  display: flex; align-items: center; gap: .4rem; min-width: 0;
  margin-block-end: .3rem;
  font-size: .6875rem; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--fg-3);
}
.bar__n {
  display: grid; place-items: center; flex: none;
  width: 1.15rem; height: 1.15rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: .625rem; font-weight: 800;
}
.bar__lab { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar__c {
  margin-inline-start: auto; flex: none;
  letter-spacing: 0; text-transform: none; font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* La carte de resultat : pleine largeur, contenu EN LIGNE. Empilee, elle
   laissait une colonne de blanc a sa droite. */
.wide__res {
  min-width: 0;
  padding: .9rem 1.1rem 1.05rem;
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
@media (min-width: 62rem) {
  .wide__res .buy,
  .wide__res .rent {
    display: grid; gap: .7rem 1.6rem; margin: 0;
    grid-template-columns: minmax(0, 1fr) 18rem;
    align-items: start;
  }
  .wide__res .buy .ops,
  .wide__res .buy .buy__one { grid-column: 1; grid-row: 1 / span 3; }
  .wide__res .buy .buy__num  { grid-column: 2; grid-row: 1; margin: 0; }
  .wide__res .buy .buy__cta  { grid-column: 2; grid-row: 2; margin: 0; }
  .wide__res .buy .buy__fine { grid-column: 2; grid-row: 3; margin: 0; }

  /* La location tient sur UNE ligne : les durees a gauche, le devis a droite.
     🔴 En deux colonnes egales, les trois pastilles de duree laissaient sous
     elles une demi-page blanche pendant que le devis, lui, s'etirait — le
     defaut meme qu'on vient de corriger sur la barre. */
  .wide__res .rent { grid-template-columns: auto minmax(0, 1fr); align-items: center; }
  .wide__res .rent__dur { grid-column: 1; grid-row: 1; margin: 0; }
  .wide__res .rent__go  { grid-column: 1; grid-row: 2; margin: 0; }
  .wide__res .rent__out { grid-column: 2; grid-row: 1 / span 2; margin: 0; }

  /* ⚠️ Le devis perd sa propre carte : il est DEJA dans une carte, et une
     surface dans une surface se lit comme une erreur de gabarit. */
  .wide__res .rent__quote {
    display: grid; gap: .35rem 1.5rem; align-items: center;
    grid-template-columns: minmax(0, 1fr) 16rem;
    border: 0; background: none; padding: 0; box-shadow: none;
  }
  .wide__res .rent__sum   { grid-column: 1; grid-row: 1; margin: 0; }
  .wide__res .rent__price { grid-column: 1; grid-row: 2; margin: 0; }
  .wide__res .rent__act   { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
  .wide__res .rent__fine  { grid-column: 2; grid-row: 3; margin: 0; }

  /* Les reseaux se rangent en tuiles : en colonne, dix reseaux faisaient une
     carte deux fois plus haute que le bloc qui vend, juste a cote. */
  .wide__res .ops__l {
    display: grid; gap: .4rem;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  }
}
.wide__res .buy__idle,
.wide__res .rent__idle { margin: 0; }
.wide__buy { min-width: 0; }

/* ── 18bis. le gros menu deroulant des PAGES depliees ────────────────────
   ⚠️ C'est un `<details>` / `<summary>` : le seul selecteur qui s'ouvre et se
      ferme SANS une ligne de script, et la CSP de ce site interdit tout
      `<script>` en ligne. Le script n'ajoute que le confort — fermeture au clic
      exterieur, Echap, fleches, focus dans la recherche.

   ⚠️ Le RAIL n'en porte aucun : ses listes restent ouvertes, c'est sa raison
      d'etre. Les pages, elles, ont besoin de leur hauteur pour le reste.
   ──────────────────────────────────────────────────────── */
.dd { position: relative; }

/* ⚠️ Le triangle natif du <summary>, sur les deux moteurs : `list-style` pour
   Firefox, le pseudo-element pour WebKit. En oublier un laisse une fleche
   parasite a cote de la notre, sur un seul navigateur. */
.dd__btn { list-style: none; }
.dd__btn::-webkit-details-marker { display: none; }

/* 🔴 CA DOIT SE LIRE COMME UN MENU DEROULANT, sans qu'on ait a l'essayer.
   Premiere version sans bordure ni fond : elle tenait mieux dans la barre, et
   plus personne ne voyait qu'on pouvait cliquer — le chevron seul ne suffit
   pas, il passe pour un ornement. Ce qui fait reconnaitre un selecteur, c'est
   la combinaison qu'on connait depuis toujours : un CHAMP delimite, et une
   ZONE DE FLECHE separee par un filet a son extremite. */
.dd__btn {
  display: flex; align-items: center; gap: .6rem;
  /* 48 px : la cible tactile, sans faire enfler la barre. */
  min-height: 3rem;
  padding: .35rem 0 .35rem .6rem;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.dd__btn:hover { border-color: var(--brand-line); background: var(--surface); }
.dd__btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.dd[open] .dd__btn {
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.dd__badge {
  display: grid; place-items: center; flex: none;
  width: 1.75rem; height: 1.75rem; border-radius: 8px;
  background: var(--surface-3); color: var(--fg-3);
}
.dd__badge svg { width: .95rem; height: .95rem; }

/* ⚠️ `min-width: 0` sur la colonne de texte : sans lui, un nom de service long
   pousse le bouton au-dela de la carte au lieu de se couper. */
.dd__tx { min-width: 0; flex: 1 1 auto; display: grid; gap: .1rem; }
.dd__name {
  font-size: .9375rem; font-weight: 650; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd__ph {
  font-size: .9375rem; font-weight: 550; color: var(--fg-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd__sub {
  display: flex; align-items: center; gap: .35rem; min-width: 0;
  font-size: .75rem; color: var(--fg-3);
}
.dd__sub > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd__price {
  flex: none; font-size: .9375rem; font-weight: 700; color: var(--fg);
  font-variant-numeric: tabular-nums;
}
/* La zone de fleche : separee par un filet, comme sur un <select> natif. C'est
   ce filet, plus que le chevron, qui fait dire « ca s'ouvre ». */
.dd__chev {
  flex: none;
  box-sizing: content-box;
  width: 1.1rem; height: 1.1rem;
  padding: .55rem .6rem;
  margin-inline-start: .25rem;
  border-inline-start: 1px solid var(--border);
  color: var(--fg-2);
  transition: rotate .18s ease, color .18s ease;
}
.dd__btn:hover .dd__chev { color: var(--accent); }
.dd[open] .dd__chev { rotate: 180deg; color: var(--accent); border-inline-start-color: var(--accent-line); }

/* Le panneau RECOUVRE, il ne pousse pas : c'est tout l'interet du menu.
   ⚠️ Il deborde de 8 px de chaque cote du segment : cale au ras, il se lisait
   comme la suite du segment plutot que comme une surface posee dessus. */
.dd__panel {
  position: absolute; z-index: 50;
  inset-inline: -.5rem; top: calc(100% + .4rem);
  padding: .6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3), var(--glow-brand);
}
@media (prefers-reduced-motion: no-preference) {
  .dd[open] .dd__panel { animation: dd-in .14s ease-out; }
  @keyframes dd-in { from { opacity: 0; translate: 0 -.35rem; } }
}
.dd__q { display: flex; gap: .5rem; align-items: center; margin-block-end: .6rem; }
.dd__q .srch { flex: 1 1 auto; min-width: 0; }
/* ⚠️ La liste porte sa propre hauteur ET son propre defilement. Sans
   `min-width: 0`, une ligne au nom long etire la colonne et casse la grille. */
.dd__list {
  min-width: 0;
  max-height: calc(var(--opt-h) * 6 + 2px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  border: 1px solid var(--border);
  border-radius: var(--r);
}

/* La consigne qui remplace le menu tant qu'il n'a rien a proposer. */
.dd__wait {
  display: flex; align-items: center; gap: .45rem;
  min-height: 2.75rem; padding: .3rem .45rem; margin: 0;
  font-size: .8125rem; color: var(--fg-3);
}
.dd__waiticon { width: 1rem; height: 1rem; flex: none; color: var(--fg-3); }



.blk__go { margin-block-start: 1.35rem; }

/* La frise a QUATRE etapes : le rail doit se caler sur des colonnes plus
   etroites, sinon il depasse des noeuds des deux cotes. */
@media (min-width: 62rem) {
  .steps--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .steps__wrap--4 .steps__rail { inset-inline: 12.5%; }
}

/* ── 19. bloc de code ─────────────────────────────────────────────────── */

.code {
  min-width: 0; margin-block-start: 1.25rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
}
.code__bar {
  display: flex; align-items: center; gap: .625rem;
  padding: .45rem .7rem;
  border-block-end: 1px solid var(--border);
}
.code__dots { display: inline-flex; gap: .28rem; }
.code__dots i { width: .5rem; height: .5rem; border-radius: 50%; background: var(--border-2); }
.code__file {
  font-family: var(--mono-font); font-size: .7188rem; color: var(--fg-3);
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.code__copy {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .25rem .5rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: .7188rem; font-weight: 620; color: var(--fg-2);
  cursor: pointer; flex: none;
  transition: color .15s ease, border-color .15s ease;
}
.code__copy:hover { color: var(--fg); border-color: var(--border-2); }
.code__copy.is-done { color: var(--accent); border-color: var(--accent-line); }
.code__copy .i { width: .9rem; height: .9rem; }
.code__pre {
  margin: 0; padding: .875rem;
  /* Le bloc defile dans SON conteneur : le corps de la page ne bouge pas. */
  overflow-x: auto;
  font-family: var(--mono-font);
  font-size: .75rem; line-height: 1.7; tab-size: 2;
}
.code__pre code { white-space: pre; }
.c-c { color: var(--fg-3); }
.c-s { color: var(--accent); }
.api__note {
  display: flex; align-items: center; gap: .4rem;
  margin-block-start: .75rem; font-size: .75rem; color: var(--fg-3);
}
.api__lock { width: .9rem; height: .9rem; }

/* ── 20. FAQ ──────────────────────────────────────────────────────────── */

.faq {
  margin-block-start: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
}
.faq__i + .faq__i { border-block-start: 1px solid var(--border); }
.faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .85rem 1rem;
  font-weight: 620; font-size: .9063rem;
  cursor: pointer; list-style: none;
  transition: background-color .15s ease;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { background: var(--surface-2); }
.faq__chev { color: var(--fg-3); flex: none; transition: transform .2s ease; }
.faq__i[open] .faq__chev { transform: rotate(180deg); }
.faq__a { padding: 0 1rem 1rem; color: var(--fg-2); max-width: 64ch; font-size: .875rem; }

/* ── 21. infobulle ────────────────────────────────────────────────────── */

.hint { position: relative; display: inline-grid; place-items: center; cursor: help; }
.hint__q {
  width: .875rem; height: .875rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-3); color: var(--fg-2);
  font-size: .5625rem; font-weight: 700;
}
.hint__bubble {
  position: absolute; bottom: calc(100% + .45rem); inset-inline-start: 50%;
  translate: -50% 0;
  width: max-content; max-width: 15rem;
  padding: .55rem .7rem;
  background: var(--fg); color: var(--bg-2);
  border-radius: var(--r);
  box-shadow: var(--shadow-3);
  font-size: .6875rem; font-weight: 450; line-height: 1.45;
  letter-spacing: 0; text-transform: none;
  opacity: 0; visibility: hidden;
  transition: opacity .14s ease, visibility .14s ease;
  z-index: 30;
}
.hint:hover .hint__bubble, .hint:focus-visible .hint__bubble { opacity: 1; visibility: visible; }

/* ── 22. page absente ─────────────────────────────────────────────────── */

.miss { padding-block: clamp(3rem, 2.5rem + 3vw, 6rem); }
.miss__in { text-align: center; }
.miss__h { font-size: clamp(1.5rem, 1.25rem + 1.4vw, 2.25rem); margin-block-start: .5rem; }
.miss__p { margin-block-start: .75rem; color: var(--fg-2); }
.miss__btns { margin-block-start: 1.5rem; }

/* ── 23. pied de page ─────────────────────────────────────────────────── */

.ftr { border-block-start: 1px solid var(--border); background: var(--surface); }
.ftr__coin { box-shadow: var(--shadow-1); }
.ftr__top {
  display: grid; gap: 1.75rem;
  grid-template-columns: minmax(0, 1fr);
  padding-block: clamp(2rem, 1.6rem + 1.8vw, 3rem) 1.5rem;
}
@media (min-width: 40rem) { .ftr__top { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) {
  .ftr__top { grid-template-columns: minmax(0, 1.5fr) repeat(4, minmax(0, 1fr)); gap: 2rem 1.25rem; }
}
.ftr__brand { min-width: 0; }
.ftr__blurb { margin-block-start: .75rem; max-width: 26rem; font-size: .8125rem; color: var(--fg-2); }
.ftr__paylabel {
  margin-block-start: 1.15rem;
  font-size: .625rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3);
}
.ftr__coins { display: flex; flex-wrap: wrap; gap: .35rem; margin-block-start: .5rem; }
.ftr__coin {
  display: inline-flex; align-items: center; gap: .28rem;
  padding: .22rem .45rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2);
  font-size: .625rem; font-weight: 700; color: var(--fg-2);
}
.coin { width: .875rem; height: .875rem; }
.ftr__col { min-width: 0; }
.ftr__h {
  font-size: .6875rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3); margin-block-end: .7rem;
}
.ftr__list { display: grid; gap: .45rem; }
.ftr__a { font-size: .8125rem; color: var(--fg-2); transition: color .15s ease; }
.ftr__a:hover { color: var(--fg); }
.ftr__bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.25rem;
  padding-block: 1rem 1.5rem;
  border-block-start: 1px solid var(--border);
  font-size: .75rem; color: var(--fg-3);
}
.ftr__badge { display: inline-flex; align-items: center; gap: .35rem; }
.ftr__lock { width: .9rem; height: .9rem; }
.ftr__sync { margin-inline-start: auto; }

/* ── 24. tactile, mouvement, impression ───────────────────────────────── */

/* ⚠️ Une cible tactile se decide au POINTEUR, jamais a la largeur d'ecran :
   une tablette large se manipule au doigt, une fenetre etroite sur portable
   se manipule a la souris. */
@media (pointer: coarse) {
  .nav__a, .ftr__a { min-height: 44px; display: flex; align-items: center; }
  .opt { min-height: 46px; }
  .tr  { min-height: 52px; }
  .iconbtn { width: 2.625rem; height: 2.625rem; }
  .btn { padding-block: .68rem; }
  .btn--sm { padding-block: .45rem; }
  .stp__change { padding-block: .35rem; }
  .opts { max-height: 15rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

@media print {
  .hdr, .mnav, .rail, .skip { display: none !important; }
  body { background: #fff; color: #000; }
}

/* Une etape repliee libere ~250 px : la liste encore ouverte les prend —
   toujours en demi-lignes. */
@media (max-width: 67.999rem) {
  .rail:has(.stp.is-done) .opts { max-height: calc(3.375rem * 6.5); }
}

/* Barres de defilement discretes : par defaut, celle de Chrome sur fond clair
   dessine une bande grise franche au bord de chaque liste. */
.opts::-webkit-scrollbar, .rail__panel::-webkit-scrollbar { width: 10px; }
.opts::-webkit-scrollbar-thumb, .rail__panel::-webkit-scrollbar-thumb {
  background: var(--border-2); border-radius: 99px;
  border: 3px solid var(--surface);
}
.opts::-webkit-scrollbar-track, .rail__panel::-webkit-scrollbar-track { background: transparent; }

/* L'etape 3 ferme le rail : un fond plein, un filet et une ombre montante la
   detachent des deux listes.
   ⚠️ Elle a ete `position: sticky; bottom: 0` tant que le rail etait un
   conteneur qui defilait. Le rail defile maintenant AVEC LA PAGE : il n'y a
   plus de conteneur ou se coller, et l'epinglage ne faisait plus rien.
   `relative` est garde pour la seule chose qui restait utile — le contexte
   d'empilement, sans lequel l'ombre montante passe SOUS l'etape voisine. */
@media (min-width: 68rem) {
  .stp[data-step="buy"] {
    position: relative;
    background: var(--surface);
    border-block-start: 1px solid var(--border);
    box-shadow: 0 -8px 16px -12px rgba(15, 23, 42, .25);
  }
}

/* ── 25. icones officielles et drapeaux ───────────────────────────────────
   Les fichiers sont TELECHARGES et servis par nous. La CSP (`img-src 'self'
   data:`) refuserait une image externe — sans erreur, en laissant des trous a
   la place des icones. */

.mono--img { background: var(--surface-2); border: 1px solid var(--border); }
.mono--img img { width: 1.25rem; height: 1.25rem; object-fit: contain; }
.mono--lg.mono--img img { width: 1.5rem; height: 1.5rem; }

.flag {
  display: block; flex: none;
  width: 1.5rem; height: 1.125rem;
  border-radius: 3px; overflow: hidden;
  /* Un liseré interne : sans lui, un drapeau a fond blanc (Japon, Pologne…)
     se fond dans la surface et n'a plus de forme. */
  box-shadow: 0 0 0 1px rgba(15, 23, 42, .12) inset;
}
.flag img { width: 100%; height: 100%; object-fit: cover; display: block; }
.flag--lg { width: 2rem; height: 1.5rem; border-radius: 4px; }

/* Dans une ligne de texte (bloc d'achat), le drapeau s'aligne sur la ligne. */
.buy__numv { display: inline-flex; align-items: center; gap: .35rem; }
.buy__numv .flag { width: 1.125rem; height: .875rem; }

/* ⚠️ CE BLOC EST EN FIN DE FEUILLE, ET IL DOIT Y RESTER. Place plus haut — il
   l'a ete — toutes les regles de `.bar`, `.dd` et `.wide__res` ecrites APRES
   lui le battaient a specificite egale : la barre du bas s'affichait avec les
   menus l'un SOUS l'autre, les cartes de bureau et leurs ombres. Une media
   query ne pese pas plus lourd qu'une regle simple. */
/* ── LE TELEPHONE : le formulaire est COLLE EN BAS, le rail disparait ─────
   🔴 Sans colonne ou vivre, le rail retombait EN HAUT DE PAGE : une carte d'un
      ecran et demi — deux listes de six lignes et un encart d'achat — qu'il
      fallait franchir avant de lire la premiere phrase du site. Sur telephone,
      ce qui vend doit etre sous le POUCE, pas au-dessus du titre.

   Le meme bloc que la barre des pages produit, en barre d'application : deux
   menus et le bouton, toujours visibles, jamais dans le chemin.
   ─────────────────────────────────────────────────────────── */
@media (max-width: 67.999rem) {
  .rail { display: none; }

  .wide {
    position: fixed; z-index: 60;
    inset-inline: 0; bottom: 0;
    gap: .5rem;
    padding: .55rem .7rem;
    /* ⚠️ `env()` avec un repli : sur un telephone sans encoche la variable vaut
       0, et sans le `max()` la barre collerait au bord — mesure a 8 px de vide
       sous les menus, ce qui se lit comme un debordement. */
    padding-block-end: max(.75rem, env(safe-area-inset-bottom));
    background: var(--surface);
    border-block-start: 1px solid var(--border);
    box-shadow: 0 -8px 24px -12px rgba(15, 23, 42, .28);
  }
  /* ⚠️ Le pied de page passerait SOUS la barre : ses derniers liens seraient
     inatteignables, et cela ne se voit qu'en allant au bas de la page.
     La reserve est MESUREE, pas devinee — `./bin/rail-click.sh` section D la
     compare a la hauteur reelle de la barre sur les trois pages, et echoue si
     elle passe dessous. Mesure a 390 px : jusqu'a 195 px avec les onglets et le
     bouton d'achat, 325 px sur /rental qui porte en plus les trois durees et le
     recapitulatif. D'ou deux valeurs, et de la marge : chaque element ajoute a
     la barre la fait grandir, et c'est le pied de page qui paie. */
  /* 🔴 `:has([data-wide])` — la réserve ne vaut QUE pour les pages qui portent
     vraiment la barre. Posée sur `body` tout court, elle ajoutait 280 px de
     blanc au bas de CHAQUE page de compte : /topup, /signin, /api-key,
     /settings n'ont pas de barre d'achat, et on scrollait un tiers d'écran de
     vide après le pied de page. Mesuré à 390 px : document 2 210 px pour
     1 930 px de contenu. */
  /* 🔴 `--dock-h` est MESURÉE par le script sur la barre elle-même, et les
     valeurs écrites ici ne sont que le repli sans JavaScript.
     Pourquoi : la réserve doit couvrir la barre la plus HAUTE (325 px sur
     /rental avec les durées et le récapitulatif), mais la barre au repos n'en
     fait que 144. Une constante généreuse laissait donc ~136 px de blanc sous
     le pied de page — sur toutes les pages, maintenant que le dock y est.
     Avec la mesure, la réserve colle à la barre et suit ses changements ; sans
     script la barre ne bouge plus après le rendu, donc le repli généreux est
     exact au pire d'un peu de marge — le bon sens de l'erreur : le pied de page
     ne passe JAMAIS sous la barre. */
  body:has([data-wide]) { padding-block-end: calc(var(--dock-h, 17.5rem) + env(safe-area-inset-bottom)); }
  body.p-rental:has([data-wide]) { padding-block-end: calc(var(--dock-h, 22rem) + env(safe-area-inset-bottom)); }
  /* Tant que la duree n'est pas affichee, la barre est courte : reserver la
     hauteur du cas complet laisserait un bloc de blanc en bas de page. */
  body.p-rental:has([data-wide]):has(.rent--idle) { padding-block-end: calc(14rem + env(safe-area-inset-bottom)); }

  .bar {
    border: 0; border-radius: 0; box-shadow: none; background: none;
    gap: .5rem; grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .bar__seg { padding: 0; border-radius: 0; background: none; }
  /* Le surtitre de l'etape passe en simple numero : sur 180 px de large,
     « SELECT SERVICE » + le compteur mangeaient toute la ligne. */
  .bar__k { margin: 0 0 .25rem; gap: .3rem; }
  .bar__lab { font-size: .625rem; }
  .bar__c { display: none; }
  .dd__btn { min-height: 2.75rem; padding: .25rem 0 .25rem .45rem; gap: .45rem; }
  .dd__sub { display: none; }
  .dd__badge { width: 1.5rem; height: 1.5rem; }
  /* 🔴 Le prix quitte le bouton du menu. Sur 180 px de large il ne laissait au
     nom que trois lettres et des points de suspension — « T⋯ », « U⋯ », soit
     exactement l'information que le menu existe pour montrer. Il est deja sur le
     bouton d'achat, en gros, juste dessous. */
  .dd__price { display: none; }
  /* ⚠️ La pastille grise du placeholder degage aussi : elle ne porte aucune
     information (c'est la meme loupe que dans le champ de recherche) et coutait
     les 30 px qui coupaient « Choose a se⋯ ». La pastille du CHOIX, elle —
     l'icone de marque, le drapeau — reste : celle-la se lit d'un coup d'oeil. */
  .dd__badge { display: none; }
  .dd__ph { font-size: .875rem; }
  .dd__chev { padding: .5rem .4rem; margin-inline-start: .15rem; }
  .dd .mono, .dd .flag--lg { width: 1.5rem; }
  .dd .flag--lg { height: 1.125rem; }

  /* 🔴 Le panneau s'ouvre VERS LE HAUT. Sous la barre il n'y a rien : ouvert
     vers le bas, il sortait entierement de l'ecran — le menu repondait, et on
     ne voyait rien se produire.

     🔴 Et il prend TOUTE la largeur, pas celle de son segment. Cale sur les
     180 px du segment, ses lignes se coupaient a « Whats⋯ » / « 367K nu⋯ » et
     le champ de recherche n'affichait plus que « Find » : le menu montrait
     moins que le bouton qui l'ouvre. On rend donc le SEGMENT non positionne —
     le bloc conteneur du panneau devient la barre entiere. */
     ⚠️ Et c'est `.dd` — le <details> lui-meme — qu'il faut rendre non
     positionne, pas seulement le segment : c'est LUI le bloc conteneur du
     panneau. Ne neutraliser que le segment ne change strictement rien, et la
     capture d'ecran revient identique au pixel pres. */
  .bar { position: relative; }
  .bar__seg, .dd { position: static; }
  .dd__panel {
    inset-inline: 0; top: auto; bottom: calc(100% + .5rem);
    max-height: 62vh; overflow: auto;
  }
  .dd__list { max-height: 44vh; }

  /* Le resultat se reduit a ce qui VEND : le bouton et son prix. Le choix du
     reseau, le recapitulatif et les mentions restent dans la page, ou l'on a la
     place de les lire. */
  .wide__res {
    padding: 0; border-radius: 0; box-shadow: none; background: none;
  }
  .wide__res > .bar__k,
  .wide__res .buy__one,
  .wide__res .buy__num,
  .wide__res .buy__fine,
  .wide__res .rent__sum,
  .wide__res .rent__fine,
  .wide__res .rent__go { display: none; }
  .wide__res .buy__cta { margin: 0; }

  /* 🔴 LE RESEAU RESTE CHOISISSABLE. Masque pour gagner de la place, il ne
     l'etait plus NULLE PART sur telephone : le rail est cache, et la page ne
     porte pas de second encart d'achat. Or la moitie des couples (service,
     pays) ont plusieurs reseaux — jusqu'a dix — a des prix qui vont du simple
     au triple. Le masquer revenait a choisir a la place du visiteur un ecran
     sur deux, et c'est la location, elle, qui offrait le choix.

     Il tient donc sur UNE ligne qui defile lateralement, comme une rangee de
     filtres. ⚠️ Le libelle « NETWORK · 8 » reste : sans lui, une rangee de
     pastilles a prix ne dit pas ce qu'elle propose. */
  .wide__res .ops { margin: 0 0 .5rem; }
  .wide__res .ops__h { margin-block-end: .3rem; }
  .wide__res .ops__l {
    display: flex; gap: .35rem;
    overflow-x: auto; overscroll-behavior-x: contain;
    scrollbar-width: none;
    border: 0; padding-block-end: .15rem;
    scroll-snap-type: x proximity;
  }
  .wide__res .ops__l::-webkit-scrollbar { display: none; }
  .wide__res .ops__l li { flex: none; scroll-snap-align: start; }
  .wide__res .op {
    gap: .4rem; min-height: 2rem; padding: .25rem .6rem;
    border: 1px solid var(--border); border-radius: 999px;
  }
  .wide__res .op__n { flex: 0 1 auto; font-size: .75rem; }
  .wide__res .op__p { font-size: .75rem; }
  .wide__res .op.is-on { border-color: var(--accent-line); }
  /* Le lisere vertical du choix courant n'a pas de sens sur une pastille : la
     bordure et le fond le disent deja, et il depassait du coin arrondi. */
  .wide__res .op.is-on::before { display: none; }

  /* La location garde ses durees UNE FOIS le pays et le reseau choisis : le
     prix du bouton en depend, et les faire defiler hors de vue rendrait ce prix
     inexplicable.
     🔴 Mais pas AVANT : trois pastilles pleine largeur pour un prix qui ne peut
     pas encore exister, c'est 90 px pris a ce qui vend, sur l'ecran ou la
     hauteur est la plus rare. */
  .wide__res .rent--idle .rent__dur { display: none; }
  .wide__res .rent { display: grid; gap: .5rem; margin: 0; }
  .wide__res .rent__dur { margin: 0; gap: .35rem; }
  .wide__res .rent__dur .dur { flex: 1 1 0; padding-block: .3rem; }
  .wide__res .rent__dur .dur__n { font-size: 1rem; }
  .wide__res .rent__out { margin: 0; }
  .wide__res .rent__quote { border: 0; background: none; padding: 0; }
  .wide__res .rent__price { margin: 0 0 .4rem; }
  .wide__res .rent__amt { font-size: 1.5rem; }
  .wide__res .rent__act { margin: 0; }

  /* 🔴 La consigne « Pick a service and a country… » ne s'affiche PAS ici. Sur
     un ecran de 780 px dont la barre occupe deja 145, deux lignes de texte pour
     dire ce que les deux menus juste au-dessus disent d'eux-memes, c'est de la
     hauteur prise a ce qui vend. Elle reste dans le rail et dans la page. */
  .wide__res .buy__idle, .wide__res .rent__idle { display: none; }
  .wide__res .buy__warn { margin: 0; font-size: .8125rem; }

  /* 🔴 LES DEUX PRODUITS. La barre remplace le rail, et le rail portait les
     onglets : sans eux, qui arrive sur le code unique n'a aucun moyen de
     decouvrir la location. Un segmente compact, pose sur la barre. */
  .wide__tabs {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2px; padding: 2px; margin-block-end: .5rem;
    background: var(--surface-3); border-radius: 999px;
  }
  .wide__tab {
    padding: .3rem .5rem; border-radius: 999px;
    font-size: .75rem; font-weight: 650; text-align: center;
    color: var(--fg-3);
  }
  .wide__tab.is-on {
    background: var(--surface); color: var(--fg);
    box-shadow: var(--shadow-1);
  }
}

/* Sur grand ecran, l'accueil n'affiche pas cette barre : le rail y est le
   tunnel, et deux formulaires pour la meme chose sur le meme ecran, c'est un
   formulaire de trop. Les onglets de produit non plus : le rail les porte. */
@media (min-width: 68rem) {
  .wide--dock { display: none; }
  .wide__tabs { display: none; }
}

/* ── L'ACCROCHE SUR PETIT ÉCRAN ────────────────────────────────────────────
   🔴 Empilée telle quelle, elle mangeait 527 px sur un écran de 780 : deux
      tiers de la première vue pour un titre, un paragraphe et quatre nombres,
      avant le moindre contenu. Sur téléphone une accroche doit se lire d'un
      coup d'œil, pas se dérouler.

   ⚠️ Le texte n'est pas raccourci — on ne coupe pas une phrase par une feuille
      de style, et une accroche tronquée ne veut plus rien dire. C'est la
      DENSITÉ qui change : corps, interlignes, chrome.
   ────────────────────────────────────────────────────────────────────────── */
@media (max-width: 61.999rem) {
  .hero__grid { padding: 1.35rem 1.15rem 1.45rem; gap: 1.05rem; }
  .hero__h1 { line-height: 1.12; }
  /* ⚠️ Le texte n'est pas raccourci — on ne coupe pas une phrase par une
     feuille de style. C'est le corps qui descend d'un cran : cinq lignes
     deviennent quatre, et le paragraphe cesse d'etre la masse dominante. */
  .hero__p { margin-block-start: .55rem; font-size: .875rem; line-height: 1.5; }

  /* Les chiffres perdent leur en-tête et leur ombre : sur 390 px, « LIVE
     CATALOGUE » coûtait 34 px pour redire ce que les quatre libellés disent
     déjà. La grille reste à DEUX colonnes — à quatre, « services in stock »
     tombe sur trois lignes et la frise est plus haute qu'avant. */
  .hero__sideh { display: none; }
  /* ⚠️ Huit logos, pas douze : au-dela ils passent a la ligne et la rangee
     coute 44 px de plus pour redire la meme chose. Ceux qui restent sont les
     plus demandes — c'est l'ordre du catalogue, pas un choix de gabarit. */
  .hero__withk { margin-block-start: 1.1rem; }
  .hero__icons li:nth-child(n+9) { display: none; }
  .hero__icons .mono { width: 1.9rem; height: 1.9rem; }
  .fig { padding: .55rem .7rem .6rem; }
  .fig dd { font-size: 1.1875rem; }
  .fig dt { font-size: .625rem; }
}

/* ── 24. la page de compte ─────────────────────────────────────────────────
   Une seule page pour se connecter, ouvrir un compte et recuperer un mot de
   passe. Trois volets, un seul visible ; les deux autres restent dans le
   document — c'est ce qui permet de basculer sans requete, et surtout ce qui
   laisse le gestionnaire de mots de passe du navigateur voir les champs qu'il
   attend, avec leurs `autocomplete`.
   ────────────────────────────────────────────────────────────────────────── */
/* 🔴 UNE PAGE, pas une carte perdue au milieu. Le formulaire seul laissait les
   deux tiers de la colonne vides — et la question que se pose vraiment
   quelqu'un devant une inscription sur un site sans KYC (« qu'est-ce que vous
   allez me demander ? ») n'avait nulle part ou etre repondue.
   ⚠️ Largeur FIXE pour le formulaire : en fraction, les champs s'etiraient a
   700 px, ce qui rend une adresse mail plus dure a relire, pas plus facile. */
.auth {
  display: grid; gap: clamp(1rem, .6rem + 1.6vw, 2rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 62rem) {
  .auth { grid-template-columns: 27rem minmax(0, 1fr); }
}
.auth .crumb { margin-block-end: .9rem; }

.auth__card {
  padding: clamp(1.15rem, .9rem + 1vw, 1.75rem);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
}

/* Les deux onglets : meme segmente que les produits, pour que « changer de
   mode » se manipule partout de la meme facon. */
.auth__tabs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px; padding: 2px; margin-block-end: 1.15rem;
  background: var(--surface-3); border-radius: 999px;
}
.auth__tab {
  padding: .45rem .6rem; border-radius: 999px;
  font-size: .8125rem; font-weight: 650; text-align: center; color: var(--fg-3);
}
.auth__tab.is-on { background: var(--surface); color: var(--fg); box-shadow: var(--shadow-1); }
.auth__tab:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.auth__h1 { font-size: 1.375rem; letter-spacing: -.02em; }
.auth__lede { margin-block: .35rem 1.1rem; font-size: .875rem; color: var(--fg-3); }
.auth__fine { margin-block-start: .9rem; font-size: .75rem; line-height: 1.5; color: var(--fg-3); }
/* ── la colonne de droite ─────────────────────────────────────────────── */
.auth__side { display: grid; gap: 1rem; min-width: 0; }
.auth__blk {
  padding: clamp(1.15rem, .9rem + 1vw, 1.6rem);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
.auth__blkh { font-size: 1rem; font-weight: 680; margin-block-end: .9rem; }
.auth__list { display: grid; gap: .95rem; }
/* L'icone en gouttiere, sur la ligne du titre — pas au-dessus : un bloc
   par-dessus le titre coute une ligne entiere par element. */
.auth__item { display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: .1rem .7rem; }
.auth__ico {
  display: grid; place-items: center;
  width: 2rem; height: 2rem; border-radius: 9px;
  background: var(--accent-soft); color: var(--accent-ink);
}
.auth__ico svg { width: 1.05rem; height: 1.05rem; }
.auth__itx { display: grid; gap: .2rem; min-width: 0; }
.auth__itx b { font-size: .875rem; font-weight: 650; }
.auth__itx span { font-size: .8125rem; line-height: 1.55; color: var(--fg-3); }

.auth__forgot { margin-block: .1rem 1rem; font-size: .8125rem; }
.auth__forgot a { color: var(--accent-ink); font-weight: 560; }

/* ⚠️ Le message d'erreur de FORMULAIRE porte `role="alert"` : il est annonce
   des qu'il apparait. Les erreurs de CHAMP, elles, sont reliees a leur champ
   par `aria-describedby` — annoncees a la prise de focus, pas toutes en bloc. */
.auth__err {
  margin-block-end: 1rem; padding: .6rem .75rem;
  background: color-mix(in oklab, var(--danger) 10%, var(--surface));
  border: 1px solid color-mix(in oklab, var(--danger) 35%, var(--surface));
  border-radius: var(--r);
  font-size: .8125rem; color: var(--danger);
}
.auth__ok {
  display: flex; align-items: flex-start; gap: .5rem;
  margin-block-end: 1rem; padding: .6rem .75rem;
  background: color-mix(in oklab, var(--ok) 9%, var(--surface));
  border: 1px solid color-mix(in oklab, var(--ok) 30%, var(--surface));
  border-radius: var(--r);
  font-size: .8125rem; color: var(--ok);
}
.auth__okico { width: 1rem; height: 1rem; flex: none; margin-block-start: .1rem; }

/* ── un champ ─────────────────────────────────────────────────────────── */
.fld { margin-block-end: .9rem; }
/* ⚠️ Un vrai <label> au-dessus, jamais un texte a l'interieur du champ. Un
   libelle qui disparait des qu'on tape oblige a se souvenir de ce qu'on
   remplit — et il n'existe pas pour un lecteur d'ecran. */
.fld__l {
  display: block; margin-block-end: .3rem;
  font-size: .8125rem; font-weight: 620; color: var(--fg-2);
}
.fld__wrap { position: relative; display: block; }
.fld__i {
  width: 100%;
  /* 48 px : la cible tactile. Et 16 px de corps — en dessous, iOS ZOOME sur le
     champ a la prise de focus, et la page ne revient jamais a sa taille. */
  min-height: 3rem; padding: .6rem .8rem;
  font-size: 1rem;
  color: var(--fg);
  background: var(--surface-2);
  border: 1.5px solid var(--border-2);
  border-radius: var(--r);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.fld__i:hover { border-color: var(--brand-line); }
.fld__i:focus {
  outline: 0; background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.fld.is-bad .fld__i { border-color: var(--danger); }
.fld.is-bad .fld__i:focus { box-shadow: 0 0 0 3px color-mix(in oklab, var(--danger) 18%, transparent); }
.fld__h { display: block; margin-block-start: .3rem; font-size: .75rem; color: var(--fg-3); }
.fld__e { display: block; margin-block-start: .3rem; font-size: .75rem; color: var(--danger); }
.fld__caps { display: block; margin-block-start: .3rem; font-size: .75rem; color: var(--warn); }
.fld__caps[hidden] { display: none; }

/* Montrer le mot de passe : sur la moitie droite du champ, jamais par-dessus
   le texte — le champ reserve la place par son remplissage. */
.fld__wrap:has(.fld__eye:not([hidden])) .fld__i { padding-inline-end: 4.25rem; }
.fld__eye {
  position: absolute; inset-inline-end: .35rem; top: 50%; translate: 0 -50%;
  padding: .35rem .55rem; border-radius: var(--r-sm);
  font-size: .75rem; font-weight: 650; color: var(--fg-3);
  background: transparent; border: 0; cursor: pointer;
}
.fld__eye:hover { color: var(--accent-ink); background: var(--surface-3); }
.fld__eye:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
.fld__eye[hidden] { display: none; }

/* ── la jauge de robustesse ───────────────────────────────────────────────
   ⚠️ C'est un INDICE, pas une regle. Le serveur n'exige qu'une longueur : une
   jauge qui bloque l'envoi pousse a « Motdepasse1! », que toutes les listes
   contiennent, et refuse quatre mots au hasard que personne ne devine. */
.pwm { display: flex; align-items: center; gap: .6rem; margin-block: -.35rem .9rem; }
.pwm[hidden] { display: none; }
.pwm__bar {
  flex: 1 1 auto; height: 4px; border-radius: 999px;
  background: var(--surface-3); overflow: hidden;
}
.pwm__fill {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: var(--danger);
  transition: width .18s ease, background-color .18s ease;
}
.pwm.is-2 .pwm__fill { width: 55%; background: var(--warn); }
.pwm.is-3 .pwm__fill { width: 78%; background: var(--accent); }
.pwm.is-4 .pwm__fill { width: 100%; background: var(--ok); }
.pwm.is-1 .pwm__fill { width: 28%; }
.pwok { margin-block: -.5rem .9rem; font-size: .75rem; font-weight: 560; }
.pwok[hidden] { display: none; }
.pwok.is-ok { color: var(--ok); }
.pwok.is-no { color: var(--fg-3); }
/* Le widget anti-robot pose son propre iframe : on ne lui impose qu'une marge
   et une hauteur reservee — sans elle, le bouton saute quand il finit de se
   rendre, et on clique sur autre chose que ce qu'on visait. */
.cf-turnstile { margin-block-end: .9rem; min-height: 65px; }

.pwm__tx { flex: none; font-size: .6875rem; font-weight: 620; color: var(--fg-3); }

/* L'adresse connectee, dans la bande sombre. */
.top__me {
  max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .8125rem; color: rgba(255, 255, 255, .72);
}
.top__out { display: contents; }

/* ── 25. les pages de référencement ────────────────────────────────────────
   ⚠️ Aucune nouveauté de charte ici : même carte, mêmes rayons, mêmes jetons
      que le reste du site. Une page d'atterrissage qui ne ressemble pas au site
      se lit comme une page satellite — et c'est exactement ce qu'on ne veut pas
      donner à lire, ni à un visiteur ni à un moteur.
   ────────────────────────────────────────────────────────────────────────── */
.seohead { margin-block-end: 1.1rem; }
.seohead__h1 {
  font-size: clamp(1.5rem, 1.15rem + 1.5vw, 2.25rem);
  line-height: 1.12; letter-spacing: -.03em; text-wrap: balance;
}
.seohead__p {
  margin-block-start: .6rem; max-width: 52rem;
  font-size: clamp(.9375rem, .9rem + .18vw, 1.0313rem);
  line-height: 1.55; color: var(--fg-2); text-wrap: pretty;
}
/* Les chiffres de la page : la même frise que l'accroche, en clair. */
.seofacts {
  display: grid; gap: 1px; margin-block-start: 1.1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  background: var(--border);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden;
}
@media (min-width: 48rem) { .seofacts { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.seofacts .fig { background: var(--surface); }
.seofacts .fig dd { color: var(--fg); }
.seofacts .fig dt { color: var(--fg-3); }

/* Le tableau de données — celui qui justifie la page. */
.seorows { padding-block: 0; }
.seotab__cap {
  caption-side: top; text-align: start;
  padding-block-end: .7rem;
  font-size: .8125rem; line-height: 1.5; color: var(--fg-3);
}
/* La note sous le tableau : elle explique la colonne « Network 63 ». Meme
   registre visuel que la légende — c'est la même voix, de l'autre côté du
   tableau. */
/* La legende des tableaux de reference (/docs, /about, /resellers). Meme
   registre que `.seotab__cap` : c'est la meme voix, sur un autre gabarit. */
.dtab__cap {
  caption-side: top; text-align: start;
  padding-block-end: .6rem;
  font-size: .8125rem; line-height: 1.5; color: var(--fg-3);
}

.seotab__note {
  margin-block-start: .7rem;
  font-size: .8125rem; line-height: 1.55; color: var(--fg-3);
}

/* ⚠️ `min-width: 0` sur la cellule qui porte le nom : sans lui, un nom long
   pousse le tableau au-delà de la colonne au lieu de se couper, et c'est le
   défilement horizontal de `.tblx` qui masque le débordement. */
.seotab th[scope="row"] { min-width: 0; }
.seotab__a { display: inline-flex; align-items: center; gap: .5rem; min-width: 0; color: inherit; }
.seotab__a:hover .seotab__n { color: var(--accent-ink); text-decoration: underline; }
.seotab__n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 620; }
.seotab__pfx { flex: none; font-size: .75rem; color: var(--fg-3); font-variant-numeric: tabular-nums; }
.seotab .rate { font-size: .8125rem; font-weight: 620; }
.rate.is-none { color: var(--fg-3); font-weight: 500; font-size: .75rem; }

/* Les index : des tuiles, pas un annuaire. */
.hub {
  display: grid; gap: .5rem; margin-block-start: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

/* 🔴 724 TUILES, DONC 704 <img> ET 51 644 px DE DOCUMENT SUR /receive-sms.
   Mesure du 23/08/2026 : TBT a 291 ms, contre un seuil de 200 — le seul
   depassement de signal web essentiel du site. Ce n'est pas le poids
   (214 Ko) qui coute, c'est la mise en page de sept cents vignettes dont
   quatre-vingt-dix-neuf pour cent sont sous la ligne de flottaison.

   `content-visibility: auto` fait sauter la mise en page ET la peinture de ce
   qui est hors de l'ecran, et le navigateur les fait au defilement. Le contenu
   reste DANS le document : il est indexe, trouvable par la recherche du
   navigateur, et atteignable par une ancre. Ce n'est pas `display: none`.

   ⚠️ `contain-intrinsic-size` EST LA MOITIE QUI COMPTE. Sans elle, chaque
      tuile hors ecran est supposee de hauteur nulle, la barre de defilement
      ment, et tout saute au fur et a mesure qu'on descend. 60 px est la
      hauteur MESUREE d'une tuile — identique a 390 px et a 1 280 px de large,
      ce qui est exactement le cas ou cette technique ne coute aucun decalage.
      Le mot-cle `auto` fait retenir au navigateur la taille reelle une fois
      qu'il l'a vue.
   ⚠️ Sous-estimer cette valeur fait mentir un audit de contraste : les
      descendants declarent alors des rectangles qui ne sont pas les vrais. */
.hub > li {
  content-visibility: auto;
  contain-intrinsic-size: auto 60px;
}
.hub__a {
  display: flex; align-items: center; gap: .6rem;
  min-height: 3.5rem; padding: .55rem .75rem;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r);
  color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.hub__a:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); }
.hub__tx { display: grid; gap: .1rem; min-width: 0; flex: 1 1 auto; }
.hub__n { font-size: .875rem; font-weight: 640; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub__c { font-size: .6875rem; color: var(--fg-3); }
.hub__p { flex: none; font-size: .875rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Le maillage interne : des liens qu'on lit, pas un pied de page déguisé. */
.sibs { padding-block: 0; margin-block-start: 1.5rem; }
.sibs__h { font-size: 1rem; font-weight: 680; margin-block-end: .7rem; }
.sibs__l { display: flex; flex-wrap: wrap; gap: .4rem; }
.sibs__a {
  display: inline-block; padding: .35rem .7rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  font-size: .8125rem; color: var(--fg-2);
}
.sibs__a:hover { border-color: var(--brand-line); color: var(--accent-ink); }
.sibs__more { margin-block-start: .8rem; }

/* Le moins cher, en vignettes — la première question devant une liste de 45. */
.cheap { padding-block: 0; margin-block-start: 1.5rem; }
.cheap__h { font-size: 1rem; font-weight: 680; margin-block-end: .7rem; }
.cheap__l {
  display: grid; gap: .5rem;
  grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
}
.cheap__a {
  display: flex; align-items: center; gap: .55rem;
  padding: .55rem .7rem; min-height: 3.25rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.cheap__a:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); }
.cheap__tx { display: grid; gap: .1rem; min-width: 0; flex: 1 1 auto; }
.cheap__n { font-size: .875rem; font-weight: 640; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cheap__s { font-size: .6875rem; color: var(--fg-3); display: flex; align-items: center; gap: .3rem; }
.cheap__p { flex: none; font-size: .9375rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Le paragraphe de fin : lisible, pas un mur. */
.prose { margin-block-start: 1.5rem; }
.prose__p {
  max-width: 58rem; margin-block-start: .8rem;
  font-size: .9375rem; line-height: 1.65; color: var(--fg-2); text-wrap: pretty;
}

/* ── 26. le solde, dans la bande sombre ────────────────────────────────────
   🔴 C'est un LIEN vers la recharge, pas un affichage : le premier chiffre
      qu'on vient chercher en revenant, et la seule chose qu'on veut en faire
      quand il est bas est le remonter.
   ⚠️ `tabular-nums` et une largeur minimale : sans elles, toute la barre se
      décale d'un pixel à chaque centime dépensé.
   ────────────────────────────────────────────────────────────────────────── */
/* 🔴 PLUS GROS QUE LE RESTE DE LA BARRE, et c'est voulu. Le solde et le compte
   sont les deux choses qu'on vient chercher en revenant sur le site ; le
   bouton de theme et le lien d'action, non. A 13 px ils se lisaient comme des
   mentions légales à côté d'un bouton orange. La bande fait 3,5 rem de haut :
   il y a la place. */
.top__bal {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .4rem .8rem .4rem .62rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, .10);
  border: 1px solid rgba(255, 255, 255, .16);
  color: #fff;
  font-size: .9375rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background-color .15s ease, border-color .15s ease;
}
.top__bal:hover { background: rgba(255, 255, 255, .18); border-color: rgba(255, 255, 255, .3); }
.top__bal:focus-visible { outline: 2px solid #a8c7fa; outline-offset: 2px; }
.top__balico { width: 1.1rem; height: 1.1rem; flex: none; color: #a8c7fa; }
/* 🔴 AUCUNE largeur minimale. C'est `tabular-nums` qui empêche la barre de
   danser — pas une largeur plancher. Mesuré : « $0.00 » et « $9.99 » font
   exactement 40,5 px tous les deux, donc dépenser des centimes ne décale rien.
   La largeur minimale de 3,6 rem ne couvrait que le changement de NOMBRE DE
   CHIFFRES ($9.99 → $10.00), et le payait par 17 px de vide permanent à droite
   du montant à toute valeur plus courte — d'abord à gauche avec
   `text-align: end`, puis à droite une fois aligné au départ. On déplace le
   vide, on ne le supprime pas : il faut retirer le plancher.
   La pastille change donc de largeur quand on passe de 9 à 10 dollars. C'est
   rare, c'est un vrai changement, et ça coûte moins qu'un trou permanent. */
.top__balv { text-align: start; }
.mnav__bal { justify-content: center; gap: .4rem; }
.mnav__bal svg { width: 1rem; height: 1rem; }

/* 🔴 `.wamt` / `.wcoin`, pas `.amt` / `.coin`. `.coin` EXISTE DEJA : c'est le
   SVG des logos de monnaie du pied de page, en 14 px de cote (icons.php). Mes
   pastilles en heritaient donc une largeur de 14 px et le nom de la monnaie
   debordait dessous — une collision de nom de classe, muette, que rien ne
   signale : les deux regles s'appliquent, la derniere gagne au cas par cas. */
.wamt, .wcoin {
  position: relative; cursor: pointer;
  border: 1.5px solid var(--border-2); border-radius: var(--r);
  background: var(--surface-2);
  transition: border-color .15s ease, background-color .15s ease;
}
/* ⚠️ Le bouton radio est masqué visuellement mais TOUJOURS dans le document :
   `display:none` le retirerait de la navigation au clavier et le libellé ne
   serait plus sélectionnable qu'à la souris. */
.wamt input, .wcoin input { position: absolute; opacity: 0; width: 100%; height: 100%; inset: 0; margin: 0; cursor: pointer; }
.wamt { padding: .5rem .85rem; font-weight: 680; font-variant-numeric: tabular-nums; }
.wcoin { padding: .45rem .7rem; display: grid; gap: .05rem; min-width: 5.25rem; }
.wcoin__t { font-size: .8125rem; font-weight: 700; }
.wcoin__n { font-size: .6875rem; color: var(--fg-3); }
.wamt:hover, .wcoin:hover { border-color: var(--brand-line); }
.wamt:has(input:checked), .wcoin:has(input:checked) {
  border-color: var(--accent); background: var(--accent-soft);
}
.amt:has(input:checked) .amt__v, .coin:has(input:checked) .wcoin__t { color: var(--accent-ink); }
.wamt:has(input:focus-visible), .wcoin:has(input:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
/* ── 28. le menu de compte, dans la bande sombre ───────────────────────────
   🔴 L'adresse mail a quitté la barre : douze caractères de large pour une
      information dont on n'a besoin qu'une fois, affichée à côté de quelqu'un
      dans un train. Une icône, un numéro, et les quatre choses qu'on vient y
      chercher.
   ⚠️ `<details>` — le seul menu qui s'ouvre sans script, et la CSP interdit
      tout script en ligne.
   ────────────────────────────────────────────────────────────────────────── */
.acct { position: relative; display: none; }
@media (min-width: 68rem) { .acct { display: block; } }
.acct__btn { list-style: none; }
.acct__btn::-webkit-details-marker { display: none; }
.acct__btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .25rem .45rem .25rem .3rem;
  border-radius: 999px; cursor: pointer;
  color: var(--top-fg); font-size: .8125rem; font-weight: 600;
  transition: background-color .15s ease;
}
.acct__btn:hover { background: rgba(255, 255, 255, .10); }
.acct__btn:focus-visible { outline: 2px solid #a8c7fa; outline-offset: 2px; }
.acct[open] .acct__btn { background: rgba(255, 255, 255, .14); color: #fff; }
.acct__ava {
  display: grid; place-items: center; flex: none;
  width: 2.1rem; height: 2.1rem; border-radius: 999px;
  background: rgba(255, 255, 255, .14); color: #fff;
}
.acct__ava svg { width: 1.2rem; height: 1.2rem; }
/* Le numéro de compte a quitté la barre (voir header.php) : le bouton n'est
   plus qu'une pastille et un chevron, donc il se resserre. */
.acct__btn { gap: .2rem; padding-inline-end: .3rem; }
.acct__chev { width: .95rem; height: .95rem; opacity: .75; transition: rotate .18s ease; }
.acct[open] .acct__chev { rotate: 180deg; }

/* ⚠️ Ancré à DROITE (`inset-inline-end: 0`) : ouvert vers la gauche depuis le
   bord droit de la fenêtre, un menu ancré à gauche sort de l'écran. */
.acct__menu {
  position: absolute; z-index: 60;
  inset-inline-end: 0; top: calc(100% + .5rem);
  min-width: 15rem; padding: .35rem;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
}
@media (prefers-reduced-motion: no-preference) {
  .acct[open] .acct__menu { animation: dd-in .14s ease-out; }
}
.acct__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
  padding: .5rem .6rem .55rem;
  border-block-end: 1px solid var(--border); margin-block-end: .3rem;
}
.acct__headk { font-size: .6875rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-3); }
.acct__headv { font-size: .875rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--fg); }
.acct__i {
  display: flex; align-items: center; gap: .55rem; width: 100%;
  padding: .5rem .6rem; border-radius: var(--r-sm);
  font-size: .8125rem; font-weight: 560; color: var(--fg-2);
  background: none; border: 0; cursor: pointer; font-family: inherit; text-align: start;
}
.acct__i svg { width: 1rem; height: 1rem; flex: none; color: var(--fg-3); }
.acct__i:hover { background: var(--surface-2); color: var(--fg); }
.acct__i:hover svg { color: var(--accent); }
.acct__i:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.acct__ib { margin-inline-start: auto; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--fg); }
.acct__i--out { color: var(--danger); }
.acct__i--out svg { color: var(--danger); }
.acct__i--out:hover { background: color-mix(in oklab, var(--danger) 8%, var(--surface)); color: var(--danger); }

/* ── 29. la recharge, refaite ──────────────────────────────────────────────
   🔴 La première version : deux colonnes déséquilibrées, un grand vide sous la
      carte du solde, et un avertissement orange qui dominait la page comme une
      erreur. Ici : une colonne de gauche qui répond « combien me reste-t-il »
      et « ça me fait combien de codes », une colonne de droite en TROIS ÉTAPES
      NUMÉROTÉES — la même grammaire que le rail et que la barre de commande.
   ────────────────────────────────────────────────────────────────────────── */
.wal {
  display: grid; gap: 1rem; margin-block-end: 1.25rem;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 62rem) { .wal { grid-template-columns: 19rem minmax(0, 1fr); } }
.wal__side { display: grid; gap: .7rem; }

.wal__bal {
  padding: 1.35rem 1.35rem 1.45rem;
  background: var(--ink); color: #fff;
  border-radius: var(--r-lg); box-shadow: var(--shadow-3);
}
.wal__k {
  font-size: .6875rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: rgba(255, 255, 255, .74);
}
.wal__v {
  margin-block: .3rem 0;
  font-size: clamp(2.25rem, 1.8rem + 1.8vw, 3rem); font-weight: 780;
  letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums;
}
.wal__v--sm { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); }
/* Le repère « ça fait combien de codes » : la question qu'on calcule de tête
   sinon, et la seule qui rende un solde concret. */
.wal__eq {
  display: flex; gap: .5rem; align-items: flex-start;
  margin-block-start: 1rem; padding-block-start: .9rem;
  border-block-start: 1px solid rgba(255, 255, 255, .16);
  font-size: .75rem; line-height: 1.5; color: rgba(255, 255, 255, .82);
}
.wal__eqico { width: .95rem; height: .95rem; flex: none; margin-block-start: .1rem; color: #a8c7fa; }
.wal__note {
  display: flex; gap: .5rem; align-items: flex-start;
  padding: .7rem .85rem; border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: .75rem; line-height: 1.5; color: var(--fg-3);
}
.wal__noteico { width: .95rem; height: .95rem; flex: none; margin-block-start: .1rem; color: var(--accent); }

.wal__main {
  padding: clamp(1.15rem, .9rem + 1vw, 1.6rem);
  background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow-2);
}
/* Les étapes numérotées : la grammaire du site, reprise telle quelle. */
.wstep { padding-block-end: 1.15rem; margin-block-end: 1.15rem; border-block-end: 1px solid var(--border); }
.wstep--last { padding-block-end: 0; margin-block-end: 0; border-block-end: 0; }
.wstep__h {
  display: flex; align-items: center; gap: .55rem;
  font-size: .9375rem; font-weight: 680; margin-block-end: .8rem;
}
.wstep__n {
  display: grid; place-items: center; flex: none;
  width: 1.4rem; height: 1.4rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: .75rem; font-weight: 800;
}
.wstep__n svg { width: .85rem; height: .85rem; }
.wal__p { font-size: .875rem; line-height: 1.6; color: var(--fg-3); }

.wal__free { margin-block-start: .8rem; }
.wal__free[hidden] { display: none; }
.wal__freein { position: relative; display: block; }
.wal__cur {
  position: absolute; inset-inline-start: .8rem; top: 50%; translate: 0 -50%;
  color: var(--fg-3); font-weight: 620; pointer-events: none;
}
.wal__freein .fld__i { padding-inline-start: 1.9rem; max-width: 12rem; }

/* ⚠️ Le conteneur porte le `flex`, pas les pastilles. En le retirant par
   inadvertance lors d'un nettoyage, les cinq monnaies se sont empilées sur
   toute la largeur — chacune était pourtant réglée correctement. */
.wal__amts, .wal__coins { display: flex; flex-wrap: wrap; gap: .5rem; }

/* Les pastilles de monnaie portent le VRAI logo — le même que le pied de page. */
.wcoin { display: flex; align-items: center; gap: .5rem; }
.wcoin__ico {
  display: grid; place-items: center; flex: none;
  width: 1.6rem; height: 1.6rem; border-radius: 999px;
  background: var(--surface-3); color: var(--fg-2);
}
.wcoin__ico .coin { width: .95rem; height: .95rem; }
.wcoin:has(input:checked) .wcoin__ico { background: var(--accent); color: #fff; }
.wcoin__tx { display: grid; gap: .05rem; }

/* Le récapitulatif : ce qu'on ajoute, ce que le solde devient. */
.wsum {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .45rem;
  padding: .85rem 1rem; margin-block-end: .9rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r);
  font-size: .8125rem; color: var(--fg-3);
}
.wsum__a, .wsum__b {
  font-size: 1.0625rem; font-weight: 760; color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.wsum__b { color: var(--accent-ink); }
.wsum__ar { display: inline-flex; color: var(--fg-3); }
.wsum__ar svg { width: 1rem; height: 1rem; }

/* ⚠️ Ce qui manque se dit sur un ton NEUTRE, pas en orange d'alerte : ce n'est
   pas une erreur du visiteur, et une page qui crie décourage de revenir. */
.wal__soon {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .75rem .9rem; border-radius: var(--r);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  font-size: .8125rem; line-height: 1.55; color: var(--accent-ink);
}
.wal__soonico { width: 1rem; height: 1rem; flex: none; margin-block-start: .15rem; }
.wal__hist { margin-block-start: 1.5rem; }
.wal__in { color: var(--ok); }
.wal__empty {
  padding: 1.5rem .5rem; text-align: center; color: var(--fg-3); font-size: .8125rem;
}
.wal__emptyico { width: 1rem; height: 1rem; vertical-align: -.15rem; margin-inline-end: .4rem; color: var(--fg-3); }

/* ── 30. les logos officiels des monnaies ──────────────────────────────────
   🔴 Ce sont des `<img>`, pas du SVG en ligne : chaque dessin porte SA couleur
      de marque (l'orange de Bitcoin, le vert de Tether). Repeints en
      `currentColor` ils deviendraient huit disques identiques, ce qui est
      exactement l'inverse de ce à quoi sert un logo sur un écran de paiement.
   ⚠️ Donc AUCUNE règle `fill` ou `color` ne les atteint : la taille, et rien
      d'autre.
   -------------------------------------------------------------------------- */

.coin { width: .875rem; height: .875rem; display: inline-block; vertical-align: -.13em; }

/* La pastille de choix : le logo à sa vraie taille, sans fond derrière. Le
   cercle gris de la version précédente était là pour porter un glyphe
   monochrome ; sous un disque de marque, il ne fait plus qu'un halo sale. */
.wcoin__ico { display: grid; place-items: center; flex: none; width: 1.75rem; height: 1.75rem; }
.wcoin__img { width: 1.75rem; height: 1.75rem; display: block; }
.wcoin__net { font-variant-numeric: tabular-nums; }
/* ⚠️ Deux lignes de texte à droite du logo : le nom, puis le sigle et la
   chaîne. Sur les deux Tether, c'est la SECONDE ligne qui les distingue —
   elle ne peut donc pas être plus discrète que le reste au point de ne pas
   se lire. */
.wcoin__n { font-size: .6875rem; color: var(--fg-2); letter-spacing: .01em; }
.wcoin { min-width: 9.5rem; padding: .5rem .8rem; }

/* ── 31. l'écran de dépôt ──────────────────────────────────────────────────
   🔴 La seule page du site où une erreur coûte de l'argent réel. Tout y est
      réglé pour qu'on ne puisse PAS se tromper de champ : le montant exact en
      premier et en gros, l'adresse en dessous coupée sur plusieurs lignes
      (jamais tronquée par des points de suspension — on ne recopie pas ce
      qu'on ne voit pas), et le réseau dans un encadré de la couleur d'une
      alerte, parce qu'une mention discrète ne se lit pas.
   -------------------------------------------------------------------------- */

.pay { display: grid; gap: 1rem; }

/* Le bandeau d'état : la première chose lue, et la seule qui change toute
   seule. `--tone` porte la couleur, une seule règle par ton. */
.pay__state {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .9rem 1rem; border-radius: var(--r-md);
  border: 1px solid var(--tone-line); background: var(--tone-soft);
}
.pay__state--ok   { --tone-line: var(--accent-line); --tone-soft: var(--accent-soft); --tone-ink: var(--accent-ink); }
.pay__state--good { --tone-line: color-mix(in oklab, var(--ok) 34%, var(--surface));     --tone-soft: color-mix(in oklab, var(--ok) 10%, var(--surface));     --tone-ink: var(--ok); }
.pay__state--warn {   --tone-line: color-mix(in oklab, var(--warn) 34%, var(--surface));   --tone-soft: var(--warn-soft);   --tone-ink: var(--warn); }
.pay__state--bad  {    --tone-line: color-mix(in oklab, var(--danger) 34%, var(--surface));    --tone-soft: color-mix(in oklab, var(--danger) 10%, var(--surface));    --tone-ink: var(--danger); }
.pay__state--off  {   --tone-line: var(--border);      --tone-soft: var(--surface-2);   --tone-ink: var(--fg-2); }
.pay__sico { display: inline-flex; flex: none; color: var(--tone-ink); margin-block-start: .05rem; }
.pay__sico svg { width: 1.15rem; height: 1.15rem; }
.pay__stx { display: grid; gap: .15rem; min-width: 0; }
.pay__sh  { font-size: .9375rem; font-weight: 720; color: var(--tone-ink); }
.pay__sb  { font-size: .8125rem; line-height: 1.55; color: var(--fg-2); }

.pay__head { display: grid; gap: .3rem; }
.pay__h1 {
  font-size: clamp(1.25rem, 1.05rem + .9vw, 1.6rem); font-weight: 760;
  letter-spacing: -.015em; color: var(--fg);
}
.pay__lede { font-size: .875rem; color: var(--fg-2); }

.pay__grid { display: grid; gap: 1rem; align-items: start; }
@media (min-width: 52rem) { .pay__grid { grid-template-columns: minmax(0, 1fr) auto; } }
.pay__fields { display: grid; gap: .8rem; min-width: 0; }

.pfield {
  display: grid; gap: .35rem; min-width: 0;
  padding: .85rem 1rem; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface-2);
}
.pfield--amt { border-color: var(--accent-line); background: var(--accent-soft); }
.pfield__k {
  font-size: .625rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3);
}
.pfield__v { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; min-width: 0; }
.pfield__ico { display: inline-flex; flex: none; }
.pfield__coin { width: 1.5rem; height: 1.5rem; display: block; }
/* 🔴 `anywhere`, pas `break-word` : une adresse Bitcoin n'a aucune césure
      naturelle, et `break-word` la laisse déborder du conteneur sans un mot de
      la part de personne — la moitié de l'adresse hors de l'écran, sur la page
      où on la recopie. */
.pfield__code {
  font-family: var(--mono-font);
  font-size: .9375rem; font-weight: 600; color: var(--fg);
  overflow-wrap: anywhere; word-break: break-all; min-width: 0; line-height: 1.45;
}
.pfield--amt .pfield__code { font-size: 1.25rem; font-weight: 700; letter-spacing: -.01em; }
.pfield__code--addr { font-size: .8125rem; flex: 1 1 14rem; }
.pfield__unit { font-size: .875rem; font-weight: 700; color: var(--fg-2); }
.pfield__h { font-size: .75rem; line-height: 1.5; color: var(--fg-2); }

/* Le bouton copier. ⚠️ Il garde sa largeur quand le libellé passe à « Copied »
   (`min-width`) : sinon la ligne entière bouge sous le doigt au moment du clic,
   ce qui donne l'impression d'avoir cliqué à côté. */
.cpy {
  display: inline-flex; align-items: center; gap: .3rem; flex: none;
  padding: .32rem .6rem; min-width: 5.25rem; justify-content: center;
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--fg-2);
  font: inherit; font-size: .75rem; font-weight: 640; cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.cpy:hover { border-color: var(--brand-line); color: var(--fg); }
.cpy:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.cpy svg { width: .9rem; height: .9rem; }
.cpy.is-done { border-color: color-mix(in oklab, var(--ok) 34%, var(--surface)); background: color-mix(in oklab, var(--ok) 10%, var(--surface)); color: var(--ok); }

/* Le réseau. Encadré d'alerte, pas une note de bas de page : un Tether envoyé
   sur la mauvaise chaîne est perdu, et c'est l'erreur la plus fréquente. */
.pnet {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .7rem .9rem; border-radius: var(--r);
  border: 1px solid color-mix(in oklab, var(--warn) 34%, var(--surface)); background: var(--warn-soft);
  font-size: .8125rem; line-height: 1.55; color: var(--warn);
}
.pnet__ico { display: inline-flex; flex: none; margin-block-start: .1rem; }
.pnet__ico svg { width: 1rem; height: 1rem; }

.pay__qr { display: grid; gap: .6rem; justify-items: center; align-content: start; }
/* Fond BLANC en dur, pas `--surface` : en thème sombre, un QR noir sur fond
   sombre ne se scanne pas. La marge blanche autour fait partie du code. */
.pay__qrimg {
  display: block; width: 13.75rem; height: 13.75rem;
  padding: .6rem; border-radius: var(--r-md);
  background: #fff; border: 1px solid var(--border);
}
/* Le délai, en pastille sous le titre. Il qualifie l'ordre entier — pas le QR. */
/* ⚠️ `justify-self`, pas `align-self` : `.pay__head` est une GRILLE, donc
   l'étirement à corriger est celui de l'axe en ligne. Avec `align-self` la
   pastille s'étendait sur toute la largeur et ne se lisait plus comme une
   pastille — juste comme une quatrième ligne de texte encadrée. */
.pay__left {
  display: inline-flex; align-items: center; gap: .35rem; justify-self: start;
  margin-block-start: .15rem;
  padding: .28rem .6rem .28rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface-2);
  font-size: .8125rem; color: var(--fg-2);
}
.pay__leftico { width: .95rem; height: .95rem; color: var(--fg-3); }
.pay__leftk { color: var(--fg-3); }
.pay__lefts { font-variant-numeric: tabular-nums; font-weight: 720; color: var(--fg); }
.pay__lefts.is-low { color: var(--warn); }

.pay__acts { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.pay__cancel { margin-inline-start: auto; }
/* Un vrai bouton, déguisé en lien : l'annulation est un POST, et un `<a>` qui
   détruit quelque chose part tout seul au premier pré-chargement. */
.paylnk {
  padding: .3rem .1rem; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: .78125rem; color: var(--fg-3);
  text-decoration: underline; text-underline-offset: .18em;
}
.paylnk:hover { color: var(--danger); }
.paylnk:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--r-sm); }

/* ── 32. la recharge : rappel, refus, bouton ───────────────────────────────
   -------------------------------------------------------------------------- */

/* La commande encore ouverte, rappelée EN TÊTE. Placée en bas, elle serait lue
   après avoir re-choisi un montant — c'est-à-dire trop tard. */
.wopen {
  display: flex; flex-wrap: wrap; gap: .7rem; align-items: center;
  padding: .8rem 1rem; margin-block-end: 1rem; border-radius: var(--r-md);
  border: 1px solid var(--accent-line); background: var(--accent-soft);
}
.wopen__ico { display: inline-flex; flex: none; color: var(--accent-ink); }
.wopen__ico svg { width: 1.1rem; height: 1.1rem; }
.wopen__tx { display: grid; gap: .1rem; min-width: 0; flex: 1 1 16rem; }
.wopen__h { font-size: .875rem; font-weight: 720; color: var(--accent-ink); }
.wopen__b { font-size: .8125rem; color: var(--fg-2); }
.wopen__a { flex: none; }
@media (max-width: 40rem) {
  /* Pleine largeur et 44 px : ce bouton est ce qui évite d'ouvrir une seconde
     commande pour la même intention. Mesuré à 65 x 32 px avant. */
  .wopen__a { flex: 1 1 100%; min-height: 2.75rem; }
}

/* Le refus du formulaire. Rouge, contrairement à `.wal__soon` : ici c'est bien
   quelque chose qui n'est pas passé, et le taire ferait renvoyer le même
   formulaire à l'identique. */
.werr {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .75rem .9rem; margin-block-end: 1rem; border-radius: var(--r);
  border: 1px solid color-mix(in oklab, var(--danger) 34%, var(--surface)); background: color-mix(in oklab, var(--danger) 10%, var(--surface));
  font-size: .8125rem; line-height: 1.55; color: var(--danger);
}
.werr__ico { width: 1rem; height: 1rem; flex: none; margin-block-start: .15rem; }

.wal__min { margin-block-start: .5rem; font-size: .75rem; color: var(--fg-3); }
.wgo { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .9rem; }
.wgo__b { flex: none; }
.wgo__s { font-size: .75rem; line-height: 1.5; color: var(--fg-3); flex: 1 1 12rem; }

/* ── 33. l'écran de dépôt sur téléphone ────────────────────────────────────
   🔴 C'est LÀ qu'on paie. Le portefeuille est sur le même appareil, et le geste
      réel est : scanner, ou copier-coller. Les deux étaient mal servis — le QR
      arrivait sous trois cartes, et « Copier » était une pastille de 12 px
      collée à droite d'une adresse qu'elle écrasait. Ici : le QR d'abord, et
      des boutons de copie pleine largeur qu'on ne peut pas rater du pouce.
   -------------------------------------------------------------------------- */
@media (max-width: 40rem) {
  /* ⚠️ Sur un téléphone, le bouton prend toute la largeur : c'est la seule
     action de la page, et une cible de 8 rem au milieu d'un écran de 20 se
     rate. */
  .wgo__b { flex: 1 1 100%; min-height: 2.75rem; }

  /* 🔴 DES GRILLES, PAS DU `flex-wrap`. Mesuré à 390 px : les paliers faisaient
     79, 79, 88, 88 et 65 px — cinq largeurs différentes sur deux rangées
     inégales, chacune dimensionnée par la longueur de son nombre. Une grille
     leur donne la même largeur, et « Other » prend la dernière rangée entière
     plutôt que de flotter à côté d'un vide.
     Et 44 px de haut : les pastilles en faisaient 41, sous le seuil où une
     cible se rate au pouce. */
  .wal__amts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
  .wamt { display: grid; place-items: center; min-height: 2.75rem; padding-inline: .5rem; }
  .wamt--free { grid-column: 1 / -1; }

  /* Les pastilles de monnaie laissaient 13 px de vide au bout de chaque
     rangée : `min-width: 9.5rem` les fige à 152 px et `flex` ne les étire pas.
     En grille, deux colonnes égales remplissent la largeur exactement. */
  .wal__coins { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
  .wcoin { min-width: 0; }
  /* ⚠️ `min-width: 0` sur l'élément de grille ET sur le texte : sans le second,
     « Tether · TRC-20 » pousse la colonne au-delà de sa part. */
  .wcoin__tx { min-width: 0; }
  .wcoin__t, .wcoin__n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Le récapitulatif tenait sur trois lignes bancales (89 px de haut pour une
     phrase). Deux lignes alignées, la flèche retirée : elle ne relie plus rien
     une fois que ça passe à la ligne. */
  .wsum { display: grid; grid-template-columns: auto 1fr; gap: .3rem .6rem; align-items: baseline; }
  .wsum__ar { display: none; }
  .wsum__a, .wsum__b { justify-self: end; }
  /* ⚠️ La copie est UNE phrase (« You add … → balance becomes … ») : elle se
     lit ainsi sur grand écran. Coupée en deux rangées, sa seconde moitié
     devient un intitulé et une minuscule en tête se remarque. On le corrige en
     CSS plutôt qu'en dupliquant la chaîne — deux libellés à maintenir pour une
     majuscule, c'est un de trop. */
  .wsum__l::first-letter { text-transform: uppercase; }

  /* Le QR passe AVANT les champs. `order` sur les enfants directs de la grille :
     l'aside est après dans le document (c'est l'ordre juste au clavier et au
     lecteur d'écran sur grand écran), il remonte visuellement ici. */
  .pay__grid { grid-template-columns: minmax(0, 1fr); }
  .pay__qr      { order: -1; }
  .pay__qrimg   { width: 12.5rem; height: 12.5rem; }

  /* 🔴 La copie devient un vrai bouton, sous la valeur, sur toute la largeur.
     Côte à côte, elle volait la moitié de la ligne à une adresse de 42
     caractères — celle qu'on est justement venu lire. */
  .pfield__v { flex-direction: column; align-items: stretch; gap: .55rem; }
  .pfield__code--addr { flex: 1 1 auto; font-size: .875rem; }
  .cpy {
    width: 100%; min-height: 2.75rem; min-width: 0;
    font-size: .875rem; border-radius: var(--r);
  }
  .cpy svg { width: 1rem; height: 1rem; }
  /* Le montant garde sa ligne : le chiffre et son sigle vont ensemble, et les
     séparer ferait lire « 50.18 » sans savoir en quoi. */
  .pfield--amt .pfield__v { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .pfield--amt .cpy { flex: 1 1 100%; }

  /* Les actions, empilées et pleine largeur. `Check again` et `Back to balance`
     à 45 % chacune se rataient une fois sur trois. */
  .pay__acts { flex-direction: column; align-items: stretch; }
  .pay__acts .btn { width: 100%; justify-content: center; min-height: 2.75rem; }
  .pay__cancel { margin-inline-start: 0; width: 100%; text-align: center; }
  .paylnk { width: 100%; padding-block: .6rem; }
}

/* ── 34. l'espace client : réglages et clef d'API ──────────────────────────
   🔴 TOUTE LA LARGEUR, EN DEUX COLONNES. Première version : une colonne unique
      plafonnée à 46 rem, qui laissait 204 px de vide à droite dans une colonne
      de 940 — mesuré. Mais étirer cette même colonne à 940 px donnerait des
      champs de mot de passe de 900 px de long, ce qui est un autre défaut : on
      ne remplit pas une largeur en allongeant une ligne, on la remplit en
      posant quelque chose à côté. D'où deux colonnes à partir de 64 rem, et
      `--full` pour les blocs qui gagnent à s'étaler (les tableaux de faits, la
      clef fraîchement émise).
   ⚠️ `align-items: start` : sans lui, le bloc le plus court d'une rangée
      s'étire à la hauteur du plus long et se retrouve avec un fond vide sous
      son dernier bouton.
   -------------------------------------------------------------------------- */

.acc { display: grid; gap: .9rem; align-items: start; }
@media (min-width: 64rem) {
  .acc { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .accblk--full { grid-column: 1 / -1; }
}

.accblk {
  display: grid; gap: .55rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface);
}
/* Le bloc qui n'appelle aucune action : même grammaire, une surface plus
   calme, pour qu'on voie d'un coup d'œil où il y a quelque chose à faire. */
.accblk--quiet { background: var(--surface-2); }
.accblk__h {
  display: flex; align-items: center; gap: .55rem;
  font-size: .9375rem; font-weight: 720; color: var(--fg);
}
.accblk__i {
  display: grid; place-items: center; flex: none;
  width: 1.6rem; height: 1.6rem; border-radius: var(--r-sm);
  background: var(--accent-soft); color: var(--accent-ink);
}
.accblk__i svg { width: .95rem; height: .95rem; }
.accblk__p { font-size: .8125rem; line-height: 1.6; color: var(--fg-2); }
.accblk__p--sm { font-size: .75rem; color: var(--fg-3); }

/* Les faits du compte : intitulé à gauche, valeur à droite, alignées. */
.accfacts { display: grid; gap: .1rem; }
.accfacts__r {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  padding-block: .45rem;
  border-block-start: 1px solid var(--border);
}
.accfacts__r:first-child { border-block-start: 0; }
.accfacts__r dt { font-size: .8125rem; color: var(--fg-3); flex: 1 1 9rem; }
.accfacts__r dd {
  font-size: .875rem; font-weight: 620; color: var(--fg);
  /* ⚠️ `anywhere` : une adresse mail longue n'a pas de césure naturelle et
     déborderait de la colonne sans un mot de la part de personne. */
  overflow-wrap: anywhere; min-width: 0;
}
.accfacts__a { color: var(--accent-ink); font-weight: 700; text-decoration: none; }
.accfacts__a:hover { text-decoration: underline; }
.accfacts__c { font-family: var(--mono-font); font-size: .8125rem; }

.accform { display: grid; gap: .7rem; margin-block-start: .25rem; }
.accform--row { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.accform__b { justify-self: start; }

.acclist { display: grid; gap: .45rem; }
.acclist li {
  display: flex; gap: .5rem; align-items: flex-start;
  font-size: .8125rem; line-height: 1.55; color: var(--fg-2);
}
.acclist__i { width: .95rem; height: .95rem; flex: none; margin-block-start: .2rem; color: var(--ok); }

/* ── la clef, au moment où elle est montrée ────────────────────────────────
   🔴 Ce bloc n'apparaît qu'UNE fois dans la vie d'une clef : on ne garde que
      son empreinte, donc elle ne peut pas être ré-affichée. Il doit donc se
      remarquer — bordure accentuée, avertissement AVANT la clef, bouton de
      copie de la taille d'un vrai bouton.
   -------------------------------------------------------------------------- */
.accblk--fresh { border-color: var(--accent); box-shadow: var(--shadow-1); }
.accblk__warn {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .7rem .9rem; border-radius: var(--r);
  border: 1px solid color-mix(in oklab, var(--warn) 34%, var(--surface));
  background: var(--warn-soft);
  font-size: .8125rem; line-height: 1.55; color: var(--warn);
}
.accblk__warnico { width: 1rem; height: 1rem; flex: none; margin-block-start: .15rem; }

.keybox {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  padding: .8rem .9rem; border-radius: var(--r);
  border: 1px solid var(--border-2); background: var(--surface-2);
}
.keybox__k {
  font-family: var(--mono-font); font-size: .875rem; font-weight: 600;
  color: var(--fg); overflow-wrap: anywhere; word-break: break-all;
  flex: 1 1 16rem; min-width: 0; line-height: 1.5;
}
.keybox--cmd { margin-block-start: .35rem; }
.keybox--cmd .keybox__k { font-size: .75rem; color: var(--fg-2); }

/* Une route de l'API : la méthode, l'adresse, ce qu'elle rend. */
.apiroute {
  display: grid; gap: .25rem;
  padding-block: .6rem;
  border-block-start: 1px solid var(--border);
}
.apiroute__h { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; min-width: 0; }
.apiroute__m {
  flex: none; padding: .12rem .4rem; border-radius: var(--r-sm);
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: .625rem; font-weight: 800; letter-spacing: .05em;
}
.apiroute__u {
  font-family: var(--mono-font); font-size: .8125rem; color: var(--fg);
  overflow-wrap: anywhere; min-width: 0;
}
.apiroute__p { font-size: .78125rem; line-height: 1.55; color: var(--fg-2); }

/* La confirmation d'une action réussie. Verte, contrairement à `.werr` : ce
   n'est pas un avertissement, et la teinte doit le dire avant la phrase. */
.wok {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .75rem .9rem; margin-block-end: 1rem; border-radius: var(--r);
  border: 1px solid color-mix(in oklab, var(--ok) 34%, var(--surface));
  background: color-mix(in oklab, var(--ok) 10%, var(--surface));
  font-size: .8125rem; line-height: 1.55; color: var(--ok);
}
.wok__ico { width: 1rem; height: 1rem; flex: none; margin-block-start: .15rem; }

@media (max-width: 40rem) {
  /* Mêmes règles que partout ailleurs sur téléphone : les actions prennent la
     largeur et 44 px de haut, sinon elles se ratent au pouce. */
  .accblk { padding: .95rem 1rem; }
  .accform__b, .accform--row .btn { width: 100%; min-height: 2.75rem; }
  .accform--row { flex-direction: column; align-items: stretch; }
  .accform--row form { width: 100%; }
  .accfacts__r { flex-direction: column; gap: .1rem; }
  .accfacts__r dt { flex: none; }
}

/* ── 35. la documentation développeur ──────────────────────────────────────
   🔴 REFAITE EN PARTANT DU TÉLÉPHONE. La première version était pensée pour un
      écran large et rétrécie ensuite : sur 390 px, les sections faisaient
      581 px — la page glissait de 219 px vers la droite — et le sommaire
      atterrissait à 7 978 px, c'est-à-dire après les 9 600 px du document.
      Une table des matières qu'on ne rencontre qu'une fois tout lu ne sert à
      rien, et une page de référence qui défile latéralement ne se lit pas.

   🔴 LA CAUSE DU DÉBORDEMENT, parce qu'elle se reproduira : la règle mobile
      `.cpy { width: 100% }`, écrite pour l'écran de dépôt, s'appliquait AUSSI
      aux boutons « copier » des blocs de code. Chacun réclamait 525 px dans une
      barre d'outils, et poussait toute la section. Une classe utilitaire large
      finit toujours par atteindre un contexte qu'on n'avait pas en tête : elle
      est désormais portée aux endroits qui la veulent, pas à tous les `.cpy`.

   ⚠️ Et le piège de grille du parc : un élément de grille a `min-width: auto`,
      donc il refuse de devenir plus étroit que son contenu. Un `<pre>` ou un
      tableau à l'intérieur élargit la colonne au lieu de défiler. Chaque
      conteneur imbriqué porte donc `min-width: 0`.
   -------------------------------------------------------------------------- */

/* ── l'en-tête ─────────────────────────────────────────────────────────── */
.dhero {
  display: grid; gap: 1.1rem;
  padding: clamp(1.1rem, .8rem + 1.4vw, 1.75rem);
  /* ⚠️ Pas de `margin-block-end` : `.col` est une grille avec `gap: 1.5rem`, et
     une marge par-dessus s'y AJOUTE. C'est la même erreur que sur la rangée de
     pastilles — 44 px là où le reste du site en met 24. */
  border-radius: var(--r-lg);
  background: var(--ink);
  color: #fff;
  min-width: 0;
}
@media (min-width: 56rem) {
  .dhero { grid-template-columns: minmax(0, 1fr) 20rem; align-items: center; gap: 2rem; }
}
.dhero__tx { min-width: 0; }
.dhero .kicker { color: #a8c7fa; }
.dhero__h1 {
  margin-block-start: .3rem;
  font-size: clamp(1.5rem, 1.2rem + 1.5vw, 2.125rem); font-weight: 780;
  letter-spacing: -.025em; line-height: 1.1;
}
.dhero__p {
  margin-block-start: .55rem;
  font-size: .9375rem; line-height: 1.6; color: rgba(255, 255, 255, .8);
}
.dhero__side { display: grid; gap: .5rem; min-width: 0; }
.dhero__k {
  font-size: .625rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: rgba(255, 255, 255, .55);
}
.dbase {
  display: flex; align-items: center; gap: .5rem; min-width: 0;
  padding: .5rem .5rem .5rem .75rem; border-radius: var(--r);
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(255, 255, 255, .16);
}
.dbase__u {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--mono-font); font-size: .8125rem; color: #fff;
  overflow-wrap: anywhere;
}
.dhero__cta { justify-content: center; }
@media (min-width: 56rem) { .dhero__cta { justify-self: start; } }

/* ── la grille de la page ──────────────────────────────────────────────── */
.doc { display: grid; gap: 1.25rem; align-items: start; min-width: 0; }
.doc__body { min-width: 0; display: grid; gap: 1.5rem; }
/* 🔴 Sans ceci, chaque section refuse de descendre sous la largeur de son plus
   large tableau, et c'est la PAGE qui s'élargit. */
.doc__body > * { min-width: 0; }

@media (min-width: 64rem) {
  .doc { grid-template-columns: minmax(0, 1fr) 14.5rem; gap: 2rem; }
  /* Placement EXPLICITE plutôt que `order` : le sommaire est premier dans le
     document (pour le téléphone et pour le clavier) et s'affiche à droite. */
  .doc__body { grid-column: 1; grid-row: 1; }
  .doc__toc  { grid-column: 2; grid-row: 1; }
}

/* ── le sommaire : pastilles qui défilent sur téléphone ────────────────────
   ⚠️ Collant sous les deux bandes d'en-tête, donc toujours à portée. Une seule
      liste de liens sert les deux formes : rien à resynchroniser.
   -------------------------------------------------------------------------- */
.doc__toc {
  position: sticky; z-index: 20;
  top: calc(var(--top-h) + var(--sub-h));
  padding: .45rem .5rem;
  border: 1px solid var(--border); border-radius: var(--r);
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-1);
  min-width: 0;
}
.doc__tock { display: none; }
.doc__nav {
  display: flex; gap: .35rem; overflow-x: auto; scrollbar-width: none;
  /* La rangée déborde volontairement : on la fait défiler au doigt. Les bords
     s'estompent pour que ça se voie. */
  -webkit-overflow-scrolling: touch;
  mask-image: linear-gradient(90deg, transparent, #000 .75rem,
                                     #000 calc(100% - .75rem), transparent);
}
.doc__nav::-webkit-scrollbar { display: none; }
.doc__l {
  flex: none; white-space: nowrap;
  padding: .4rem .7rem; border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: .8125rem; color: var(--fg-2); text-decoration: none;
  transition: background-color .14s ease, color .14s ease, border-color .14s ease;
}
.doc__l--2 { font-size: .78125rem; color: var(--fg-3); }
.doc__l:hover { border-color: var(--brand-line); color: var(--fg); }
.doc__l.is-here {
  background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 640;
}

@media (min-width: 64rem) {
  /* Colonne collante à droite. La rangée redevient une liste. */
  .doc__toc {
    top: calc(var(--top-h) + var(--sub-h) + 1rem);
    margin-inline: 0; padding: .6rem .7rem;
    background: var(--surface); backdrop-filter: none; box-shadow: none;
    border: 1px solid var(--border); border-radius: var(--r-md);
    /* 🔴 On s'arrête AU-DESSUS du lanceur du chat. Il flotte à `bottom: 1rem`
       dans le coin bas-droit, exactement là où finit cette colonne : sans cette
       réserve, il recouvre la ou les deux dernières entrées du sommaire — et
       une entrée recouverte n'est pas cliquable. */
    max-height: calc(100vh - var(--top-h) - var(--sub-h) - 7rem);
    overflow: auto; overscroll-behavior: contain;
  }
  .doc__tock {
    display: block; padding: .2rem .4rem .45rem;
    font-size: .625rem; font-weight: 800; letter-spacing: .07em;
    text-transform: uppercase; color: var(--fg-3);
  }
  .doc__nav { display: grid; gap: .05rem; overflow: visible; mask-image: none; }
  .doc__l {
    white-space: normal; border: 0; background: none; border-radius: var(--r-sm);
    border-inline-start: 2px solid transparent;
    padding: .3rem .5rem;
  }
  .doc__l--2 { padding-inline-start: 1.1rem; }
  .doc__l:hover { background: var(--surface-2); }
  .doc__l.is-here {
    background: var(--accent-soft); border-inline-start-color: var(--accent);
    color: var(--accent-ink);
  }
}

/* ── les sections ──────────────────────────────────────────────────────── */
.dsec {
  display: grid; gap: .7rem; min-width: 0;
  scroll-margin-block-start: calc(var(--top-h) + var(--sub-h) + 4rem);
}
.dsec > * { min-width: 0; }
.dsec--ep {
  padding: 1.1rem; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface);
}
@media (min-width: 48rem) { .dsec--ep { padding: 1.35rem 1.5rem; } }
.dsec__h2 {
  font-size: clamp(1.1875rem, 1.05rem + .6vw, 1.5rem); font-weight: 760;
  letter-spacing: -.02em; color: var(--fg);
}
.dsec__h3 { font-size: 1.0625rem; font-weight: 730; color: var(--fg); }
.dsec__anchor {
  margin-inline-start: .35rem; color: var(--fg-3); text-decoration: none;
  font-weight: 400; opacity: 0; transition: opacity .14s ease;
}
.dsec__h2:hover .dsec__anchor, .dsec__h3:hover .dsec__anchor,
.dsec__anchor:focus-visible { opacity: 1; }
.dsec__p { font-size: .875rem; line-height: 1.65; color: var(--fg-2); }
.dsub {
  margin-block-start: .3rem;
  font-size: .625rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3);
}

/* La ligne méthode + adresse. Elle défile seule plutôt que d'élargir la carte :
   `/api/v1/pricing?service={service}&country={country}` ne tient pas sur 390 px
   et la couper en deux la rendrait illisible. */
.dep__line {
  display: flex; align-items: center; gap: .5rem; min-width: 0;
  padding: .45rem .6rem; border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: thin;
}
.dep__m {
  flex: none; padding: .16rem .45rem; border-radius: var(--r-sm);
  font-size: .625rem; font-weight: 800; letter-spacing: .05em; color: #fff;
}
.dep__m--get  { background: #0a7d55; }
.dep__m--post { background: #1f5fd0; }
.dep__u {
  font-family: var(--mono-font); font-size: .8125rem; color: var(--fg);
  white-space: nowrap;
}

/* ── les tableaux ──────────────────────────────────────────────────────────
   Sur téléphone ils deviennent des CARTES : une table de trois colonnes sur
   390 px donne des mots coupés lettre par lettre, ou un défilement latéral
   dans lequel on perd la colonne des noms.
   -------------------------------------------------------------------------- */
.dtab { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.dtab th, .dtab td { text-align: start; vertical-align: top; }
.dtab thead th {
  padding: .3rem .7rem; font-size: .625rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3);
}
.dtab th[scope="row"] { font-weight: 620; color: var(--fg); }
.dtab td { color: var(--fg-2); line-height: 1.55; }

@media (max-width: 47.999rem) {
  .dtab thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .dtab, .dtab tbody, .dtab tr, .dtab th, .dtab td { display: block; }
  .dtab tr {
    padding: .6rem .75rem; border: 1px solid var(--border);
    border-radius: var(--r); background: var(--surface-2);
  }
  .dtab tr + tr { margin-block-start: .4rem; }
  .dtab th[scope="row"] { margin-block-end: .15rem; }
  .dtab td { padding-block: .1rem; }
  /* La colonne « type » et le code HTTP redeviennent des étiquettes en ligne. */
  .dtab td:has(.dtype), .dtab td:has(.dhttp), .dtab td:has(.dreq), .dtab td:has(.dopt) {
    display: inline-block; margin-inline-end: .4rem;
  }
}
@media (min-width: 48rem) {
  .dtab th, .dtab td { padding: .5rem .7rem; border-block-start: 1px solid var(--border); }
  .dtab thead th { border-block-start: 0; }
  .dtab tbody tr:first-child th, .dtab tbody tr:first-child td { border-block-start: 0; }
  .dtab th[scope="row"] { white-space: nowrap; }
}

.dcodei {
  font-family: var(--mono-font); font-size: .8125rem;
  padding: .08rem .3rem; border-radius: var(--r-sm);
  background: var(--surface-3); color: var(--fg);
  overflow-wrap: anywhere;
}
.dcodei--a { text-decoration: none; }
.dcodei--a:hover { background: var(--accent-soft); color: var(--accent-ink); }
.dtype { font-family: var(--mono-font); font-size: .75rem; color: var(--fg-3); }
.dreq { font-size: .6875rem; font-weight: 700; color: var(--danger); }
.dopt { font-size: .6875rem; color: var(--fg-3); }
.dno {
  font-size: .6875rem; font-weight: 700; color: var(--warn);
  padding: .05rem .3rem; border-radius: var(--r-sm); background: var(--warn-soft);
}
.dhttp {
  display: inline-block; min-width: 2.4rem; text-align: center;
  padding: .1rem .35rem; border-radius: var(--r-sm);
  font-family: var(--mono-font); font-size: .75rem; font-weight: 700;
}
.dhttp--ok { background: color-mix(in oklab, var(--ok) 14%, var(--surface)); color: var(--ok); }
.dhttp--x4 { background: var(--warn-soft); color: var(--warn); }
.dhttp--x5 { background: color-mix(in oklab, var(--danger) 12%, var(--surface)); color: var(--danger); }

.derrs { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.derrs__k {
  font-size: .625rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3); margin-inline-end: .2rem;
}

.dsteps { display: grid; gap: .7rem; }
.dsteps li { display: flex; gap: .65rem; align-items: flex-start; min-width: 0; }
.dsteps__n {
  display: grid; place-items: center; flex: none;
  width: 1.5rem; height: 1.5rem; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: .75rem; font-weight: 800;
}
.dsteps__tx { display: grid; gap: .1rem; font-size: .8125rem; line-height: 1.55; min-width: 0; }
.dsteps__tx b { color: var(--fg); font-weight: 700; }
.dsteps__tx span { color: var(--fg-2); }

.dnote {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .75rem .9rem; border-radius: var(--r);
  font-size: .8125rem; line-height: 1.6;
}
.dnote--warn {
  border: 1px solid color-mix(in oklab, var(--warn) 34%, var(--surface));
  background: var(--warn-soft); color: var(--warn);
}
.dnote__i { width: 1rem; height: 1rem; flex: none; margin-block-start: .18rem; }
.dnote__tx { display: grid; gap: .12rem; min-width: 0; }
.dnote__h { font-weight: 720; }

/* ── les blocs de code ─────────────────────────────────────────────────────
   Fond d'encre dans les DEUX thèmes : un bloc qui change de fond entre clair et
   sombre se lit comme une capture d'écran collée.
   -------------------------------------------------------------------------- */
.dcode {
  border: 1px solid var(--border); border-radius: var(--r);
  background: #0f172a; overflow: hidden; min-width: 0;
}
.dcode__bar {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .35rem .4rem .35rem .75rem;
  background: rgba(255, 255, 255, .05);
  border-block-end: 1px solid rgba(255, 255, 255, .08);
}
.dcode__file {
  font-family: var(--mono-font); font-size: .6875rem; color: rgba(255, 255, 255, .55);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.dcode__pre {
  margin: 0; padding: .8rem .9rem; overflow-x: auto;
  font-family: var(--mono-font); font-size: .75rem; line-height: 1.65;
  color: #e2e8f0; tab-size: 2;
}
@media (min-width: 48rem) { .dcode__pre { font-size: .78125rem; } }
.dcode__pre code { white-space: pre; }

/* 🔴 `width: auto` EXPLICITE. La règle mobile `.cpy { width: 100% }` de l'écran
   de dépôt s'appliquait ici aussi : chaque bouton réclamait 525 px dans sa
   barre d'outils et poussait la section entière à 581 px de large. */
.cpy--sm, .cpy--ink {
  width: auto; flex: none; min-width: 0;
  padding: .3rem .55rem; font-size: .6875rem; min-height: 1.9rem;
}
.cpy--sm svg, .cpy--ink svg { width: .8rem; height: .8rem; }
.cpy--sm, .cpy--ink {
  background: rgba(255, 255, 255, .10);
  border-color: rgba(255, 255, 255, .18);
  color: rgba(255, 255, 255, .85);
}
.cpy--sm:hover, .cpy--ink:hover {
  background: rgba(255, 255, 255, .18); border-color: rgba(255, 255, 255, .32); color: #fff;
}
.cpy--sm.is-done, .cpy--ink.is-done {
  background: rgba(60, 221, 168, .18); border-color: rgba(60, 221, 168, .4); color: #8df3d0;
}
@media (pointer: coarse) {
  /* Au doigt, 1,9 rem se rate. On grandit sans jamais prendre toute la ligne. */
  .cpy--sm, .cpy--ink { min-height: 2.25rem; padding-inline: .7rem; }
}

/* Requête et réponse : empilées sur téléphone (deux colonnes de code y
   défileraient chacune de leur côté), côte à côte dès qu'il y a la place. */
.dpair { display: grid; gap: .8rem; min-width: 0; }
.dpair > * { min-width: 0; }
@media (min-width: 64rem) { .dpair { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.dlog { display: grid; gap: .5rem; }
.dlog li { display: flex; flex-wrap: wrap; gap: .2rem .75rem; font-size: .8125rem; color: var(--fg-2); }
.dlog__d {
  flex: none; font-family: var(--mono-font); font-size: .75rem; color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 40rem) {
  /* Sur écran tactile il n'y a pas de survol : l'ancre reste visible. */
  .dsec__anchor { opacity: .4; }
}

/* L'appel a l'action de la section developpeurs de l'accueil. */
.api__cta { margin-block-start: .9rem; }
.api__cta .btn svg { width: .9rem; height: .9rem; }

/* ── 36. la foire aux questions ────────────────────────────────────────────
   Même charpente que la documentation (en-tête sur l'encre, sommaire en tête,
   sections en cartes) : deux pages de référence qui se ressemblent
   s'apprennent une fois.
   🔴 Chaque question est un `<details>` REPLIÉ. Vingt-huit réponses dépliées
      font un mur dans lequel on ne trouve rien ; repliées, la page est une
      liste de questions qu'on parcourt du pouce.
   -------------------------------------------------------------------------- */

.faql { display: grid; gap: .5rem; }

.faqi {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); min-width: 0;
  scroll-margin-block-start: calc(var(--top-h) + var(--sub-h) + 4rem);
}
.faqi__q {
  display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem;
  /* 🔴 44 px de haut au minimum : une question est la cible qu'on vise au
     pouce, et elles sont empilées — se tromper de ligne est facile. */
  padding: .8rem 1rem; min-height: 2.75rem;
  list-style: none; cursor: pointer;
  font-size: .9375rem; font-weight: 640; line-height: 1.45; color: var(--fg);
}
.faqi__q::-webkit-details-marker { display: none; }
.faqi__q:hover { color: var(--accent-ink); }
.faqi__q:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; border-radius: var(--r-md); }
.faqi__c {
  width: 1rem; height: 1rem; flex: none; margin-block-start: .15rem;
  color: var(--fg-3); transition: rotate .18s ease;
}
.faqi[open] .faqi__c { rotate: 180deg; }
.faqi[open] { border-color: var(--brand-line); }
.faqi[open] .faqi__q { color: var(--accent-ink); }
.faqi__a { padding: 0 1rem 1rem; }
/* 🔴 PAS DE PLAFOND DE LARGEUR sur ces textes. Mesure a 1440 px : la colonne
   de contenu fait 676 px et le texte s'arretait a 588 — 88 px de vide a
   droite de CHAQUE reponse, et 172 sur le bloc de fin. Le plafond etait la
   pour la longueur de ligne, mais la colonne est deja bornee par la grille
   (sommaire a droite, rail a gauche) : elle plafonne le texte pour nous, et
   un second plafond par-dessus ne fait que creuser un trou.
   ⚠️ En dessous de 64 rem la colonne est plus etroite que l'ancien plafond :
      le retirer n'y change donc rien du tout. */
.faqi__a p { font-size: .875rem; line-height: 1.7; color: var(--fg-2); }
.faqi__link {
  display: inline-block; margin-block-start: .5rem;
  font-size: .75rem; color: var(--fg-3); text-decoration: none;
}
.faqi__link::before { content: '# '; }
.faqi__link:hover { color: var(--accent-ink); text-decoration: underline; }

/* 🔴 Une ancre doit OUVRIR la réponse qu'elle désigne, pas seulement défiler
   jusqu'à un titre muet.
   ⚠️ `:target > .faqi__a { display: block }` NE MARCHE PAS, et c'est ce que
      j'avais écrit : le contenu d'un `<details>` fermé n'est pas masqué par un
      `display: none` qu'on pourrait annuler, il n'est pas distribué dans le
      slot du composant. La règle s'appliquait sans rien révéler — la question
      se surlignait, la réponse restait invisible. C'est donc le script qui
      ouvre (voir app.js) ; sans lui l'ancre amène quand même sur la bonne
      question, et un clic l'ouvre. Le surlignage, lui, reste en CSS pur. */
.faqi:target { border-color: var(--accent); box-shadow: var(--shadow-1); }
.faqi:target > .faqi__q { color: var(--accent-ink); }

/* La recherche. Révélée par le script : un champ qui ne filtre rien est pire
   qu'un champ absent. */
.faqs {
  display: flex; align-items: center; gap: .5rem; min-width: 0;
  padding: .5rem .75rem; border-radius: var(--r);
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(255, 255, 255, .16);
}
.faqs__i { width: 1rem; height: 1rem; flex: none; color: rgba(255, 255, 255, .55); }
.faqs__in {
  flex: 1 1 auto; min-width: 0; border: 0; background: none; color: #fff;
  font: inherit; font-size: .875rem; outline: none;
}
.faqs__in::placeholder { color: rgba(255, 255, 255, .45); }
.faqs__in::-webkit-search-cancel-button { filter: invert(1) opacity(.5); }

.faqnone {
  padding: 1.25rem 1rem; text-align: center;
  font-size: .875rem; color: var(--fg-2);
  border: 1px dashed var(--border-2); border-radius: var(--r-md);
}

/* La sortie de la page : on n'y arrive que quand on n'a pas trouvé. */
.faqend {
  display: grid; gap: .5rem; justify-items: start;
  padding: 1.25rem; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--border);
}
.faqend__h { font-size: 1rem; font-weight: 720; color: var(--fg); }
.faqend__p { font-size: .875rem; line-height: 1.6; color: var(--fg-2); }
.faqend__a { display: flex; flex-wrap: wrap; gap: .5rem; margin-block-start: .35rem; }

@media (max-width: 40rem) {
  .faqend__a { flex-direction: column; align-items: stretch; width: 100%; }
  .faqend__a .btn { width: 100%; min-height: 2.75rem; }
}

/* L'action d'un en-tête de page : le lien vers la référence depuis /api-key. */
.seohead__a { margin-block-start: .9rem; display: flex; flex-wrap: wrap; gap: .5rem; }
.seohead__a .btn svg { width: .9rem; height: .9rem; }
@media (max-width: 40rem) {
  .seohead__a .btn { width: 100%; min-height: 2.75rem; justify-content: center; }
}

/* ── 37. le chat d'assistance ──────────────────────────────────────────────
   🔴 LE LANCEUR NE DOIT JAMAIS RECOUVRIR LA BARRE D'ACHAT. Sur téléphone le bas
      de l'écran appartient au dock — 144 px au repos, jusqu'à 325 sur la
      location. Un bouton flottant posé à `bottom: 1rem` atterrirait dessus et
      masquerait le prix ou le bouton d'achat. Il part donc de `--dock-h`, la
      hauteur RÉELLE de la barre, mesurée par le script et déjà utilisée pour la
      réserve du pied de page.
   ⚠️ Sans JavaScript `--dock-h` n'existe pas : le repli vaut la hauteur du cas
      le plus haut, donc le bouton monte un peu trop. C'est le bon sens de
      l'erreur — il reste atteignable et ne cache rien.
   -------------------------------------------------------------------------- */

.chat { position: fixed; z-index: 70; inset-inline-end: 1rem; bottom: 1rem; }
@media (max-width: 67.999rem) {
  /* La marge au-dessus de la barre. Le `+ .75rem` évite que le bouton ne touche
     son bord — collé, on croit qu'il en fait partie. */
  .chat { bottom: calc(var(--dock-h, 17.5rem) + .75rem + env(safe-area-inset-bottom)); }
}

/* 🔴 UNE PASTILLE RONDE, ANIMÉE, SANS LIBELLÉ. Le mot à côté de la bulle
   doublait la largeur du bouton pour dire ce que le pictogramme dit déjà, et
   sur téléphone il venait manger la place juste au-dessus du dock.
   L'animation a un rôle : un bouton flottant immobile dans un coin se confond
   avec un bandeau publicitaire et ne se clique pas. Un halo qui respire dit
   qu'il y a quelqu'un derrière. */
.chat__btn {
  display: grid; place-items: center; position: relative;
  width: 3.25rem; height: 3.25rem;
  border-radius: 999px; border: 1px solid var(--accent);
  background: var(--accent); color: #fff; text-decoration: none;
  box-shadow: 0 8px 22px -6px rgba(15, 23, 42, .5);
  transition: transform .16s ease, box-shadow .16s ease;
}
.chat__btn:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 12px 28px -8px rgba(15, 23, 42, .55); }
.chat__btn:active { transform: translateY(0) scale(.97); }
.chat__btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
.chat__ico { display: grid; place-items: center; }
.chat__ico svg { width: 1.45rem; height: 1.45rem; }

/* Le halo. ⚠️ `::before` en `position: absolute` DERRIÈRE le bouton plutôt
   qu'une `box-shadow` animée : une ombre qui s'anime repeint tout le bouton à
   chaque image, le halo ne repeint que lui-même. */
.chat__btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; background: var(--accent);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  /* Deux mouvements, lents et décalés : le halo qui s'échappe toutes les
     4 s, et un souffle à peine perceptible sur la pastille. Assez pour
     accrocher l'œil au passage, pas assez pour gêner la lecture — c'est un
     élément qui reste à l'écran en permanence. */
  .chat__btn::before { animation: chatping 4s cubic-bezier(.22, 1, .36, 1) infinite; }
  .chat__ico { animation: chatnudge 4s ease-in-out infinite; }
  /* Au survol et une fois ouvert, tout s'arrête : l'animation appelle, elle
     n'accompagne pas. */
  .chat__btn:hover::before,
  .chat[data-open] .chat__btn::before { animation: none; opacity: 0; }
  .chat__btn:hover .chat__ico,
  .chat[data-open] .chat__ico { animation: none; }
}
@keyframes chatping {
  0%        { opacity: .55; transform: scale(1); }
  55%, 100% { opacity: 0;   transform: scale(1.75); }
}
@keyframes chatnudge {
  0%, 88%, 100% { transform: rotate(0deg) scale(1); }
  91%           { transform: rotate(-9deg) scale(1.07); }
  95%           { transform: rotate(7deg)  scale(1.07); }
}

/* La pastille de réponses non lues. */
.chat__dot {
  position: absolute; inset-block-start: -.15rem; inset-inline-end: -.15rem;
  display: grid; place-items: center; min-width: 1.25rem; height: 1.25rem;
  padding-inline: .3rem; border-radius: 999px;
  background: var(--danger); color: #fff;
  border: 2px solid var(--bg);
  font-size: .6875rem; font-weight: 800; font-variant-numeric: tabular-nums;
}

/* ── le panneau ────────────────────────────────────────────────────────── */
.chat__panel {
  position: absolute; inset-inline-end: 0; bottom: calc(100% + .6rem);
  width: min(23rem, calc(100vw - 2rem));
  display: flex; flex-direction: column;
  max-height: min(32rem, calc(100vh - var(--top-h) - var(--sub-h) - 6rem));
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); box-shadow: var(--shadow-3);
  overflow: hidden;
}
.chat__panel[hidden] { display: none; }
@media (max-width: 30rem) {
  /* Feuille pleine largeur : à 23 rem sur un écran de 24, les marges latérales
     ne servent qu'à rétrécir la conversation. */
  .chat__panel {
    position: fixed;
    /* 🔴 LE BORD DE FIN EST CELUI DU BOUTON (1rem), pas .5rem. Mesure du
       23/08/2026 : la feuille debordait de 8 px a droite de la pastille dont
       elle est censee sortir — assez peu pour ne pas sauter aux yeux, assez
       pour que le lien entre les deux ne se fasse pas. Le bord de DEBUT reste
       plus serre : c'est lui qui donne la largeur. */
    inset-inline-start: .5rem; inset-inline-end: 1rem;
    width: auto; bottom: calc(var(--dock-h, 17.5rem) + 4.4rem + env(safe-area-inset-bottom));
    max-height: calc(100vh - var(--top-h) - var(--dock-h, 17.5rem) - 7rem);
  }
}
.chat__head {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .7rem .5rem .7rem .9rem;
  background: var(--ink); color: #fff; flex: none;
}
.chat__title { display: inline-flex; align-items: center; gap: .45rem; font-size: .875rem; font-weight: 700; }
.chat__titlei { width: 1rem; height: 1rem; color: #a8c7fa; }
.chat__x {
  display: grid; place-items: center; width: 2rem; height: 2rem;
  border: 0; border-radius: var(--r-sm); background: none;
  color: rgba(255, 255, 255, .7); cursor: pointer;
}
.chat__x:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.chat__x svg { width: 1rem; height: 1rem; }
.chat__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.chat__load { padding: 1.5rem 1rem; text-align: center; font-size: .8125rem; color: var(--fg-3); }

/* ── les deux écrans de refus ──────────────────────────────────────────────
   Ils disent POURQUOI et donnent le chemin. Un « non » sans explication se lit
   comme une panne, et on réessaie.
   -------------------------------------------------------------------------- */
.chatg { display: grid; gap: .5rem; justify-items: center; text-align: center; padding: 1.5rem 1.15rem; }
.chatg__i {
  display: grid; place-items: center; width: 2.75rem; height: 2.75rem;
  border-radius: 999px; background: var(--accent-soft); color: var(--accent-ink);
}
.chatg__i svg { width: 1.35rem; height: 1.35rem; }
.chatg__h { font-size: 1rem; font-weight: 720; color: var(--fg); }
.chatg__p { font-size: .8125rem; line-height: 1.6; color: var(--fg-2); }
.chatg__b { margin-block-start: .35rem; min-height: 2.75rem; }
.chatg__alt { font-size: .78125rem; color: var(--fg-3); }
.chatg__alt a { color: var(--accent-ink); }

/* ── la conversation ───────────────────────────────────────────────────── */
.chatt {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: .5rem; padding: .9rem;
}
.chatt__empty {
  margin: auto; display: grid; gap: .4rem; justify-items: center; text-align: center;
  font-size: .8125rem; line-height: 1.55; color: var(--fg-3); padding: 1rem .5rem;
}
.chatt__emptyi { width: 1.25rem; height: 1.25rem; color: var(--fg-3); }
.chatm { max-width: 85%; display: grid; gap: .15rem; }
.chatm--me { justify-self: end; text-align: end; }
.chatm--us { justify-self: start; }
.chatm__b {
  padding: .55rem .75rem; border-radius: var(--r-md);
  font-size: .8125rem; line-height: 1.5; text-align: start;
  overflow-wrap: anywhere;
}
.chatm--me .chatm__b { background: var(--accent); color: #fff; border-end-end-radius: var(--r-sm); }
.chatm--us .chatm__b { background: var(--surface-2); border: 1px solid var(--border); color: var(--fg); border-end-start-radius: var(--r-sm); }
.chatm__t { font-size: .625rem; color: var(--fg-3); font-variant-numeric: tabular-nums; }

/* 🔴 L'accusé de réception ne se fait PAS passer pour un message d'agent : ni
   bulle, ni avatar, ni horodatage — une ligne d'état, et elle en a l'air. */
.chatack {
  display: flex; align-items: center; gap: .4rem; justify-content: center;
  padding: .4rem .6rem; border-radius: var(--r);
  background: color-mix(in oklab, var(--ok) 10%, var(--surface));
  font-size: .75rem; color: var(--ok);
}
.chatack__i { width: .9rem; height: .9rem; flex: none; }

/* ── la saisie ─────────────────────────────────────────────────────────── */
.chatf {
  flex: none; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: .5rem; align-items: end; padding: .7rem .75rem;
  border-block-start: 1px solid var(--border); background: var(--surface-2);
}
.chatf__e { grid-column: 1 / -1; font-size: .75rem; color: var(--danger); }
.chatf__in {
  min-width: 0; resize: none; font: inherit; font-size: .8125rem; line-height: 1.45;
  padding: .55rem .7rem; border: 1px solid var(--border-2); border-radius: var(--r);
  background: var(--surface); color: var(--fg); max-height: 7rem;
}
.chatf__in:focus { outline: 2px solid var(--ring); outline-offset: -1px; border-color: var(--accent); }
.chatf__send {
  display: grid; place-items: center; flex: none;
  width: 2.5rem; height: 2.5rem; border: 0; border-radius: var(--r);
  background: var(--accent); color: #fff; cursor: pointer;
}
.chatf__send:hover { background: var(--accent-hi, var(--accent)); }
.chatf__send:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.chatf__send svg { width: 1.05rem; height: 1.05rem; }

/* ── la page /support ──────────────────────────────────────────────────── */
.chatpage {
  display: flex; flex-direction: column; max-width: 44rem;
  min-height: 24rem; max-height: 60vh;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
}
.chatnote {
  display: flex; gap: .5rem; align-items: flex-start; margin-block-start: .9rem;
  font-size: .78125rem; line-height: 1.55; color: var(--fg-3); max-width: 44rem;
}
.chatnote__i { width: .95rem; height: .95rem; flex: none; margin-block-start: .15rem; }

/* ── 38. le pied de page sur téléphone ─────────────────────────────────────
   🔴 MESURÉ À 390 px AVANT : 1 023 px de haut — plus que l'écran — et des liens
      de 17 px, soit la moitié de ce qu'on vise au pouce. Quatre listes
      empilées, chacune avec son titre, qu'il fallait traverser pour atteindre
      la mention légale.
   La correction : les groupes se replient, les liens deviennent des rangées
   pleine largeur de 44 px, et le bloc de marque se resserre.
   -------------------------------------------------------------------------- */

/* Le repliement est une affaire de TÉLÉPHONE. Sur grand écran le groupe reste
   une colonne : un titre qui se replie au clic y surprend pour rien. */
.ftr__grp > .ftr__h { list-style: none; }
.ftr__grp > .ftr__h::-webkit-details-marker { display: none; }
.ftr__chev { display: none; }

@media (max-width: 47.999rem) {
  .ftr__top { gap: 0; }

  /* Le bloc de marque : plus resserré, et la phrase raccourcie par la mise en
     page plutôt que par le texte (elle sert aussi au grand écran). */
  .ftr__brand { padding-block-end: 1rem; }
  .ftr__blurb { font-size: .78125rem; }

  /* Chaque groupe devient une rangée repliable, séparée par un filet : c'est
     ce qui donne au pied de page l'allure d'une liste de réglages plutôt que
     d'un mur de liens. */
  .ftr__col { border-block-start: 1px solid var(--border); }
  .ftr__h {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    /* 🔴 48 px : c'est la cible qu'on ouvre, et il y en a quatre à la suite. */
    min-height: 3rem; margin: 0; padding-block: .6rem;
    cursor: pointer; color: var(--fg);
    font-size: .8125rem; font-weight: 700; letter-spacing: .02em; text-transform: none;
  }
  .ftr__h:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; border-radius: var(--r-sm); }
  .ftr__chev {
    display: block; width: 1rem; height: 1rem; flex: none;
    color: var(--fg-3); transition: rotate .18s ease;
  }
  .ftr__grp[open] > .ftr__h { color: var(--accent-ink); }
  .ftr__grp[open] .ftr__chev { rotate: 180deg; }

  .ftr__list { gap: 0; padding-block-end: .5rem; }
  .ftr__a {
    /* 🔴 44 px de haut et TOUTE la largeur : un lien de 83 px dans une rangée
       de 362 laisse 280 px de zone morte à côté de lui, et c'est là qu'on
       appuie. */
    display: flex; align-items: center; min-height: 2.75rem;
    padding-inline-start: .1rem; font-size: .875rem;
  }

  /* La barre du bas : empilée, lisible, et le tout centré — trois lignes
     ferrées à gauche sur un écran étroit se lisent comme un reste de page. */
  .ftr__bar {
    flex-direction: column; align-items: center; text-align: center;
    gap: .45rem; padding-block: 1rem 1.25rem;
  }
}

@media (min-width: 48rem) {
  /* Sur grand écran le `summary` n'est plus un bouton : ni curseur, ni clic. */
  .ftr__grp > .ftr__h { pointer-events: none; }
}

/* ── 39. le blog : l'index ─────────────────────────────────────────────────
   🔴 CONÇU POUR DES MILLIERS D'ARTICLES : douze cartes par page, une grille qui
      se remplit sans se déformer, et une pagination qui reste courte quelle que
      soit la longueur du catalogue.
   -------------------------------------------------------------------------- */

/* Les rubriques : une rangée de pastilles qui défile au doigt, comme le
   sommaire de la doc — même geste, même apparence, appris une fois. */
.ptopics {
  display: flex; gap: .4rem; overflow-x: auto; scrollbar-width: none;
  /* 🔴 AUCUNE marge verticale ici. `.col` est une grille avec `gap: 1.5rem` :
     une marge de 1,25 rem par-dessus s'AJOUTE au gap au lieu de le remplacer —
     mesuré 65 px entre l'accroche et les pastilles, et 44 px avant la grille,
     pour une rangée qui n'en fait que 36. Le gap de la colonne suffit, et il
     est le même partout ailleurs sur le site. */
  margin: 0; padding: 0;
  mask-image: linear-gradient(90deg, transparent, #000 .75rem,
                                     #000 calc(100% - .75rem), transparent);
}
.ptopics::-webkit-scrollbar { display: none; }
@media (min-width: 48rem) { .ptopics { flex-wrap: wrap; overflow: visible; mask-image: none; } }
.ptopics__l {
  flex: none; display: inline-flex; align-items: center; gap: .35rem;
  /* ⚠️ 36 px à la souris, 44 au doigt. Le plancher de 44 px vaut pour ce qu'on
     vise au pouce ; imposé au curseur, il gonfle une rangée de filtres qui ne
     porte que treize pixels de texte, et la page respire dans le vide. */
  min-height: 2.25rem; padding: .3rem .75rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--fg-2);
  font-size: .8125rem; font-weight: 600; text-decoration: none; white-space: nowrap;
  transition: border-color .14s ease, color .14s ease, background-color .14s ease;
}
.ptopics__l:hover { border-color: var(--brand-line); color: var(--fg); }
.ptopics__l.is-here { background: var(--accent); border-color: var(--accent); color: #fff; }
@media (pointer: coarse) { .ptopics__l { min-height: 2.75rem; padding-inline: .85rem; } }
.ptopics__n {
  font-size: .6875rem; font-weight: 700; color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.ptopics__l.is-here .ptopics__n { color: rgba(255, 255, 255, .75); }

.pfound { font-size: .8125rem; color: var(--fg-2); }
.pfound a { color: var(--accent-ink); margin-inline-start: .35rem; }

/* La grille. `auto-fill` avec un plancher : elle passe de 1 à 2 à 3 colonnes
   toute seule, sans point de rupture à maintenir. */
.pgrid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

.pcard {
  display: flex; flex-direction: column; min-width: 0; position: relative;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
  transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
}
.pcard:hover { border-color: var(--brand-line); transform: translateY(-2px); box-shadow: var(--shadow-2); }
.pcard:has(.pcard__a:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
.pcard__media { display: block; background: var(--ink); }
.pcard__img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.pcard__tx { display: flex; flex-direction: column; gap: .4rem; padding: .9rem 1rem 1.05rem; }
.pcard__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  font-size: .6875rem; color: var(--fg-3); font-variant-numeric: tabular-nums;
}
.pcard__dot { color: var(--border-2); }
.ptopic {
  color: var(--accent-ink); font-weight: 700; text-decoration: none;
  text-transform: uppercase; letter-spacing: .05em; font-size: .625rem;
  /* Au-dessus du lien de carte, sinon il est avalé par lui (voir ::after). */
  position: relative; z-index: 1;
}
.ptopic:hover { text-decoration: underline; }
.pcard__h { font-size: 1.0625rem; font-weight: 720; line-height: 1.3; letter-spacing: -.01em; }
.pcard__a { color: var(--fg); text-decoration: none; }
/* 🔴 Le lien du titre couvre TOUTE la carte. Seul le titre cliquable, c'est
   une cible de deux lignes dans une carte de 280 px de haut — on appuie à côté
   et il ne se passe rien. Le pseudo-élément garde un seul lien dans l'arbre
   d'accessibilité, là où envelopper l'image et le texte en annoncerait deux. */
.pcard__a::after { content: ''; position: absolute; inset: 0; }
.pcard__p { font-size: .8125rem; line-height: 1.6; color: var(--fg-2); }

/* La carte de tête : image à gauche sur grand écran, elle donne un point
   d'entrée à la page plutôt qu'une grille plate. */
@media (min-width: 52rem) {
  .pcard--big {
    grid-column: 1 / -1; flex-direction: row; align-items: stretch;
  }
  .pcard--big .pcard__media { flex: 0 0 55%; }
  .pcard--big .pcard__img { height: 100%; }
  .pcard--big .pcard__tx { justify-content: center; gap: .55rem; padding: 1.5rem 1.75rem; }
  .pcard--big .pcard__h { font-size: 1.5rem; letter-spacing: -.02em; }
  .pcard--big .pcard__p { font-size: .9375rem; }
}

/* La pagination. Une fenêtre autour de la page courante : à deux cents pages,
   la liste entière serait plus longue que ce qu'elle pagine. */
.ppage {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .5rem; margin-block-start: 1.75rem;
}
.ppage__b {
  display: inline-flex; align-items: center; gap: .35rem;
  min-height: 2.75rem; padding: .5rem .9rem;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); color: var(--fg-2);
  font-size: .8125rem; font-weight: 620; text-decoration: none;
}
.ppage__b:hover { border-color: var(--brand-line); color: var(--fg); }
.ppage__b svg { width: .9rem; height: .9rem; }
.ppage__prev { rotate: 180deg; }
.ppage__n { display: flex; align-items: center; gap: .2rem; }
.ppage__n a, .ppage__n .is-here {
  display: grid; place-items: center; min-width: 2.75rem; min-height: 2.75rem;
  padding-inline: .5rem; border-radius: var(--r);
  font-size: .8125rem; font-variant-numeric: tabular-nums; text-decoration: none;
}
.ppage__n a { color: var(--fg-2); }
.ppage__n a:hover { background: var(--surface-2); color: var(--fg); }
.ppage__n .is-here { background: var(--accent); color: #fff; font-weight: 700; }
.ppage__gap { color: var(--fg-3); padding-inline: .15rem; }

/* ── 40. le blog : un article ──────────────────────────────────────────────
   🔴 LA LECTURE PASSE AVANT TOUT. Corps à 1,0625 rem et interligne à 1,75 :
      c'est plus grand que le reste du site, et c'est voulu — on lit ici pendant
      cinq minutes, pas pendant cinq secondes.
   -------------------------------------------------------------------------- */

.post { display: grid; gap: 1.25rem; }
.post__head { display: grid; gap: .55rem; }
.post__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
  font-size: .75rem; color: var(--fg-3);
}
.post__h1 {
  font-size: clamp(1.625rem, 1.25rem + 1.9vw, 2.5rem); font-weight: 780;
  line-height: 1.12; letter-spacing: -.028em; text-wrap: balance; color: var(--fg);
}
.post__lede { font-size: clamp(1rem, .95rem + .3vw, 1.125rem); line-height: 1.6; color: var(--fg-2); }
.post__upd { font-size: .75rem; color: var(--fg-3); }

.post__cover { border-radius: var(--r-lg); overflow: hidden; background: var(--ink); }
.post__cover img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }

.post__grid { display: grid; gap: 1.5rem; align-items: start; min-width: 0; }
@media (min-width: 64rem) {
  .post__grid { grid-template-columns: minmax(0, 1fr) 14.5rem; gap: 2.5rem; }
  .post__body { grid-column: 1; grid-row: 1; }
  .post__toc  { grid-column: 2; grid-row: 1; }
}
.post__body { min-width: 0; display: grid; gap: 1.1rem; }
.post__body > * { min-width: 0; }

.pb__p { font-size: 1.0625rem; line-height: 1.75; color: var(--fg-1, var(--fg-2)); }
.pb__h2 {
  margin-block-start: 1.1rem;
  font-size: clamp(1.25rem, 1.1rem + .7vw, 1.625rem); font-weight: 760;
  letter-spacing: -.02em; line-height: 1.25; color: var(--fg);
  scroll-margin-block-start: calc(var(--top-h) + var(--sub-h) + 4rem);
}
.pb__h3 { margin-block-start: .5rem; font-size: 1.125rem; font-weight: 720; color: var(--fg); }

.pb__list { display: grid; gap: .5rem; padding-inline-start: 1.4rem; }
.pb__list li { font-size: 1.0625rem; line-height: 1.7; color: var(--fg-2); }
.pb__list--ul { list-style: disc; }
.pb__list--ol { list-style: decimal; }
.pb__list li::marker { color: var(--accent); }

.pb__note {
  display: flex; gap: .65rem; align-items: flex-start;
  padding: .95rem 1.1rem; border-radius: var(--r-md);
  border: 1px solid var(--accent-line); background: var(--accent-soft);
}
.pb__note p { font-size: .9375rem; line-height: 1.65; color: var(--accent-ink); }
.pb__notei { width: 1.1rem; height: 1.1rem; flex: none; margin-block-start: .2rem; color: var(--accent-ink); }

.pb__quote {
  padding-inline-start: 1.1rem; border-inline-start: 3px solid var(--accent);
}
.pb__quote p { font-size: 1.125rem; line-height: 1.6; color: var(--fg); font-style: italic; }

.pb__steps { display: grid; gap: .85rem; }
.pb__steps li { display: flex; gap: .8rem; align-items: flex-start; min-width: 0; }
.pb__stepn {
  display: grid; place-items: center; flex: none;
  width: 1.75rem; height: 1.75rem; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: .8125rem; font-weight: 800;
}
.pb__stept { display: grid; gap: .2rem; min-width: 0; }
.pb__stept b { font-size: 1rem; font-weight: 700; color: var(--fg); }
.pb__stept span { font-size: .9375rem; line-height: 1.65; color: var(--fg-2); }

.pb__tab { font-size: .875rem; }

/* L'appel à l'action, UNE fois, en fin d'article. Quelqu'un qui a lu jusqu'ici
   sait de quoi il s'agit ; un encart tous les trois paragraphes le ferait
   partir. */
.post__cta {
  display: grid; gap: .5rem; justify-items: start;
  margin-block-start: 1rem; padding: 1.35rem;
  border-radius: var(--r-md); background: var(--ink); color: #fff;
}
.post__ctah { font-size: 1.0625rem; font-weight: 740; }
.post__ctap { font-size: .875rem; line-height: 1.6; color: rgba(255, 255, 255, .78); }
.post__cta .btn { margin-block-start: .35rem; }

/* Précédent / suivant. */
.pnav { display: grid; gap: .6rem; margin-block-start: 1.5rem; }
@media (min-width: 40rem) { .pnav { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.pnav__a {
  display: grid; gap: .25rem; min-width: 0;
  padding: .85rem 1rem; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); text-decoration: none;
}
.pnav__a:hover { border-color: var(--brand-line); }
.pnav__a--next { text-align: end; }
.pnav__a--next .pnav__k { justify-content: flex-end; }
.pnav__k {
  display: flex; align-items: center; gap: .3rem;
  font-size: .625rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-3);
}
.pnav__i { width: .8rem; height: .8rem; }
.pnav__a--prev .pnav__i { rotate: 180deg; }
.pnav__t { font-size: .9375rem; font-weight: 660; line-height: 1.4; color: var(--fg); }

.prel { margin-block-start: 2.25rem; }
.prel__h { font-size: 1.1875rem; font-weight: 740; letter-spacing: -.015em; color: var(--fg); }

@media (max-width: 40rem) {
  .post__cta { justify-items: stretch; }
  .post__cta .btn { width: 100%; justify-content: center; min-height: 2.75rem; }
  .pb__p, .pb__list li { font-size: 1rem; }
}

/* 🔴 Le nom de colonne, réaffiché devant chaque valeur quand le tableau devient
   une pile de cartes. Sans lui, un comparatif à trois colonnes se lit
   « Charged / Per code received / Per term, up front » — deux valeurs et rien
   pour dire à quel produit chacune appartient. */
@media (max-width: 47.999rem) {
  .pb__tab td[data-l] { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: .5rem; }
  .pb__tab td[data-l]::before {
    content: attr(data-l);
    font-size: .6875rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--fg-3);
  }
}

/* Un lien dans le corps d'un article : souligné, parce qu'une couleur seule ne
   distingue pas un lien pour qui ne voit pas la nuance. */
.pb__a {
  color: var(--accent-ink); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: .18em;
}
.pb__a:hover { text-decoration-thickness: 2px; }

/* ── 41. les pages légales ─────────────────────────────────────────────────
   Même charpente que la documentation. Ce qui change : les sections sont
   NUMÉROTÉES — c'est ce qui permet de citer « article 4 » dans un échange avec
   le support, et c'est à quoi ressemble un document qu'on peut opposer.
   -------------------------------------------------------------------------- */

.lgdate { font-size: 1.0625rem; font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; }

.lgsec__n {
  display: inline-grid; place-items: center;
  min-width: 1.6rem; height: 1.6rem; padding-inline: .3rem;
  margin-inline-end: .5rem; vertical-align: .05em;
  border-radius: var(--r-sm); background: var(--accent-soft); color: var(--accent-ink);
  font-size: .8125rem; font-weight: 800; font-variant-numeric: tabular-nums;
}
.lgnum {
  display: inline-block; min-width: 1.1rem;
  margin-inline-end: .35rem; color: var(--fg-3);
  font-size: .75rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.doc__l.is-here .lgnum { color: inherit; }

/* Le corps : un peu plus grand que le reste du site. On lit une condition
   d'utilisation en se méfiant, et un texte serré s'abandonne. */
.lgp { font-size: .9375rem; line-height: 1.75; color: var(--fg-2); }
.lgul { display: grid; gap: .45rem; padding-inline-start: 1.35rem; list-style: disc; }
.lgul li { font-size: .9375rem; line-height: 1.7; color: var(--fg-2); }
.lgul li::marker { color: var(--accent); }
.lgsec { gap: .8rem; }

@media (max-width: 40rem) {
  .lgsec__n { min-width: 1.4rem; height: 1.4rem; font-size: .75rem; }
  .faqend__a .btn { width: 100%; justify-content: center; min-height: 2.75rem; }
}

/* ── 42. le programme revendeur ────────────────────────────────────────────
   🔴 Le bandeau de chiffres est le seul élément visuel de la page, et il porte
      des valeurs LUES dans le catalogue. C'est là que ce genre de page écrit
      d'habitude des nombres ronds inventés ; ceux-ci ne peuvent pas mentir, et
      c'est ce qui rend le reste croyable.
   -------------------------------------------------------------------------- */

.rsfigs {
  display: grid; gap: 1px; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--border);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 52rem) { .rsfigs { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.rsfig { padding: .95rem 1.1rem; background: var(--surface); min-width: 0; }
.rsfig__k {
  font-size: clamp(1.125rem, 1rem + .6vw, 1.4375rem); font-weight: 780;
  letter-spacing: -.02em; color: var(--fg); font-variant-numeric: tabular-nums;
}
.rsfig__v { margin-block-start: .15rem; font-size: .75rem; line-height: 1.4; color: var(--fg-3); }

/* Les deux actions de l'en-tête s'empilent proprement dans la colonne de
   droite, et prennent la largeur au téléphone. */
.dhero__side .dhero__cta + .dhero__cta { margin-block-start: -.1rem; }
@media (max-width: 40rem) {
  .dhero__side .btn { width: 100%; justify-content: center; min-height: 2.75rem; }
}

/* ══ « PAR OU COMMENCER » — LES TROIS RUBRIQUES DU BLOG ═══════════════════
   L'index du blog n'avait aucun titre de niveau 2 : le plan sautait du <h1>
   aux <h3> des cartes. Ce bloc ferme la page et donne une entree a qui ne
   sait pas quoi lire.

   ⚠️ `min-width: 0` sur les elements de grille : sans lui `min-width: auto`
      les empeche de retrecir sous la largeur de leur plus long mot, et la
      grille deborde a droite sur telephone — sans une seule erreur. */
.bstart {
  display: grid; gap: .75rem;
  margin-block-start: 1.1rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 46rem) {
  .bstart { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.bstart__i { min-width: 0; }
.bstart__a {
  display: block; height: 100%; min-width: 0;
  padding: .95rem 1.05rem;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface);
  text-decoration: none; color: inherit;
  transition: border-color .15s, box-shadow .15s, transform .15s;
}
.bstart__a:hover, .bstart__a:focus-visible {
  border-color: var(--brand);
  box-shadow: var(--shadow-1);
  transform: translateY(-1px);
}
/* ⚠️ Le mouvement au survol est retire pour qui le demande : une carte qui se
      souleve sous le curseur est une animation comme une autre. */
@media (prefers-reduced-motion: reduce) {
  .bstart__a { transition: none; }
  .bstart__a:hover, .bstart__a:focus-visible { transform: none; }
}
.bstart__h {
  display: block; font-weight: 680; font-size: .9375rem;
  line-height: 1.2; letter-spacing: -.01em; color: var(--fg);
}
.bstart__p {
  display: block; margin-block-start: .35rem;
  font-size: .8125rem; line-height: 1.5; color: var(--fg-3);
}

/* ══ LE SELECTEUR DE LANGUE ══════════════════════════════════════════════
   Il a pris la place de « Get started » en haut a droite. Meme composant que
   le menu de compte — un `<details>`, le seul qui s'ouvre sans script sous
   cette politique de contenu — et volontairement la meme silhouette : deux
   menus voisins qui ne s'ouvrent pas pareil se lisent comme deux bricolages.

   ⚠️ `.lang--top` est masque sous 68rem, comme `.acct` : la barre du haut n'a
      pas la place, et le tiroir mobile porte `.lang--block` a la place.
   ⚠️ Proprietes LOGIQUES partout (`inset-inline-end`, `margin-inline-start`) :
      ce composant est le premier que verra un visiteur en arabe, et c'est
      celui par lequel il basculera. Un `right: 0` le ferait sortir de l'ecran
      dans l'autre sens. */
.lang { position: relative; }
.lang--top { display: none; }
@media (min-width: 68rem) { .lang--top { display: block; } }
.lang--block { display: block; width: 100%; }

.lang__btn { list-style: none; }
.lang__btn::-webkit-details-marker { display: none; }
.lang__btn {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .35rem .5rem; border-radius: 999px; cursor: pointer;
  color: var(--top-fg); font-size: .8125rem; font-weight: 650;
  transition: background-color .15s ease;
}
.lang--block .lang__btn {
  display: flex; justify-content: center; width: 100%;
  padding: .7rem .9rem; border-radius: var(--r-sm);
  border: 1px solid var(--border); color: var(--fg);
}
.lang__btn:hover { background: rgba(255, 255, 255, .10); }
.lang--block .lang__btn:hover { background: var(--surface-2); }
.lang__btn:focus-visible { outline: 2px solid #a8c7fa; outline-offset: 2px; }
.lang[open] .lang__btn { background: rgba(255, 255, 255, .14); color: #fff; }
.lang--block.lang[open] .lang__btn { background: var(--surface-2); color: var(--fg); }
.lang__globe { width: 1.05rem; height: 1.05rem; flex: none; }
.lang__code { font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.lang__chev { width: .9rem; height: .9rem; opacity: .75; transition: rotate .18s ease; }
.lang[open] .lang__chev { rotate: 180deg; }

.lang__menu {
  position: absolute; z-index: 60;
  inset-inline-end: 0; top: calc(100% + .5rem);
  min-width: 13rem; max-height: 22rem; overflow-y: auto; padding: .35rem;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
}
/* Dans le tiroir, il n'y a rien a survoler par-dessus : le menu se pose dans
   le flux plutot que de flotter au-dessus du reste. */
.lang--block .lang__menu {
  position: static; margin-block-start: .4rem; min-width: 0;
  box-shadow: none;
}
@media (prefers-reduced-motion: no-preference) {
  .lang[open] .lang__menu { animation: dd-in .14s ease-out; }
}
.lang__head {
  padding: .5rem .6rem .55rem;
  border-block-end: 1px solid var(--border); margin-block-end: .3rem;
  font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-3);
}
.lang__i {
  display: flex; align-items: center; gap: .55rem; width: 100%;
  padding: .5rem .6rem; border-radius: var(--r-sm);
  font-size: .8125rem; font-weight: 560; color: var(--fg-2);
  text-decoration: none;
}
.lang__i:hover { background: var(--surface-2); color: var(--fg); }
.lang__i:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.lang__i.is-here { color: var(--fg); font-weight: 700; }
.lang__n { min-width: 0; }
/* ⚠️ Le code de langue est pousse au bord par une marge LOGIQUE : en écriture
      droite-a-gauche il doit filer a gauche, pas rester a droite. */
.lang__c {
  margin-inline-start: auto; font-size: .6875rem; font-weight: 700;
  letter-spacing: .04em; color: var(--fg-3);
}
.lang__ok { width: .95rem; height: .95rem; flex: none; color: var(--accent); }
.lang__soon {
  padding: .5rem .6rem .35rem; margin-block-start: .2rem;
  border-block-start: 1px solid var(--border);
  font-size: .75rem; line-height: 1.45; color: var(--fg-3);
}

/* ══ L'OUVERTURE DU CHAT ══════════════════════════════════════════════════
   🔴 LE PANNEAU NAIT DU BOUTON. Avant, il apparaissait d'un coup : un bloc
      posé par-dessus la page, sans lien visuel avec la pastille qu'on venait
      de cliquer. Trois choses le rattachent maintenant à elle :

        1. `transform-origin` sur le COIN du bouton — le panneau se déplie
           depuis ce point, pas depuis son propre centre ;
        2. le bouton se retourne et devient la croix de fermeture ;
        3. un raccord discret comble les quelques pixels entre les deux.

   ⚠️ L'ANIMATION N'EST BRANCHÉE QUE PAR LE SCRIPT (`[data-anim]`, posé par
      app.js). Sans JavaScript, le panneau arrive déjà déplié depuis le
      serveur : lui appliquer un état initial `opacity: 0` le rendrait
      définitivement invisible, puisque rien ne viendrait jamais l'ouvrir.
      C'est le piège de toute animation d'ouverture en amélioration
      progressive, et il ne se voit qu'en coupant le script.

   ⚠️ `display: none` (l'attribut `hidden`) ne se transitionne pas. Le script
      retire donc `hidden` AVANT d'animer, et ne le repose qu'une fois
      l'animation de fermeture terminée — sinon le panneau disparaît d'un
      coup à la fermeture, ce qui est exactement le défaut qu'on corrige. */

.chat[data-anim] .chat__panel {
  /* Le coin d'où ça sort : celui du bouton. Propriété LOGIQUE — en écriture
     droite-à-gauche le bouton est à gauche, et l'origine doit suivre. */
  transform-origin: 100% 100%;
  opacity: 0;
  transform: scale(.82) translateY(10px);
  /* Sortie franche puis arrivée qui s'installe : la courbe fait plus pour la
     sensation de « ça se déplie » que la durée. */
  transition:
    opacity   .16s ease,
    transform .28s cubic-bezier(.16, 1, .3, 1),
    visibility 0s linear .28s;
  visibility: hidden;
  will-change: transform, opacity;
}
[dir="rtl"] .chat[data-anim] .chat__panel { transform-origin: 0 100%; }

.chat[data-anim][data-open] .chat__panel {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition:
    opacity   .18s ease .02s,
    transform .34s cubic-bezier(.16, 1, .3, 1),
    visibility 0s;
}

/* Le contenu entre juste APRÈS le cadre, pas avec lui : un panneau dont le
   texte arrive en même temps que la boîte se lit comme une image qui charge. */
.chat[data-anim] .chat__head,
.chat[data-anim] .chat__body {
  opacity: 0; transform: translateY(6px);
  transition: opacity .2s ease, transform .24s cubic-bezier(.16, 1, .3, 1);
}
.chat[data-anim][data-open] .chat__head { opacity: 1; transform: none; transition-delay: .10s; }
.chat[data-anim][data-open] .chat__body { opacity: 1; transform: none; transition-delay: .15s; }

/* ── le bouton devient la fermeture ───────────────────────────────────── */
/* Les deux pictogrammes occupent LA MÊME cellule de grille : la largeur du
   bouton ne bouge pas pendant la bascule. */
.chat__btn { display: grid; grid-template-areas: "ico"; place-items: center; }
.chat__ico { grid-area: ico; transition: opacity .18s ease, rotate .28s cubic-bezier(.16, 1, .3, 1), scale .28s cubic-bezier(.16, 1, .3, 1); }
.chat__ico--x { opacity: 0; rotate: -90deg; scale: .6; }
.chat[data-open] .chat__ico            { opacity: 0; rotate: 90deg;  scale: .6; }
.chat[data-open] .chat__ico--x         { opacity: 1; rotate: 0deg;   scale: 1; }
/* Ouvert, la pastille se calme : elle n'appelle plus, elle sert de fermeture. */
.chat[data-open] .chat__btn {
  background: var(--surface-3); border-color: var(--border); color: var(--fg);
  box-shadow: 0 4px 14px -6px rgba(15, 23, 42, .4);
}

/* ── le raccord ───────────────────────────────────────────────────────── */
/* Un petit losange qui comble l'espace entre le panneau et le bouton. Il n'a
   aucun rôle informatif : il empêche seulement de lire le panneau comme un
   bloc flottant sans attache. */
.chat__tail {
  position: absolute; z-index: 1;
  inset-block-end: -.3rem; inset-inline-end: 1.35rem;
  width: .7rem; height: .7rem; rotate: 45deg;
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  border-block-end: 1px solid var(--border);
  border-end-end-radius: 2px;
}
/* Le panneau doit laisser passer le raccord : sans cela `overflow: hidden` le
   découpe et il ne reste rien. */
.chat__panel { overflow: visible; }
/* …mais le CORPS, lui, continue de découper son défilement. */
.chat__body { border-end-start-radius: var(--r-lg); border-end-end-radius: var(--r-lg); overflow: hidden; }
/* Sur téléphone le panneau est en pleine largeur et posé loin du bouton : le
   raccord ne relierait plus rien et pointerait dans le vide. */
@media (max-width: 30rem) { .chat__tail { display: none; } }

/* ⚠️ Mouvement réduit : on garde l'apparition, on retire le déplacement. Une
      ouverture instantanée reste préférable à un panneau qui glisse pour qui
      a demandé qu'on arrête de bouger. */
@media (prefers-reduced-motion: reduce) {
  .chat[data-anim] .chat__panel,
  .chat[data-anim][data-open] .chat__panel { transform: none; transition: opacity .12s linear, visibility 0s; }
  .chat[data-anim] .chat__head,
  .chat[data-anim] .chat__body { transform: none; transition: opacity .12s linear; }
  .chat__ico { transition: opacity .12s linear; }
  .chat__ico--x, .chat[data-open] .chat__ico { rotate: none; scale: none; }
}
