@charset "utf-8";
/* ============================================================================
 * REDORZO — SOCLE DE CHARTE DE LA VITRINE. UNE SEULE SOURCE, VINGT PAGES.
 *
 * ── POURQUOI CE FICHIER EXISTE ──────────────────────────────────────────────
 *
 * Mesuré le 03/08/2026 et jamais corrigé depuis : `web/` n'avait AUCUN fichier
 * CSS partagé. Ses pages redéclaraient chacune quatre à cinq blocs `:root`.
 * Conséquence pratique — corriger une couleur de la charte, c'était modifier
 * quatorze fichiers sans filet, et la quinzième page naissait toujours avec une
 * valeur de travers.
 *
 * Ce fichier est la source. Une page qui redéclare `:root` est un défaut, et le
 * cliquet `CHARTE-01` de `scripts/ci-checks.mjs` le refuse.
 *
 * ── LA RÈGLE DU FICHIER ─────────────────────────────────────────────────────
 *
 *     AUCUNE VALEUR LITTÉRALE SOUS LA SECTION « COMPOSANTS ».
 *
 * Pas de couleur, pas d'espacement, pas de rayon, pas de durée écrits en dur.
 * Si une valeur manque, elle devient un jeton. C'est la seule discipline qui
 * empêche la dérive de revenir par la porte des composants — celle par laquelle
 * elle est entrée la première fois.
 *
 * ── LA CHARTE NE BOUGE PAS ──────────────────────────────────────────────────
 *
 * Les valeurs de marque, de surface, de texte, de ligne et d'état sont celles de
 * `prototype/tokens.css`, elles-mêmes issues du tableau de bord du greffon,
 * seule surface validée par le client. Ordre du propriétaire, 08/09/2026 : on
 * garde les couleurs. Ce fichier les reprend à l'identique et n'en invente
 * aucune ; ce qu'il ajoute, ce sont des jetons DÉRIVÉS, chacun avec son calcul.
 *
 * ── LE CONTRASTE : CE QUE LA MESURE A MONTRÉ, ET LA SORTIE RETENUE ──────────
 *
 * La liste d'avant-vol porte « le contraste » en rouge depuis le 06/08 avec un
 * chiffre : 4,05 pour du blanc sur le dégradé signature. Ce chiffre regardait
 * UNE extrémité. Recalculé ici sur les deux :
 *
 *     blanc sur #4F6BFF (bleu, début du dégradé)    4,30   sous AA
 *     blanc sur #22D3EE (cyan, fin du dégradé)      1,81   ILLISIBLE
 *
 * La moitié droite de chaque bouton signature portait donc du texte blanc à
 * 1,81. Le propriétaire a tranché le 08/09 : « on laisse » — le blanc reste, la
 * charte ne bouge pas. On tient donc la conformité par la GÉOMÉTRIE :
 *
 *   · le dégradé décoratif garde ses deux bornes exactes (`--rz-sig`) ;
 *   · les surfaces qui portent du texte blanc emploient `--rz-sig-text`, dont
 *     la rampe est étirée à 300 % : la portion visible ne traverse que le
 *     premier tiers, donc le pire fond sous du texte est `--rz-sig-safe`,
 *     mesuré à 3,25 — au-dessus du seuil de 3,0 des textes GRANDS ;
 *   · elles ne portent QUE du texte grand au sens WCAG (≥ 24 px, ou ≥ 18,66 px
 *     en gras). Tout texte de taille normale sur fond de marque prend l'indigo
 *     plein `--rz-brand-strong`, mesuré à 6,29.
 *
 * `scripts/contraste-vitrine.mjs` recalcule tout cela depuis CE fichier, et
 * refuse que `--rz-sig-safe` mente si quelqu'un retire l'étirement.
 * ========================================================================== */

/* ══════════════════════════════════════════════════════ 1. LES JETONS ═══ */

/* ── LES JETONS SONT DANS UN FICHIER À PART, ET C'EST RÉCENT ──────────────
 *
 * Les quatre blocs qui portaient les valeurs de la charte — `:root`, les deux
 * blocs sombres et le clair réaffirmé — vivent désormais dans
 * `rz-jetons.css`. Ils n'ont pas été recopiés : ils ont été DÉPLACÉS, et ce
 * fichier les lit au même endroit que tout le monde.
 *
 * Pourquoi : la console d'administration devait adopter la charte. En
 * important `rz.css` en entier, elle héritait aussi des seize règles que ce
 * fichier pose sur des éléments nus — `body`, `h1` à `h4`, `img, svg, video`,
 * `button`… — dont une vitrine a besoin et dont une console dense n'en veut
 * pas. Mesuré au rendu : la HAUTEUR de quatre écrans changeait
 * (`client` 927 → 912 px, `emails` 1294 → 1300, `ledger` 1253 → 1246,
 * `diagnostic` 1047 → 1050). Une couleur ne déplace rien ; une règle, si.
 *
 * Ce qu'on partage entre les surfaces, ce sont les VALEURS. Les règles,
 * chaque surface les écrit pour elle.
 *
 * Le bloc d'alias sans préfixe, plus bas dans ce fichier, N'A PAS bougé : il
 * est fait pour dix pages historiques de la vitrine, et la console ne doit
 * surtout pas en hériter. */
@import url('rz-jetons.css');

/* ═══════════════════════════════════════════════════ 2. LE SOCLE HTML ═══ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* La barre haute est collante : sans ce décalage, une ancre atterrit dessous
     et le visiteur croit que le lien n'a rien fait. */
  scroll-padding-top: var(--rz-s-20);
}

body {
  background: var(--rz-bg);
  color: var(--rz-text);
  font-family: var(--rz-font-sans);
  font-size: var(--rz-fs-md);
  line-height: var(--rz-lh-base);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--rz-font-display);
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: var(--rz-lh-tight);
  text-wrap: balance;
}
h1 { font-size: var(--rz-fs-4xl); font-weight: 800; }
h2 { font-size: var(--rz-fs-3xl); }
h3 { font-size: var(--rz-fs-xl); line-height: var(--rz-lh-snug); }
h4 { font-size: var(--rz-fs-lg); line-height: var(--rz-lh-snug); }

p { text-wrap: pretty; }
a { color: inherit; text-decoration: none; }
/* Tâche 5.2 (WCAG 1.4.1) : un lien DANS une phrase se voit autrement que par sa
   couleur. Seuls les liens sans classe du contenu principal : navigation,
   boutons et cartes portent une classe, ou vivent hors de <main>. */
main :where(p, li, dd, label) a:not([class]) { text-decoration: underline; text-underline-offset: 2px; }
img, svg, video { max-width: 100%; height: auto; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }
strong, b { font-weight: 700; }
ul, ol { list-style: none; }

/* Un seul anneau de focus pour tout le produit, et il est VISIBLE.
   `:focus-visible` seul : un clic à la souris ne doit pas dessiner l'anneau,
   sinon on apprend aux intégrateurs à le retirer. */
:focus-visible {
  outline: var(--rz-ring-w) solid var(--rz-ring);
  outline-offset: var(--rz-ring-off);
  border-radius: var(--rz-r-xs);
}

/* Le mouvement est un confort, jamais une information. Sous
   `prefers-reduced-motion`, tout s'arrête — y compris le défilement doux, qui
   déclenche des malaises vestibulaires chez les personnes concernées. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═════════════════════════════════════════════════ 3. LES COMPOSANTS ═══ */
/*  Rappel de la règle : à partir d'ici, AUCUNE valeur littérale.            */

/* ---- Mise en page ------------------------------------------------------- */

.rz-wrap { width: 100%; max-width: var(--rz-wrap); margin-inline: auto; padding-inline: var(--rz-s-6); }
.rz-wrap-tight { max-width: var(--rz-wrap-tight); }
.rz-sec { padding-block: var(--rz-sec-y); position: relative; }
.rz-sec-sm { padding-block: var(--rz-s-16); }
.rz-read { max-width: var(--rz-read); }
.rz-center { text-align: center; margin-inline: auto; }
.rz-center .rz-read { margin-inline: auto; }

/* Le saut vers le contenu. Premier élément focusable de chaque page : sans lui,
   un visiteur au clavier retraverse la navigation entière à chaque page.

   ── POURQUOI `fixed` ET `opacity`, ET NON `absolute` ET `translateY` ──────

   Il était en `position: absolute` avec `translateY(-200%)`, ce qui le posait à
   81 pixels AU-DESSUS de l'origine du document. Invisible à l'écran — mais une
   capture PLEINE PAGE rend cette zone négative. Le propriétaire y a vu, le
   08/09, une case vide en haut à gauche dans une bande noire, sans pouvoir
   deviner ce que c'était ; et il avait raison de la signaler.

   `fixed` le rattache à la fenêtre et non au document : plus aucune géométrie
   hors page. `opacity: 0` ne peint rien, là où un décalage peint ailleurs. Et
   `pointer-events: none` évite qu'un rectangle invisible n'avale les clics du
   coin haut-gauche — le défaut classique de cette technique, et celui qui la
   fait retirer six mois plus tard sans qu'on sache pourquoi elle était là. */
.rz-skip {
  position: fixed; left: var(--rz-s-4); top: var(--rz-s-4);
  z-index: var(--rz-z-overlay);
  opacity: 0; pointer-events: none;
  transform: translateY(-8px);
  background: var(--rz-surface); color: var(--rz-text);
  border: 1px solid var(--rz-border-strong); border-radius: var(--rz-r-sm);
  padding: var(--rz-s-3) var(--rz-s-5);
  font-family: var(--rz-font-mono); font-size: var(--rz-fs-sm);
  box-shadow: var(--rz-shadow-md);
  transition: opacity var(--rz-t-base) var(--rz-ease), transform var(--rz-t-base) var(--rz-ease);
}
.rz-skip:focus { opacity: 1; pointer-events: auto; transform: translateY(0); }

/* ---- Étiquette de section (« eyebrow ») --------------------------------- */

.rz-eyebrow {
  display: inline-flex; align-items: center; gap: var(--rz-s-2);
  font-family: var(--rz-font-mono);
  font-size: var(--rz-fs-xs); font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--rz-brand-ink);
  margin-bottom: var(--rz-s-4);
}
.rz-eyebrow::before {
  content: ""; width: var(--rz-s-6); height: 2px; border-radius: var(--rz-r-pill);
  background: var(--rz-sig); flex: none;
}
.rz-on-ink .rz-eyebrow { color: var(--rz-ink-text); }

/* ---- Titres de section -------------------------------------------------- */

.rz-sec-head { max-width: var(--rz-read); margin-bottom: var(--rz-s-12); }
.rz-sec-head.rz-center { margin-inline: auto; }
.rz-sec-head p {
  font-size: var(--rz-fs-lg); color: var(--rz-text-2);
  margin-top: var(--rz-s-4); max-width: var(--rz-read);
}
.rz-sec-head.rz-center p { margin-inline: auto; }

/* Le mot mis en avant, peint EN dégradé (découpe de texte).
 *
 * Ce bloc disait que « le seuil ne s'applique pas de la même façon » et que le
 * mot « reste lisible parce qu'il est GRAND », et il prenait le dégradé
 * DÉCORATIF. Mesuré sur les pixels le 28/09 (`scripts/accessibilite/degrades.mjs`) :
 * 54 mots sur 104 sous 4,5:1, jusqu'à 1,79 sur blanc — sous le 3:1 des grands
 * textes eux-mêmes. Le propriétaire, ce jour-là : « visuellement les textes
 * doivent être visibles pour les clients ». Le mot prend désormais une rampe
 * PORTEUSE DE TEXTE (`--rz-hi-*`, rz-jetons.css), et une autre sur les bandes
 * sombres, dont les halos ont la teinte du dégradé.
 *
 * `background-image`, jamais `background` : le raccourci remettrait
 * `background-clip` à sa valeur par défaut, et le mot deviendrait un pavé
 * coloré aux lettres transparentes. */
.rz-hi {
  background-image: linear-gradient(100deg, var(--rz-hi-a), var(--rz-hi-b));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.rz-ink .rz-hi, .rz-on-ink .rz-hi {
  background-image: linear-gradient(100deg, var(--rz-hi-ink-a), var(--rz-hi-ink-b));
}

/* ---- Boutons ------------------------------------------------------------ */
/*
 * Quatre variantes, six états chacune. Un composant livré sans son état d'erreur
 * ou de chargement est un composant que quelqu'un improvisera au pire moment, et
 * l'improvisation ne sera dans aucun fichier de charte.
 */

.rz-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--rz-s-2);
  min-height: var(--rz-tap);
  padding: var(--rz-s-3) var(--rz-s-6);
  border-radius: var(--rz-r-sm);
  font-family: var(--rz-font-sans);
  font-size: var(--rz-fs-base); font-weight: 600;
  letter-spacing: -.005em; white-space: nowrap;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform var(--rz-t-fast) var(--rz-ease),
              box-shadow var(--rz-t-base) var(--rz-ease),
              background var(--rz-t-base) var(--rz-ease),
              border-color var(--rz-t-base) var(--rz-ease),
              opacity var(--rz-t-base) var(--rz-ease);
}
.rz-btn svg { width: 1.05em; height: 1.05em; stroke: currentColor; stroke-width: 2; fill: none; flex: none; }
.rz-btn:active { transform: translateY(1px); }
.rz-btn[disabled], .rz-btn[aria-disabled="true"] { opacity: .5; pointer-events: none; }

/* Chargement : la largeur ne bouge PAS. Un bouton qui rétrécit pendant sa
   requête déplace ce qu'il y a à côté, et l'utilisateur clique à côté. */
.rz-btn[data-chargement] { pointer-events: none; position: relative; color: transparent; }
.rz-btn[data-chargement]::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 1.1em; height: 1.1em; border-radius: var(--rz-r-pill);
  border: 2px solid currentColor; border-top-color: transparent;
  color: var(--rz-on-brand);
  animation: rz-tourne .7s linear infinite;
}
.rz-btn-ghost[data-chargement]::after, .rz-btn-quiet[data-chargement]::after { color: var(--rz-text); }
@keyframes rz-tourne { to { transform: rotate(1turn); } }

/* PRIMAIRE — indigo plein. C'est le bouton de travail du site : il porte du
   texte de taille normale, donc il lui faut 4,5. Mesuré : 6,29. */
.rz-btn-primary {
  background: var(--rz-brand-strong); color: var(--rz-on-brand);
  box-shadow: var(--rz-shadow-brand);
}
.rz-btn-primary:hover { transform: translateY(-1px); filter: brightness(1.08); }

/* SIGNATURE — le dégradé, réservé aux appels principaux. Taille imposée
   ci-dessous : ≥ 18,66 px en gras, seuil WCAG du texte GRAND. Retirer
   `font-size` ou passer sous 700 casserait la conformité en silence. */
.rz-btn-sig {
  background: var(--rz-sig-text); color: var(--rz-on-brand);
  font-size: var(--rz-fs-lg); font-weight: 700;
  padding: var(--rz-s-4) var(--rz-s-8);
  border-radius: var(--rz-r-md);
  box-shadow: var(--rz-shadow-brand);
}
.rz-btn-sig:hover { transform: translateY(-2px); filter: saturate(1.1); }

/* GHOST — contour, sur fond clair comme sur bande sombre. */
.rz-btn-ghost {
  background: var(--rz-surface); color: var(--rz-text);
  border-color: var(--rz-border-strong);
}
.rz-btn-ghost:hover { border-color: var(--rz-brand-ink); transform: translateY(-1px); }
.rz-on-ink .rz-btn-ghost {
  background: var(--rz-ink-surface); color: var(--rz-ink-text);
  border-color: var(--rz-ink-line);
}
.rz-on-ink .rz-btn-ghost:hover { border-color: var(--rz-ink-line-strong); }

/* QUIET — sans fond ni bordure, pour les actions de second rang. */
.rz-btn-quiet { color: var(--rz-text-2); padding-inline: var(--rz-s-4); }
.rz-btn-quiet:hover { color: var(--rz-text); background: var(--rz-surface-2); }

/* DESTRUCTIF — rouge PLEIN, pas un contour. Une action irréversible doit se
   distinguer AVANT le clic, pas dans la boîte de confirmation. */
.rz-btn-danger { background: var(--rz-crit); color: var(--rz-on-brand); }
.rz-btn-danger:hover { filter: brightness(1.08); }

.rz-btn-lg { font-size: var(--rz-fs-lg); padding: var(--rz-s-4) var(--rz-s-8); border-radius: var(--rz-r-md); }
.rz-btn-block { width: 100%; }

/* ---- Lien texte --------------------------------------------------------- */

.rz-link {
  color: var(--rz-brand-ink); font-weight: 600;
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: .22em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  transition: text-decoration-color var(--rz-t-base) var(--rz-ease);
}
.rz-link:hover { text-decoration-color: currentColor; }
.rz-on-ink .rz-link { color: var(--rz-ink-text); }

/* La flèche d'un lien d'action, qui avance au survol. */
.rz-arrow { display: inline-flex; align-items: center; gap: var(--rz-s-2); }
.rz-arrow svg { transition: transform var(--rz-t-base) var(--rz-ease); }
.rz-arrow:hover svg { transform: translateX(var(--rz-s-1)); }

/* ---- Cartes ------------------------------------------------------------- */

.rz-card {
  background: var(--rz-surface);
  border: 1px solid var(--rz-border);
  border-radius: var(--rz-r-lg);
  padding: var(--rz-s-8);
  box-shadow: var(--rz-shadow-sm);
  transition: transform var(--rz-t-base) var(--rz-ease),
              box-shadow var(--rz-t-base) var(--rz-ease),
              border-color var(--rz-t-base) var(--rz-ease);
}
.rz-card-hover:hover { transform: translateY(calc(var(--rz-s-1) * -1)); box-shadow: var(--rz-shadow-md); border-color: var(--rz-border-strong); }
.rz-card h3 { margin-bottom: var(--rz-s-3); }
.rz-card p { color: var(--rz-text-2); }
.rz-card-flush { padding: 0; overflow: hidden; }

/* Carte à liseré signature — la marque sans mettre de texte sur le dégradé. */
.rz-card-sig { position: relative; overflow: hidden; }
.rz-card-sig::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  width: var(--rz-s-1); background: var(--rz-sig);
}

/* ---- Pastille d'icône --------------------------------------------------- */

.rz-ico {
  width: var(--rz-s-12); height: var(--rz-s-12);
  border-radius: var(--rz-r-md);
  display: grid; place-items: center;
  background: var(--rz-sig); color: var(--rz-on-brand);
  margin-bottom: var(--rz-s-5); flex: none;
}
.rz-ico svg { width: 55%; height: 55%; stroke: currentColor; stroke-width: 2; fill: none; }
.rz-ico-soft { background: color-mix(in srgb, var(--rz-brand) 14%, transparent); color: var(--rz-brand-ink); }

/* ---- Badges et pastilles d'état ---------------------------------------- */
/*
 * La pastille double TOUJOURS la couleur par la forme et par le mot. Un
 * daltonien lit le point et le libellé ; la couleur seule n'est jamais une
 * information.
 */

.rz-badge {
  display: inline-flex; align-items: center; gap: var(--rz-s-2);
  padding: var(--rz-s-1) var(--rz-s-3);
  border-radius: var(--rz-r-pill);
  font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); font-weight: 500;
  letter-spacing: .04em;
  background: var(--rz-surface-2); color: var(--rz-text-2);
  border: 1px solid var(--rz-border);
}
.rz-badge-good { background: var(--rz-good-soft); color: var(--rz-good-text); border-color: var(--rz-good-soft); }
.rz-badge-warn { background: var(--rz-warn-soft); color: var(--rz-warn-text); border-color: var(--rz-warn-soft); }
.rz-badge-crit { background: var(--rz-crit-soft); color: var(--rz-crit-text); border-color: var(--rz-crit-soft); }
.rz-badge-info { background: var(--rz-info-soft); color: var(--rz-info-text); border-color: var(--rz-info-soft); }

.rz-dot { width: var(--rz-s-2); height: var(--rz-s-2); border-radius: var(--rz-r-pill); background: currentColor; flex: none; }
.rz-dot-good { color: var(--rz-good); }
.rz-dot-warn { color: var(--rz-warn); }
.rz-dot-crit { color: var(--rz-crit); }

/* ---- Grilles ------------------------------------------------------------ */
/*
 * `minmax(0, 1fr)` et non `1fr`. Un enfant de grille a `min-width: auto` par
 * défaut : il refuse de rétrécir sous son contenu et pousse la grille entière
 * au-delà du viewport. C'est le défaut qui a produit trois débordements
 * horizontaux dans le prototype, et corriger la CLASSE plutôt que les trois
 * instances est ce qui a évité le quatrième.
 */
.rz-grid { display: grid; gap: var(--rz-s-6); }
.rz-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rz-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rz-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.rz-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rz-s-16); align-items: center; }
.rz-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rz-s-3); min-width: 0; }
.rz-row > * { min-width: 0; }

/* ---- Bandes sombres ----------------------------------------------------- */

.rz-ink {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--rz-ink); color: var(--rz-ink-text);
}
.rz-ink h1, .rz-ink h2, .rz-ink h3 { color: var(--rz-ink-text); }
.rz-ink p { color: var(--rz-ink-text-2); }
.rz-aurore { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.rz-aurore i {
  position: absolute; display: block; border-radius: var(--rz-r-pill);
  filter: blur(90px); opacity: .55;
}
.rz-aurore i:nth-child(1) { width: 46%; padding-top: 46%; left: -6%; top: -18%; background: var(--rz-sig-a); }
.rz-aurore i:nth-child(2) { width: 44%; padding-top: 44%; right: -4%; top: 4%; background: var(--rz-sig-b); opacity: .35; }
.rz-aurore i:nth-child(3) { width: 38%; padding-top: 38%; left: 38%; bottom: -20%; background: var(--rz-brand); opacity: .3; }

/* ---- Barre de navigation ----------------------------------------------- */
/*
 * MESURÉ LE 08/09/2026 sur l'accueil précédent, largeur 1 440 px, thème sombre :
 * la barre DÉBORDAIT et passait sur deux lignes. « TOP TRANSLATE »,
 * « TOP CALENDAR » et « Se connecter » se coupaient en deux mots, sur un écran
 * d'ordinateur ordinaire — sept entrées, plus un sélecteur de thème, plus un
 * lien de connexion, plus le bouton d'appel.
 *
 * Le correctif n'est pas de rétrécir la police : c'est d'élaguer le menu. Il
 * porte quatre entrées. Et `white-space: nowrap` sur chacune fait qu'un
 * débordement futur se verra tout de suite au lieu de se cacher en repliant un
 * mot.
 */

.rz-nav {
  position: sticky; top: 0; z-index: var(--rz-z-nav);
  background: color-mix(in srgb, var(--rz-bg) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid transparent;
  transition: border-color var(--rz-t-base) var(--rz-ease), background var(--rz-t-base) var(--rz-ease);
}
.rz-nav[data-defile] { border-bottom-color: var(--rz-border); }

/* La barre EN TÊTE D'UNE PAGE QUI OUVRE SUR UNE BANDE SOMBRE.
 *
 * Sans ça, une barre claire au-dessus d'un héros sombre dessine une couture
 * nette en travers du premier écran : le héros ne se lit plus comme un héros
 * mais comme une bannière collée sous un bandeau.
 *
 * ── MA PREMIÈRE VERSION LA RENDAIT TRANSPARENTE. ELLE ÉTAIT FAUSSE. ────────
 *
 * Cette barre est `sticky`, donc DANS LE FLUX : au repos elle se place AU-DESSUS
 * du héros, pas par-dessus. Une barre transparente laissait donc voir le fond du
 * corps — clair en thème clair — et le texte clair devenait invisible dessus. En
 * thème sombre le défaut ne se voyait pas, puisque le fond du corps y est sombre
 * lui aussi : exactement le genre de défaut qui traverse une revue.
 *
 * Elle prend donc la COULEUR DE LA BANDE. Les deux se touchent, partagent la
 * même valeur, et se lisent comme un seul bloc — sans couture, et sans jamais
 * dépendre de ce qu'il y a derrière. */
.rz-nav[data-sur-ink]:not([data-defile]) {
  background: var(--rz-ink);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.rz-nav[data-sur-ink]:not([data-defile]) .rz-brand { color: var(--rz-ink-text); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-nav-links a { color: var(--rz-ink-text-2); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-nav-links a:hover { color: var(--rz-ink-text); background: var(--rz-ink-surface); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-icon-btn { color: var(--rz-ink-text-2); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-icon-btn:hover { color: var(--rz-ink-text); background: var(--rz-ink-surface); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-btn-quiet { color: var(--rz-ink-text-2); }
.rz-nav[data-sur-ink]:not([data-defile]) .rz-btn-quiet:hover { color: var(--rz-ink-text); background: var(--rz-ink-surface); }
.rz-nav-in {
  max-width: var(--rz-wrap); margin-inline: auto;
  padding: var(--rz-s-3) var(--rz-s-6);
  display: flex; align-items: center; gap: var(--rz-s-5);
  min-width: 0;
}

/* LA MARQUE S'ÉCRIT EN CAPITALES — décision du propriétaire, 08/09/2026 :
 * « REDORZO est la marque et le logo ».
 *
 * Le calage typographique change avec elle, et ce n'est pas cosmétique : le
 * `-.03em` était réglé pour un bas-de-casse, où les jambages laissent de l'air.
 * Appliqué à des capitales d'un grotesque en graisse 800, il les fait se
 * toucher — et une marque dont les lettres se touchent se lit comme un défaut
 * d'affichage, pas comme un logo. On desserre, et on descend d'un cran en
 * taille pour que le bloc garde la même présence qu'avant. */
.rz-brand {
  display: inline-flex; align-items: center; gap: var(--rz-s-3);
  font-family: var(--rz-font-display); font-weight: 800;
  font-size: var(--rz-fs-base); letter-spacing: .02em;
  color: var(--rz-text); flex: none;
}
/* LA PASTILLE DE MARQUE — EMPLACEMENT RÉSERVÉ AU LOGO DU PROPRIÉTAIRE.

   Elle porte aujourd'hui la lettre R sur le dégradé de la charte. Le logo
   définitif est dessiné par le propriétaire (décision du 08/09) ; le jour où
   il arrive, on remplace le `<span>` par une `<img>` de 32 x 32 et on retire
   `background`, `color`, `display: grid` et les deux règles de police
   ci-dessous. La taille et l'ombre restent : elles appartiennent à la mise
   en page, pas au dessin. Voir `docs/MARQUE-EMPLACEMENT.md`. */
.rz-brand-mark {
  width: var(--rz-s-8); height: var(--rz-s-8);
  border-radius: var(--rz-r-sm);
  background: var(--rz-sig); color: var(--rz-on-brand);
  display: grid; place-items: center;
  font-size: var(--rz-fs-base); font-weight: 800;
  box-shadow: var(--rz-shadow-brand); flex: none;
}

.rz-nav-links { display: flex; align-items: center; gap: var(--rz-s-1); min-width: 0; }
.rz-nav-links a {
  padding: var(--rz-s-2) var(--rz-s-3);
  border-radius: var(--rz-r-xs);
  font-size: var(--rz-fs-sm); font-weight: 600;
  color: var(--rz-text-2); white-space: nowrap;
  transition: color var(--rz-t-fast) var(--rz-ease), background var(--rz-t-fast) var(--rz-ease);
}
.rz-nav-links a:hover { color: var(--rz-text); background: var(--rz-surface-2); }
.rz-nav-links a[aria-current="page"] { color: var(--rz-brand-ink); background: color-mix(in srgb, var(--rz-brand) 10%, transparent); }

.rz-nav-end { margin-inline-start: auto; display: flex; align-items: center; gap: var(--rz-s-2); flex: none; }
.rz-nav-end .rz-btn { font-size: var(--rz-fs-sm); padding-inline: var(--rz-s-5); }

.rz-icon-btn {
  width: var(--rz-tap); height: var(--rz-tap);
  border-radius: var(--rz-r-sm);
  display: grid; place-items: center;
  color: var(--rz-text-2);
  border: 1px solid transparent;
  transition: background var(--rz-t-fast) var(--rz-ease), color var(--rz-t-fast) var(--rz-ease);
}
.rz-icon-btn:hover { background: var(--rz-surface-2); color: var(--rz-text); }
.rz-icon-btn svg { width: var(--rz-s-5); height: var(--rz-s-5); stroke: currentColor; stroke-width: 1.9; fill: none; }
.rz-icon-btn .rz-sun { display: none; }
:root[data-theme="dark"] .rz-icon-btn .rz-sun { display: block; }
:root[data-theme="dark"] .rz-icon-btn .rz-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rz-icon-btn .rz-sun { display: block; }
  :root:not([data-theme="light"]) .rz-icon-btn .rz-moon { display: none; }
}

.rz-burger { display: none; }


/* Le tiroir mobile. `hidden` porte l'état — pas `display` en ligne — pour que
   le contenu reste hors de l'ordre de tabulation quand il est fermé. */
.rz-drawer {
  position: fixed; inset: 0; z-index: var(--rz-z-overlay);
  background: var(--rz-bg);
  padding: var(--rz-s-6);
  display: flex; flex-direction: column; gap: var(--rz-s-2);
  overflow-y: auto;
}
.rz-drawer[hidden] { display: none !important; }
/* `tabindex="-1"` : le tiroir reçoit le focus À L'OUVERTURE, jamais par
   tabulation. Retirer l'anneau ici ne prive donc personne d'un repère — au
   contraire, il en posait un sur un conteneur que nul n'avait désigné. Les
   liens DANS le tiroir gardent le leur, et c'est là qu'il sert. */
.rz-drawer:focus { outline: none; }
.rz-drawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--rz-s-8); }
/* `>` ET NON UN DESCENDANT QUELCONQUE. La règle visait les liens de
   navigation, mais `.rz-drawer a` attrapait aussi la MARQUE, posée dans
   `.rz-drawer-head` : elle recevait le rembourrage et le filet du bas, ce qui
   dessinait un trait sous « REDORZO » et une boîte fantôme autour. Vu au rendu
   du 08/09, en vérifiant tout autre chose. */
.rz-drawer > a {
  padding: var(--rz-s-4);
  border-radius: var(--rz-r-sm);
  font-size: var(--rz-fs-lg); font-weight: 600;
  border-bottom: 1px solid var(--rz-border);
}
.rz-drawer > .rz-btn { margin-top: var(--rz-s-6); border-bottom: none; }

/* ---- Pied de page ------------------------------------------------------- */

.rz-foot { background: var(--rz-surface); border-top: 1px solid var(--rz-border); padding-block: var(--rz-s-16) var(--rz-s-8); }
.rz-foot-grid { display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(0, 1fr)); gap: var(--rz-s-10); }
.rz-foot h4 { font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--rz-text-3); margin-bottom: var(--rz-s-4); }
.rz-foot ul { display: flex; flex-direction: column; gap: var(--rz-s-3); }
.rz-foot ul a { font-size: var(--rz-fs-sm); color: var(--rz-text-2); }
.rz-foot ul a:hover { color: var(--rz-brand-ink); }
.rz-foot-bas { margin-top: var(--rz-s-12); padding-top: var(--rz-s-6); border-top: 1px solid var(--rz-border); display: flex; flex-wrap: wrap; gap: var(--rz-s-4); justify-content: space-between; font-size: var(--rz-fs-sm); color: var(--rz-text-3); }

/* ---- Tableau ------------------------------------------------------------ */
/*
 * Un tableau large défile DANS son conteneur, jamais en poussant la page. Le
 * corps du document ne défile jamais horizontalement.
 */
.rz-table-wrap { overflow-x: auto; border: 1px solid var(--rz-border); border-radius: var(--rz-r-lg); background: var(--rz-surface); }
.rz-table { width: 100%; border-collapse: collapse; font-size: var(--rz-fs-base); }
.rz-table th, .rz-table td { padding: var(--rz-s-4) var(--rz-s-5); text-align: start; border-bottom: 1px solid var(--rz-border); vertical-align: top; }
.rz-table thead th { font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); font-weight: 500; letter-spacing: .1em; text-transform: uppercase; color: var(--rz-text-3); background: var(--rz-surface-2); white-space: nowrap; }
.rz-table tbody tr:last-child td { border-bottom: none; }
.rz-table td strong { color: var(--rz-text); }
.rz-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.rz-table-mark { background: color-mix(in srgb, var(--rz-brand) 6%, transparent); }

/* ---- Questions fréquentes ---------------------------------------------- */

.rz-faq { border: 1px solid var(--rz-border); border-radius: var(--rz-r-lg); background: var(--rz-surface); overflow: hidden; }
.rz-faq details { border-bottom: 1px solid var(--rz-border); }
.rz-faq details:last-child { border-bottom: none; }
.rz-faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--rz-s-4);
  padding: var(--rz-s-5) var(--rz-s-6);
  font-size: var(--rz-fs-md); font-weight: 600;
  cursor: pointer; list-style: none;
  min-height: var(--rz-tap);
  transition: background var(--rz-t-fast) var(--rz-ease);
}
.rz-faq summary::-webkit-details-marker { display: none; }
.rz-faq summary:hover { background: var(--rz-surface-2); }
.rz-faq summary::after {
  content: ""; flex: none;
  width: var(--rz-s-3); height: var(--rz-s-3);
  border-right: 2px solid var(--rz-text-3); border-bottom: 2px solid var(--rz-text-3);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--rz-t-base) var(--rz-ease);
}
.rz-faq details[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.rz-faq .rz-faq-body { padding: 0 var(--rz-s-6) var(--rz-s-6); color: var(--rz-text-2); max-width: var(--rz-read); }

/* ---- Étapes numérotées -------------------------------------------------- */

.rz-steps { counter-reset: rz-step; display: grid; gap: var(--rz-s-8); }
.rz-step { position: relative; padding-inline-start: var(--rz-s-16); counter-increment: rz-step; }
.rz-step::before {
  content: counter(rz-step);
  position: absolute; inset-inline-start: 0; top: 0;
  width: var(--rz-s-12); height: var(--rz-s-12);
  border-radius: var(--rz-r-md);
  background: var(--rz-sig); color: var(--rz-on-brand);
  display: grid; place-items: center;
  font-family: var(--rz-font-display);
  /* ≥ 24 px : texte GRAND au sens WCAG, condition du blanc sur le dégradé. */
  font-size: var(--rz-fs-xl); font-weight: 800;
}
.rz-step h3 { margin-bottom: var(--rz-s-2); }
.rz-step p { color: var(--rz-text-2); max-width: var(--rz-read); }

/* ---- Liste à coches ----------------------------------------------------- */

.rz-checks { display: grid; gap: var(--rz-s-3); }
.rz-checks li { display: flex; align-items: flex-start; gap: var(--rz-s-3); color: var(--rz-text-2); }
.rz-checks svg { width: var(--rz-s-5); height: var(--rz-s-5); flex: none; margin-top: var(--rz-s-1); stroke: var(--rz-good); stroke-width: 2.4; fill: none; }
.rz-checks .rz-no svg { stroke: var(--rz-text-3); }

/* ---- Encart d'avertissement / d'information ---------------------------- */

.rz-note {
  border: 1px solid var(--rz-border);
  border-inline-start: var(--rz-s-1) solid var(--rz-brand);
  border-radius: var(--rz-r-md);
  background: var(--rz-surface-2);
  padding: var(--rz-s-5) var(--rz-s-6);
  color: var(--rz-text-2);
  max-width: var(--rz-read);
}
.rz-note strong { color: var(--rz-text); }
.rz-note-good { border-inline-start-color: var(--rz-good); background: var(--rz-good-soft); }
.rz-note-warn { border-inline-start-color: var(--rz-warn); background: var(--rz-warn-soft); }
.rz-note-crit { border-inline-start-color: var(--rz-crit); background: var(--rz-crit-soft); }

/* ---- Formulaires -------------------------------------------------------- */

.rz-field { display: grid; gap: var(--rz-s-2); margin-bottom: var(--rz-s-5); }
.rz-label { font-size: var(--rz-fs-sm); font-weight: 600; color: var(--rz-text); }
.rz-hint { font-size: var(--rz-fs-sm); color: var(--rz-text-3); }
.rz-input, .rz-select, .rz-textarea {
  width: 100%;
  min-height: var(--rz-tap);
  padding: var(--rz-s-3) var(--rz-s-4);
  border: 1px solid var(--rz-border-strong);
  border-radius: var(--rz-r-sm);
  background: var(--rz-surface);
  color: var(--rz-text);
  font-size: var(--rz-fs-base);
  transition: border-color var(--rz-t-fast) var(--rz-ease), box-shadow var(--rz-t-fast) var(--rz-ease);
}
.rz-input::placeholder, .rz-textarea::placeholder { color: var(--rz-text-3); }
.rz-input:hover, .rz-select:hover, .rz-textarea:hover { border-color: var(--rz-brand); }
.rz-textarea { min-height: calc(var(--rz-s-32) + var(--rz-s-4)); resize: vertical; line-height: var(--rz-lh-base); }
.rz-select { appearance: none; padding-inline-end: var(--rz-s-10); background-image: none; }
.rz-field[data-erreur] .rz-input,
.rz-field[data-erreur] .rz-select,
.rz-field[data-erreur] .rz-textarea { border-color: var(--rz-crit); }
.rz-err { font-size: var(--rz-fs-sm); color: var(--rz-crit-text); display: flex; align-items: center; gap: var(--rz-s-2); }

/* ---- États vides, chargement, erreur ------------------------------------ */

.rz-empty { text-align: center; padding: var(--rz-s-16) var(--rz-s-6); color: var(--rz-text-2); }
.rz-empty h3 { color: var(--rz-text); margin-bottom: var(--rz-s-3); }
.rz-skel {
  background: linear-gradient(90deg, var(--rz-surface-2) 25%, var(--rz-surface-3) 37%, var(--rz-surface-2) 63%);
  background-size: 400% 100%;
  border-radius: var(--rz-r-xs);
  animation: rz-skel 1.4s var(--rz-ease) infinite;
}
@keyframes rz-skel { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* ---- Captures d'écran des produits -------------------------------------- */
/*
 * ── POURQUOI CE COMPOSANT EXISTE, ET CE QU'IL RÉSOUT ──────────────────────
 *
 * Le site vendait une console que personne ne voyait. C'est le manque le plus
 * coûteux d'une page produit : un acheteur veut savoir à quoi ressemble l'écran
 * dans lequel il passera son temps, et aucune description ne remplace une image.
 *
 * ── LES DEUX RÈGLES QUI ÉVITENT LES DEUX DÉFAUTS CLASSIQUES ───────────────
 *
 * 1. `width` et `height` sont OBLIGATOIRES sur chaque `<img>`, et le cliquet
 *    `CAPTURE-01` les exige. Sans eux, le navigateur ne connaît pas la place à
 *    réserver : la page saute au moment où l'image arrive, sous les doigts de
 *    quelqu'un qui était en train de lire. C'est mesuré par le décalage
 *    cumulé, et c'est le défaut de performance le plus visible qui soit.
 *
 * 2. `loading="lazy"` partout SAUF la première image visible. Charger en
 *    différé une image du premier écran retarde précisément celle qu'on attend.
 */
.rz-shot { margin: 0; }
/* CAPTURE-02 : une capture d'attente ne se montre jamais. `hidden` doit gagner,
   même si une règle future donne un `display` à la figure ou à sa section. */
.rz-shot[hidden], .rz-sec[hidden] { display: none !important; }
.rz-shot-cadre {
  border: 1px solid var(--rz-border);
  border-radius: var(--rz-r-lg);
  background: var(--rz-surface);
  box-shadow: var(--rz-shadow-lg);
  overflow: hidden;
}
.rz-on-ink .rz-shot-cadre { border-color: var(--rz-ink-line); background: var(--rz-ink-surface); }

/* La barre de fenêtre. Elle situe la capture — « ceci est un écran, pas un
   schéma » — et c'est ce qui la rend crédible en une fraction de seconde. */
.rz-shot-barre {
  display: flex; align-items: center; gap: var(--rz-s-2);
  padding: var(--rz-s-3) var(--rz-s-4);
  border-bottom: 1px solid var(--rz-border);
  background: var(--rz-surface-2);
}
.rz-on-ink .rz-shot-barre { border-bottom-color: var(--rz-ink-line); background: var(--rz-ink-surface); }
.rz-shot-barre i { width: var(--rz-s-3); height: var(--rz-s-3); border-radius: var(--rz-r-pill); background: var(--rz-border-strong); flex: none; }
.rz-shot-url {
  flex: 1; margin-inline-start: var(--rz-s-2);
  font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs);
  color: var(--rz-text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rz-on-ink .rz-shot-url { color: var(--rz-ink-text-2); }
/* PERF-IMG-01 : la capture arrive en WebP (47 % plus légère), le PNG reste en repli. */
.rz-shot picture { display: block; }
.rz-shot img { display: block; width: 100%; height: auto; }
.rz-shot figcaption {
  margin-top: var(--rz-s-4);
  font-size: var(--rz-fs-sm); color: var(--rz-text-2);
  max-width: var(--rz-read);
}
.rz-on-ink .rz-shot figcaption { color: var(--rz-ink-text-2); }
.rz-shot-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rz-s-8); }
@media (max-width: 860px) { .rz-shot-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- Utilitaires -------------------------------------------------------- */

.rz-mono { font-family: var(--rz-font-mono); }
.rz-muted { color: var(--rz-text-2); }
.rz-small { font-size: var(--rz-fs-sm); }
.rz-mt-4 { margin-top: var(--rz-s-4); }
.rz-mt-6 { margin-top: var(--rz-s-6); }
.rz-mt-8 { margin-top: var(--rz-s-8); }
.rz-mt-12 { margin-top: var(--rz-s-12); }
.rz-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ═════════════════════════════════════════════════════ 4. RESPONSIVE ═══ */

@media (max-width: 1080px) {
  .rz-foot-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 960px) {
  .rz-nav-links, .rz-nav-end .rz-desk { display: none; }
  .rz-burger { display: grid; }
  .rz-split { grid-template-columns: minmax(0, 1fr); gap: var(--rz-s-10); }
  .rz-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rz-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  body { font-size: var(--rz-fs-base); }
  /* La barre haute ne garde que la marque et le menu — mesuré : au-delà, elle
     déborde. L'appel et le sélecteur de thème passent dans le tiroir, et
     l'appel réapparaît collé en bas. */
  /* LA BARRE HAUTE NE GARDE QUE LA MARQUE, LE THÈME ET LE MENU — mesuré : au-
     delà, elle déborde. L'appel passe dans le tiroir.

     LE SÉLECTEUR DE THÈME EST REVENU DANS LA BARRE, à gauche du menu (demande
     du propriétaire, 08/09). Il avait d'abord été retiré du TIROIR, où il
     repoussait l'appel sous le sélecteur de langue — le défaut était sa place,
     pas son existence. Dans la barre il ne pousse rien, et il reste le seul
     moyen de changer de thème sur téléphone.

     LA PLACE EXISTE PARCE QUE L'APPEL N'EST PLUS LÀ. C'est lui qui faisait
     déborder la barre, pas cette icône de 44 pixels. */
  .rz-nav-end .rz-btn-primary { display: none; }

  /* LE MÊME DESSIN QUE SUR LA PAGE DE CONNEXION, que le propriétaire a
     désigné en exemple : bordure visible et fond de surface. Sur ordinateur
     l'icône vit entre deux boutons et se passe de cadre ; seule sur mobile,
     sans cadre elle ne se lit plus comme un bouton. */
  .rz-nav-end [data-rz-theme] {
    border-color: var(--rz-border);
    background: var(--rz-surface);
  }

  /* ── ET SUR LA BANDE SOMBRE DU HÉROS, C'EST L'INVERSE QU'IL FAUT ────────

     La barre est TRANSPARENTE tant qu'elle flotte au-dessus du héros sombre
     (`data-sur-ink`). Un fond `--rz-surface` y est le blanc du thème clair :
     le bouton devenait une tuile blanche éclatante sur une bande presque
     noire. Vu au rendu, une minute après l'avoir écrit — la règle était juste
     partout sauf à l'endroit exact où l'on regarde en premier, le haut de
     l'accueil.

     Les jetons `--rz-ink-*` existent pour ça : ils habillent ce qui vit SUR la
     bande sombre, quel que soit le thème choisi par le visiteur. */
  .rz-nav[data-sur-ink]:not([data-defile]) .rz-nav-end [data-rz-theme] {
    background: var(--rz-ink-surface);
    border-color: var(--rz-ink-line-strong);
    color: var(--rz-ink-text);
  }
  .rz-grid-2, .rz-grid-3, .rz-grid-4 { grid-template-columns: minmax(0, 1fr); }
  .rz-foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rz-s-8); }
  .rz-card { padding: var(--rz-s-6); }
  .rz-step { padding-inline-start: var(--rz-s-12); }
  .rz-step::before { width: var(--rz-s-10); height: var(--rz-s-10); font-size: var(--rz-fs-lg); }
  .rz-btn-block-sm { width: 100%; }
}

@media (max-width: 480px) {
  .rz-foot-grid { grid-template-columns: minmax(0, 1fr); }
  .rz-wrap { padding-inline: var(--rz-s-4); }
}


/* ═══════════════════════════════════════════ 5. MOTIFS DE VITRINE ═══ */
/*
 * Ces motifs servaient d'abord à l'accueil. Trois autres pages les emploient :
 * les laisser dans le `<style>` de chaque page aurait recréé, en une journée,
 * exactement la dette qu'on vient de solder — quatorze pages redéclarant chacune
 * leur charte. Ce qui sert deux fois vit ici.
 */

.hero { padding-block: var(--rz-s-24) var(--rz-s-20); }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr); gap: var(--rz-s-16); align-items: center; }
.hero h1 { margin-block: var(--rz-s-5) var(--rz-s-6); }
.hero-lead { font-size: var(--rz-fs-lg); color: var(--rz-ink-text-2); max-width: 40ch; }
.hero-lead strong { color: var(--rz-ink-text); font-weight: 600; }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--rz-s-3); margin-top: var(--rz-s-8); }
.hero-trust { display: flex; flex-wrap: wrap; gap: var(--rz-s-5); margin-top: var(--rz-s-8); font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); color: var(--rz-ink-text-2); letter-spacing: .03em; }
.hero-trust span { display: inline-flex; align-items: center; gap: var(--rz-s-2); }

/* La fenêtre de démonstration. Tout son texte est du HTML, jamais du SVG : le
   contenu d'un `<svg>` n'est jamais traduit par le moteur, et cette fenêtre est
   l'argument principal de la page — elle doit exister dans les 30 langues. */
.fenetre { border: 1px solid var(--rz-ink-line); border-radius: var(--rz-r-lg); background: var(--rz-ink-surface); overflow: hidden; box-shadow: var(--rz-shadow-lg); }
.fenetre-barre { display: flex; align-items: center; gap: var(--rz-s-2); padding: var(--rz-s-3) var(--rz-s-4); border-bottom: 1px solid var(--rz-ink-line); }
.fenetre-barre i { width: var(--rz-s-3); height: var(--rz-s-3); border-radius: var(--rz-r-pill); background: var(--rz-ink-line-strong); flex: none; }
.fenetre-url { flex: 1; margin-inline-start: var(--rz-s-2); background: var(--rz-ink-surface); border-radius: var(--rz-r-xs); padding: var(--rz-s-1) var(--rz-s-3); font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); color: var(--rz-ink-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fenetre-corps { padding: var(--rz-s-6); }
.fenetre-corps h3 { font-size: var(--rz-fs-xl); margin-bottom: var(--rz-s-2); }
.fenetre-corps p { font-size: var(--rz-fs-sm); }
.fenetre-prix { font-family: var(--rz-font-display); font-weight: 700; color: var(--rz-sig-b); margin-top: var(--rz-s-3); }
.fenetre-langues { display: flex; flex-wrap: wrap; gap: var(--rz-s-2); margin-top: var(--rz-s-6); padding-top: var(--rz-s-5); border-top: 1px solid var(--rz-ink-line); }
.fenetre-langues span { font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); padding: var(--rz-s-1) var(--rz-s-2); border-radius: var(--rz-r-xs); background: var(--rz-ink-surface); border: 1px solid var(--rz-ink-line); color: var(--rz-ink-text-2); }
.fenetre-langues span.actif { border-color: var(--rz-sig-b); color: var(--rz-ink-text); }

/* La bande de compatibilité. */
.stack { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--rz-s-6) var(--rz-s-10); font-family: var(--rz-font-display); font-weight: 700; font-size: var(--rz-fs-lg); color: var(--rz-text-3); }

/* Le schéma « widget contre serveur ». */
.duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rz-s-6); }
.duo-carte { padding: var(--rz-s-6); border-radius: var(--rz-r-lg); border: 1px solid var(--rz-border); background: var(--rz-surface); }
.duo-carte h3 { font-size: var(--rz-fs-lg); margin-bottom: var(--rz-s-4); display: flex; align-items: center; gap: var(--rz-s-3); }
.duo-mal { border-color: color-mix(in srgb, var(--rz-crit) 30%, var(--rz-border)); }
.duo-bien { border-color: color-mix(in srgb, var(--rz-good) 34%, var(--rz-border)); }
.duo-ligne { font-family: var(--rz-font-mono); font-size: var(--rz-fs-sm); padding: var(--rz-s-3) var(--rz-s-4); border-radius: var(--rz-r-sm); background: var(--rz-surface-2); margin-bottom: var(--rz-s-2); display: flex; align-items: center; justify-content: space-between; gap: var(--rz-s-3); }
.duo-ligne b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; }
.duo-verdict { margin-top: var(--rz-s-5); font-weight: 600; display: flex; align-items: center; gap: var(--rz-s-2); }
.duo-mal .duo-verdict { color: var(--rz-crit-text); }
.duo-bien .duo-verdict { color: var(--rz-good-text); }

/* La grille de prix. */
.paliers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--rz-s-3); }
.palier { border: 1px solid var(--rz-border); border-radius: var(--rz-r-md); background: var(--rz-surface); padding: var(--rz-s-5); }
.palier-nom { font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--rz-text-3); }
.palier-prix { font-family: var(--rz-font-display); font-size: var(--rz-fs-xl); font-weight: 800; margin-block: var(--rz-s-2) var(--rz-s-1); }
.palier-taille { font-size: var(--rz-fs-sm); color: var(--rz-text-2); }

/* L'écosystème. Les noms de classe `ecard` et `etag` sont ceux que VITRINE-01
   attend : il découpe chaque carte sur `<div class="ecard` et lit l'état sur
   `etag on` / `etag soon`. Les renommer rendrait le cliquet aveugle — et un
   cliquet aveugle est vert sur du vide. */
.ecard { position: relative; display: flex; flex-direction: column; gap: var(--rz-s-3); padding: var(--rz-s-6); border: 1px solid var(--rz-border); border-radius: var(--rz-r-lg); background: var(--rz-surface); transition: transform var(--rz-t-base) var(--rz-ease), box-shadow var(--rz-t-base) var(--rz-ease); }
.ecard:hover { transform: translateY(calc(var(--rz-s-1) * -1)); box-shadow: var(--rz-shadow-md); }
/* `ec-tete` et NON `ecard-tete`, et ce n'est pas une préférence.
 * `VITRINE-01` découpe chaque carte en cherchant `<div class="ecard` — un
 * PRÉFIXE. Une classe `ecard-tete` le satisfait aussi, donc le découpage
 * commençait APRÈS l'étiquette d'état, et les cinq produits étaient déclarés
 * mal étiquetés. Le cliquet a été resserré du même coup ; ce nom garde les deux
 * lectures sans ambiguïté. */
.ec-tete { display: flex; align-items: center; gap: var(--rz-s-3); }
/* L'ICÔNE DU PRODUIT. Elle porte sa propre tuile et son propre dégradé — le
   CSS ne pose donc ni fond ni couleur, seulement la taille. Un seul endroit
   décide de l'apparence : `scripts/marque/source.mjs`. */
.ec-icone { width: var(--rz-s-10); height: var(--rz-s-10); display: block; flex: none; }
.ecard h3 { font-size: var(--rz-fs-lg); }
.ecard p { font-size: var(--rz-fs-sm); color: var(--rz-text-2); flex: 1; }
.etag { display: inline-flex; align-items: center; gap: var(--rz-s-2); align-self: flex-start; padding: var(--rz-s-1) var(--rz-s-3); border-radius: var(--rz-r-pill); font-family: var(--rz-font-mono); font-size: var(--rz-fs-xs); }
.etag.on { background: var(--rz-good-soft); color: var(--rz-good-text); }
.etag.soon { background: var(--rz-surface-2); color: var(--rz-text-2); }

@media (max-width: 960px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr); gap: var(--rz-s-10); }
  .hero { padding-block: var(--rz-s-16) var(--rz-s-16); }
  .duo, .paliers { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
  .hero-actions .rz-btn { width: 100%; }
}


/* ══════════════════════════════════════════ 6. ALIAS DE MIGRATION ═══ */
/*
 * ── POURQUOI CE BLOC EXISTE, ET POURQUOI IL EST UNE DETTE ──────────────────
 *
 * Dix pages de `web/` n'ont pas été refaites à blanc : elles encaissent
 * l'argent, délivrent les licences, prouvent la propriété d'un domaine et
 * portent des documents comptables immuables. `platform.html` fait 96 Ko —
 * c'est une application, pas une page. Les réécrire en même temps que le
 * message, ce serait risquer la boutique pour gagner du pixel.
 *
 * Elles portent donc leur propre CSS de composants, écrit avec l'ANCIEN
 * vocabulaire (`--brand`, `--line`, `--g1`…). Ce bloc fait vivre ces noms en
 * les faisant pointer vers les jetons réels.
 *
 * L'ALTERNATIVE ÉTAIT PIRE. Sans lui, il fallait réécrire d'un coup les
 * centaines de déclarations de ces dix pages — c'est-à-dire toucher au tunnel
 * d'achat, à la connexion et à la facturation dans le même commit que la
 * refonte de la vitrine. Une refonte qui casse la production le jour de son
 * adoption ne s'adopte jamais.
 *
 * ── CHAQUE LIGNE EST UNE DETTE, ET ELLE SE COMPTE ─────────────────────────
 *
 * Ce ne sont pas des synonymes qu'on garde : ce sont des emprunts qu'on
 * rembourse, écran par écran, quand plus rien ne les lit. `ALIAS-01`, dans
 * `scripts/ci-checks.mjs`, COMPTE ceux qui restent et refuse que le nombre
 * monte. Un chiffre qui ne baisse jamais est un chiffre qu'on cesse de
 * regarder ; celui-ci ne peut pas grossir en silence.
 *
 * ── LA SEULE CHOSE QUI COMPTE VRAIMENT ICI ────────────────────────────────
 *
 * Aucun alias ne porte de VALEUR. Ils pointent tous vers un jeton `--rz-*`.
 * Le jour où la charte change, elle change à un seul endroit — y compris pour
 * ces dix pages, qui héritent alors de la correction sans être touchées. C'est
 * précisément ce que quatorze blocs `:root` recopiés rendaient impossible.
 */
:root {
  /* Surfaces et texte */
  --bg:             var(--rz-bg);
  --surface:        var(--rz-surface);
  --surface-2:      var(--rz-surface-2);
  --ink:            var(--rz-ink);
  --text:           var(--rz-text);
  --text-1:         var(--rz-text);
  --text-2:         var(--rz-text-2);
  --text-3:         var(--rz-text-3);

  /* Lignes — `--line` et `--border` désignaient la même chose sous deux noms,
     dans deux surfaces différentes. C'est ce doublon qui faisait disparaître
     une bordure quand on copiait un composant d'un écran à l'autre. */
  --border:         var(--rz-border);
  --line:           var(--rz-border);
  --border-strong:  var(--rz-border-strong);

  /* Marque et dégradé signature */
  --brand:          var(--rz-brand);
  --brand-strong:   var(--rz-brand-strong);
  --brand-soft:     color-mix(in srgb, var(--rz-brand) 12%, transparent);
  --g1:             var(--rz-sig-a);
  --g2:             var(--rz-sig-b);

  /* États. `--bad` et `--danger` visaient déjà `--crit` : trois noms pour une
     couleur, sur trois écrans qui ne se relisent pas entre eux. */
  --good:           var(--rz-good);
  --good-soft:      var(--rz-good-soft);
  --warn:           var(--rz-warn);
  --warn-soft:      var(--rz-warn-soft);
  --crit:           var(--rz-crit);
  --bad:            var(--rz-crit);
  --danger:         var(--rz-crit);

  /* Focus, ombres, mesures */
  --ring:           var(--rz-ring);
  --shadow-sm:      var(--rz-shadow-sm);
  --shadow-md:      var(--rz-shadow-md);
  --shadow-lg:      var(--rz-shadow-lg);
  --shadow-glow:    var(--rz-shadow-brand);
  --wrap:           var(--rz-wrap);

  /* Polices */
  --display:        var(--rz-font-display);
  --sans:           var(--rz-font-sans);
  --mono:           var(--rz-font-mono);
}

/* ═════════════════════════════════════════════════════ 7. IMPRESSION ═══ */

@media print {
  .rz-nav, .rz-drawer, .rz-foot, .rz-aurore, .rz-skip { display: none !important; }
  body { background: #FFF; color: #000; font-size: 11pt; }
  .rz-sec { padding-block: 1rem; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #555; }
}

/* ══════════════════════════════════════════════════════════════════════════
 * LES ANIMATIONS — SOBRES, ET QUI NE PEUVENT PAS CACHER LA PAGE.
 *
 * ── LA RÈGLE QUI GOUVERNE TOUT LE BLOC ────────────────────────────────────
 *
 * Une apparition au défilement se construit en cachant du contenu puis en le
 * révélant. Le jour où le script ne tourne pas — fichier non chargé, erreur en
 * amont, navigateur ancien —, le contenu reste caché POUR TOUJOURS. C'est le
 * défaut classique de cette technique, et il transforme une page qui vend en
 * page blanche.
 *
 * Trois verrous, et il en faut trois :
 *
 *   1. Rien n'est caché tant que `data-rz-anime` n'est pas posé sur `<html>` ;
 *   2. cet attribut n'est posé QUE par le script d'en-tête, qui vérifie d'abord
 *      que le visiteur accepte le mouvement et que son navigateur sait observer
 *      le défilement ;
 *   3. ce même script arme une minuterie de trois secondes qui le RETIRE, et
 *      que `rz.js` désamorce en prenant le relais. Si `rz.js` ne vient jamais,
 *      la page réapparaît d'elle-même.
 *
 * ── ET LE HÉROS N'EST JAMAIS CACHÉ ────────────────────────────────────────
 *
 * Il est déjà à l'écran au chargement. Le cacher pour le révéler ne se lit pas
 * comme une animation, mais comme une page qui met du temps à s'afficher.
 * ══════════════════════════════════════════════════════════════════════════ */

:root[data-rz-anime] .rz-sec > .rz-wrap > *,
:root[data-rz-anime] .rz-sec-sm > .rz-wrap > *,
:root[data-rz-anime] .ecard,
:root[data-rz-anime] .rz-card {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .55s var(--rz-ease), transform .55s var(--rz-ease);
}
:root[data-rz-anime] .hero *,
:root[data-rz-anime] .hero {
  opacity: 1 !important;
  transform: none !important;
}
/* ── LA RÉVÉLATION REPREND LES MÊMES SÉLECTEURS, ET CE N'EST PAS BAVARD ────
 *
 * Une simple `.rz-vu` ne suffit PAS : la règle qui cache porte
 * `.rz-sec > .rz-wrap > *`, donc deux classes de plus. Elle l'emporte, et le
 * contenu reste invisible alors que le script a bien fait son travail.
 *
 * Mesuré le 08/09 : 22 blocs marqués `rz-vu` et pourtant à opacité zéro après
 * un défilement complet. Le défaut ne se voyait ni dans le script — correct —
 * ni dans le HTML — correct : uniquement à l'écran. On aurait pu forcer avec
 * `!important` ; on préfère répéter les sélecteurs, parce qu'un `!important`
 * posé ici rendrait impossible toute exception plus tard. */
:root[data-rz-anime] .rz-sec > .rz-wrap > .rz-vu,
:root[data-rz-anime] .rz-sec-sm > .rz-wrap > .rz-vu,
:root[data-rz-anime] .ecard.rz-vu,
:root[data-rz-anime] .rz-card.rz-vu {
  opacity: 1;
  transform: none;
}

/* ── L'ENCADRÉ DE DÉMONSTRATION, QUI CHANGE DE LANGUE ──────────────────────
 *
 * Il montrait une seule page allemande, figée. Il en montre maintenant
 * plusieurs, l'une après l'autre : c'est la démonstration du produit sur le
 * produit lui-même, et c'est le seul endroit du site où une animation APPREND
 * quelque chose au visiteur plutôt que de le distraire.
 *
 * Fondu, jamais glissement : un texte qui glisse attire l'œil sur le
 * mouvement ; un texte qui se fond laisse lire ce qui arrive. */
.fenetre-texte {
  transition: opacity .4s var(--rz-ease);
}
.fenetre-texte[data-fondu] { opacity: 0; }

.fenetre-url { transition: opacity .4s var(--rz-ease); }
.fenetre-url[data-fondu] { opacity: 0; }

/* La pastille active glisse d'une langue à l'autre — ici le mouvement est le
   propos, puisqu'il montre qu'on change de langue. */
.fenetre-langues span {
  transition: background var(--rz-t-base) var(--rz-ease),
              color var(--rz-t-base) var(--rz-ease),
              border-color var(--rz-t-base) var(--rz-ease);
}

/* ── LE MOUVEMENT SE COUPE ENTIÈREMENT SI LE VISITEUR LE DEMANDE ───────────
 *
 * Ce n'est pas une politesse : pour une partie des gens, une animation déclenche
 * un vrai malaise physique. La règle couvre TOUT le fichier, y compris ce qui
 * sera ajouté après — d'où le sélecteur universel plutôt qu'une liste. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  :root[data-rz-anime] .rz-sec > .rz-wrap > *,
  :root[data-rz-anime] .rz-sec-sm > .rz-wrap > *,
  :root[data-rz-anime] .ecard,
  :root[data-rz-anime] .rz-card { opacity: 1; transform: none; }
}

/* TACTILE-01 (27/09) — sur un écran TACTILE, toute cible fait au moins 24 px
   (WCAG 2.2, critère 2.5.8). Mesuré à 375 px : cases à cocher de 13 px, liens
   « Retour au site » ou « Oublié ? » de 17 à 19 px. Rien ne change à la souris.
   `min-height` n'agit pas sur un lien DANS une phrase (boîte en ligne) : c'est
   voulu, la norme l'en dispense. */
@media (pointer: coarse) {
  /* `!important` et `flex: none` : une page qui fixe ses cases à 16 px, ou une
     ligne flexible qui les écrase à 14, passeraient sinon sous le plancher. */
  input[type=checkbox], input[type=radio] { width: 24px !important; height: 24px !important; flex: none; }
  a, button, [role=button], [role=switch], select,
  input:not([type=checkbox]):not([type=radio]):not([type=hidden]) { min-height: 24px; }
}
