/*
 * Le site vitrine d'amidami.app — feuille unique, aucune dépendance.
 *
 * ## Les jetons viennent de l'app, pas d'un nuancier neuf
 *
 * Ils sont portés depuis `amidami-mobile/lib/shared/theme/amidami_palette.dart`, palettes
 * `amidami` (sombre, le défaut) et `clair`. Les mêmes noms, donc les mêmes RÔLES : `--texte`
 * est l'encre du fond d'écran, `--encre-card` celle d'une surface claire, et l'une n'est
 * jamais posée à la place de l'autre. Le nom d'un jeton dit son rôle, pas sa teinte —
 * `--texte` est sombre en thème clair.
 *
 * Trois interdits déjà payés côté app, qui valent ici à l'identique :
 *  - `--marque` est un REMPLISSAGE, jamais une encre : sur les fonds sombres il tombe très
 *    au-dessous de 4,5:1 ;
 *  - `--texte-muet` est calibré pour les fonds SOMBRES (8,67:1 sur `--fond`) et tombe à
 *    1,5:1 sur `--card` : sur une surface claire, c'est `--encre-card-muette` ;
 *  - la hiérarchie se fait par la forme et la taille, jamais par l'opacité : une encre à
 *    70 % passe sous le seuil sans que rien ne le signale.
 *
 * Tous les couples introduits ici sont mesurés : ≥ 4,5:1 pour du texte, ≥ 3:1 pour la
 * bordure d'un CONTRÔLE (`--contour-action`, et pas `--bordure` qui est décorative — la
 * surface porte déjà la limite de la carte).
 *
 * ## Ce que cette feuille n'a pas
 *
 * Aucune police distante, aucun CDN, aucun script, aucun traceur. Les deux familles sont
 * dans `assets/fonts/`, sous OFL, licences comprises.
 */

/* ── Polices ─────────────────────────────────────────────────────────────────── */

/*
 * Plus Jakarta Sans est une police VARIABLE : une seule déclaration couvre 200→800, le
 * navigateur interpole sur l'axe `wght`. Déclarer des instances statiques par poids ferait
 * télécharger le même fichier plusieurs fois.
 *
 * Fraunces n'est ici qu'en Black (900) : c'est le seul poids que l'app embarque, et c'est
 * volontaire — elle ne sert qu'aux titres. Ne lui demande pas un 400 : le navigateur
 * synthétiserait un faux maigre, très laid sur un serif.
 *
 * Les deux sont SOUS-ENSEMBLÉES (latin + latin étendu, ponctuation générale, €, flèches) et
 * servies en **WOFF2** : 176 → 41 Ko pour Jakarta, 36 → 17 Ko pour Fraunces. Le sous-ensemble
 * fait l'essentiel du gain, pas le conteneur — Caddy comprimait déjà les TTF. Les TTF
 * d'origine restent dans le dépôt, hors de `public/` (`apps/site/polices-source/`) : ils
 * servent au générateur des visuels de store, et à refaire ce sous-ensemble. La commande
 * exacte est dans `docs/SEO.md` § « Polices ». **Ajouter un caractère hors du sous-ensemble
 * ne produit aucune erreur** : le glyphe part sur la police système, et seul l'œil le voit.
 */
@font-face {
  font-family: 'Plus Jakarta Sans';
  src: url('fonts/PlusJakartaSans-Variable.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Fraunces';
  src: url('fonts/Fraunces-Black.woff2') format('woff2');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

/* ── Jetons ──────────────────────────────────────────────────────────────────── */

:root {
  color-scheme: dark light;

  /* Palette `amidami` (sombre) — le défaut, comme dans l'app. */
  --fond: #130347;
  --degrade-fin: #3e2670;
  --surface: #1e0a52;
  --surface-alt: #2a1466;
  --bordure: #3d2a6b;
  --card: #e5d4ff;
  --card-soft: #f9f5ff;
  --encre-card: #130347;
  --encre-card-muette: #4a3c73;
  --texte: #fdfcfc;
  --texte-muet: #b9a9e0;
  --primaire: #f9f5ff;
  --sur-primaire: #320665;
  --marque: #8030ed;
  --accent: #b98bfe;
  --succes: #22c55e;

  /*
   * Bordure d'un CONTRÔLE, distincte de `--bordure`. Elle vaut `--texte-muet`, donc
   * 8,67:1 sur `--fond` et 5,72:1 en bas du dégradé : au-dessus des 3:1 exigés pour
   * délimiter un élément d'interface. `--bordure` (1,5:1) ne convient qu'à un trait
   * décoratif, là où la surface porte déjà la limite.
   */
  --contour-action: #b9a9e0;

  /*
   * L'ENCRE D'UN LIEN dans le corps du texte — et le second jeton propre au site, pour
   * la même raison que `--contour-action` : l'app n'a pas ce besoin.
   *
   * `--accent` était l'encre des liens, et il tenait 4,77:1 sur le bas du dégradé. Avec
   * l'aurore, le mélange le plus clair mesuré à l'écran est `#432a75` (viewport 800 ×
   * 1200, taches partiellement superposées) et `--accent` y tombe à **4,48:1** — sous le
   * seuil, de deux centièmes. Ce n'est pas une raison d'affaiblir l'aurore : ses alphas
   * sont ceux de l'app, mesurés pour `--texte-muet` (5,37:1 sur ce même pire cas), et
   * l'écran d'accueil de l'app ne porte pas de lien en `accent` sur son aurore. C'est
   * donc l'encre du LIEN qui monte d'un cran : `#c19aff` donne 5,11:1 sur le pire
   * mélange, 8,25:1 sur `--fond`, 6,10:1 sur le verre.
   *
   * `--accent` reste l'anneau de focus (3:1 suffisent pour un élément d'interface) et
   * une teinte décorative. Sur une surface CLAIRE — `.encart--fort` — un lien s'écrit
   * en `--encre-card` : ici `--lien` ne donnerait que 1,63:1.
   */
  --lien: #c19aff;

  /* Le châssis d'un téléphone ne suit aucun thème : un appareil est sombre. */
  --chassis: #0b0714;
  --chassis-liseré: rgba(255, 255, 255, 0.14);

  /*
   * CE QUI SE POSE SUR UNE CAPTURE NE SUIT PAS LE THÈME — c'est la règle `onMedia` de
   * l'app, et elle a été payée ici.
   *
   * La démonstration du geste dessine ses pastilles PAR-DESSUS une capture de l'accueil,
   * et cette capture est celle de la palette sombre : elle reste sombre quand le visiteur
   * a un système en thème clair. Des jetons qui suivent le thème y donnent donc des
   * chiffres faux, et mesurés : en thème clair, le remplissage vert foncé de la palette
   * claire ne tenait que **2,47:1** sur la capture (le seuil d'un élément d'interface est
   * 3:1), le rouge 2,39, l'ambre 2,50 — et l'encre atténuée du compteur tombait à
   * **2,31:1** au lieu de 7,85. Rien à l'écran ne le signale tant qu'on regarde la page
   * en thème sombre, c'est-à-dire par défaut.
   *
   * Les valeurs sont donc celles de la palette `amidami`, figées. Les encres sont celles
   * que `palette.inkOn(fond)` choisirait — la plus contrastée des deux — et sur ces trois
   * remplissages CLAIRS c'est l'encre sombre. Le blanc, lui, ne donne que 2,28:1 sur le
   * vert : un commentaire de la version précédente annonçait 4,54:1, et c'est exactement
   * ce qui l'a fait survivre à une relecture — un chiffre faux dans un commentaire fait
   * autorité, personne ne remesure ce qui est déjà écrit.
   *
   * Mesuré au pixel sur la page servie : glyphe et libellé à 8,14:1 sur le vert, 11,12
   * sur l'ambre, **4,93 sur le rouge** — le plus serré des trois, donc celui à remesurer
   * si une teinte bouge. Compteur à 7,85:1 sur le dégradé d'écran de la capture.
   */
  --capture-succes: #22c55e;
  --capture-danger: #ef4444;
  --capture-avertissement: #fbbf24;
  --capture-encre-reponse: #130347;
  --capture-encre-muette: #b9a9e0;

  /*
   * L'AURORE — et ses alphas ne sont pas un choix esthétique.
   *
   * Ce sont exactement ceux de `AuroraBackground.tints` dans l'app
   * (`lib/shared/widgets/aurora_background.dart`) : 0,35 sur `gradientEnd`, 0,18 sur
   * `brand`, 0,06 sur `accent` en palette sombre. Ils y ont été MESURÉS pour que le
   * mélange le plus clair — les trois taches empilées sur le haut du dégradé — garde
   * `--texte-muet` à 4,5:1. Les monter éclaircit le mélange, donc fait BAISSER le
   * contraste d'une encre claire : à 0,35 partout, l'app mesurait 2,75:1.
   *
   * Le bord s'éteint vers la MÊME teinte à alpha nul, jamais vers `transparent` : un
   * fondu vers du noir transparent assombrit à mi-course dans les moteurs qui
   * n'interpolent pas en prémultiplié. C'est la même précaution que le shader de l'app.
   */
  --aurore-1: rgba(62, 38, 112, 0.35);
  --aurore-1-nul: rgba(62, 38, 112, 0);
  --aurore-2: rgba(128, 48, 237, 0.18);
  --aurore-2-nul: rgba(128, 48, 237, 0);
  --aurore-3: rgba(185, 139, 254, 0.06);
  --aurore-3-nul: rgba(185, 139, 254, 0);

  /*
   * LE VERRE, réservé à la navigation et à UNE carte de section.
   *
   * Un `backdrop-filter` coûte une passe de rendu par frame sur sa surface, et il la
   * recalcule dès que ce qu'il y a dessous bouge — or l'aurore bouge en continu. Deux
   * ou trois éléments par page, jamais un par carte : les encarts des pages légales
   * restent donc sur `--surface`, opaque.
   *
   * Comme le verre de la WheelNav, il est SOMBRE sur une palette sombre : ses encres
   * sont `--texte` / `--texte-muet`, jamais `--encre-card`. Un `--encre-card-muette`
   * posé sur du verre donne 1,8:1 — le piège a été payé deux fois côté app.
   */
  --verre: rgba(30, 10, 82, 0.62);
  --verre-liseré: rgba(255, 255, 255, 0.1);
  --verre-flou: 14px;

  --rayon: 16px;
  --rayon-lg: 24px;
  --rayon-pilule: 999px;
  --cible: 44px;

  /*
   * LE MOUVEMENT — les jetons de `AppMotion` (`lib/shared/theme/app_motion.dart`), qui
   * ne fait lui-même que NOMMER les valeurs de Material 3. Les mêmes noms, donc les
   * mêmes rôles : `--entree` pour ce qui arrive (part vite, se pose doucement),
   * `--depart` pour ce qui s'en va, `--standard` pour un changement d'état sur place.
   *
   * Les trois règles de l'app valent ici :
   *  - rien ne dépasse `--long` (450 ms) : au-delà, on attend l'interface ;
   *  - aucune opacité animée sur un grand aplat — un `opacity` ouvre une couche par
   *    frame, et sur une carte de verre il fait rejouer le flou avec elle ;
   *  - `prefers-reduced-motion` met tout à ZÉRO, pas « plus vite ».
   */
  --duree: 200ms;
  --moyen: 300ms;
  --long: 450ms;
  --entree: cubic-bezier(0.05, 0.7, 0.1, 1);
  --depart: cubic-bezier(0.3, 0, 0.8, 0.15);
  --standard: cubic-bezier(0.2, 0, 0, 1);

  --pas-xs: 4px;
  --pas-sm: 8px;
  --pas-md: 12px;
  --pas-lg: 16px;
  --pas-xl: 24px;
  --pas-2xl: 32px;
  --pas-3xl: 48px;
  --pas-4xl: 72px;
  --pas-5xl: 104px;

  --largeur: 1120px;
  --largeur-texte: 68ch;
}

/*
 * Thème clair : la palette `clair` de l'app, dont les couples sont déjà vérifiés par
 * `test/shared/theme/contrast_test.dart`. On redéfinit les JETONS, jamais les règles :
 * une couleur qui n'aurait sa seule définition qu'ici serait absente en sombre.
 *
 * `--chassis` et son liseré n'y figurent pas, à dessein.
 */
@media (prefers-color-scheme: light) {
  :root {
    --fond: #f4f1fb;
    --degrade-fin: #e6dff7;
    --surface: #ffffff;
    --surface-alt: #ede7f8;
    --bordure: #cbc0e2;
    --card: #ffffff;
    --card-soft: #f6f2fd;
    --encre-card: #1a1230;
    --encre-card-muette: #5b5177;
    --texte: #1a1230;
    --texte-muet: #5b5177;
    --primaire: #6929d4;
    --sur-primaire: #ffffff;
    --marque: #6929d4;
    --accent: #5b21b6;
    --succes: #106935;
    --contour-action: #5b5177;
    /* En thème clair, `--accent` tient déjà 6,36:1 sur le mélange le plus SOMBRE de
       l'aurore (`#dfd4f4`) : rien à relever, le lien garde la teinte de la palette. */
    --lien: #5b21b6;

    /* Les alphas de la palette CLAIRE de l'app : 0,30 / 0,10 / 0,04. Plus bas, parce
       qu'ici c'est une encre SOMBRE qui doit survivre au mélange, et que `--marque` et
       `--accent` y sont des violets foncés qui la mangent. */
    --aurore-1: rgba(230, 223, 247, 0.3);
    --aurore-1-nul: rgba(230, 223, 247, 0);
    --aurore-2: rgba(105, 41, 212, 0.1);
    --aurore-2-nul: rgba(105, 41, 212, 0);
    --aurore-3: rgba(91, 33, 182, 0.04);
    --aurore-3-nul: rgba(91, 33, 182, 0);

    --verre: rgba(255, 255, 255, 0.66);
    --verre-liseré: rgba(26, 18, 48, 0.1);
  }
}

/* Respecter le réglage système, y compris pour le défilement piloté par ancre. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duree: 0ms;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── Socle ───────────────────────────────────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--fond);
  color: var(--texte);
  font-family:
    'Plus Jakarta Sans',
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    Roboto,
    Arial,
    sans-serif;
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

/*
 * ── L'AURORE ──────────────────────────────────────────────────────────────────
 *
 * Le fond vivant de l'app, porté au CSS : le dégradé d'écran plus trois taches de
 * dégradé radial qui dérivent lentement. C'est le même fond que
 * `AuroraBackground` (`lib/shared/widgets/aurora_background.dart`), aux mêmes teintes,
 * aux mêmes alphas, avec les mêmes ancres et le même rayon.
 *
 * ## Ce que ça coûte, et pourquoi si peu
 *
 * Trois éléments, et la SEULE propriété animée est `transform` : le navigateur promeut
 * chaque tache en couche composée et n'a plus qu'à la déplacer, sans repeindre ni le
 * fond ni le contenu. Aucune opacité animée, aucun `filter: blur` (le dégradé radial
 * fait le flou par construction, gratuitement), aucun changement de mise en page.
 * `will-change: transform` le déclare. `overflow: hidden` évite que les taches, qui
 * dépassent largement la fenêtre, allongent le document.
 *
 * Le calque est en `fixed` et non en fond de `body` : sur une page longue, un
 * `linear-gradient` de body s'étire sur toute la hauteur du document et sa transition
 * ne se voit plus.
 *
 * ## Ancres, orbites, périodes
 *
 * Les trois ancres sont celles de l'app — (15 %, 20 %), (85 %, 35 %), (50 %, 90 %) —
 * et l'orbite fait ±10 % de largeur sur ±8 % de hauteur, comme `AuroraPainter`. Le
 * rayon vaut 0,62 × le plus petit côté, d'où un diamètre de `124vmin`.
 *
 * Trois différences avec l'app, toutes délibérées : les périodes sont plus LONGUES
 * (24 à 34 s contre 14 s), parce qu'une page se lit et ne se manipule pas — un fond
 * qui bouge vite derrière un paragraphe se remarque, et se remarquer est exactement ce
 * qu'un fond ne doit pas faire ; elles sont toutes trois DIFFÉRENTES, donc le motif ne
 * se répète jamais à l'identique ; et le sens alterne, parce que deux taches qui
 * tournent dans le même sens semblent liées.
 *
 * Le premier et le dernier cran sont IDENTIQUES. Ce n'est pas de la coquetterie :
 * `prefers-reduced-motion` coupe l'animation, et une image de fin différente de
 * l'image de départ ferait sauter le fond au moment précis où on demande qu'il ne
 * bouge plus.
 */
.aurore {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(175deg, var(--fond) 0%, var(--degrade-fin) 100%);
}

.aurore__tache {
  position: absolute;
  width: 124vmin;
  height: 124vmin;
  border-radius: 50%;
  will-change: transform;
  animation: derive 28s linear infinite;
}

.aurore__tache--1 {
  left: 15%;
  top: 20%;
  background: radial-gradient(circle closest-side, var(--aurore-1), var(--aurore-1-nul));
  animation-duration: 28s;
}

.aurore__tache--2 {
  left: 85%;
  top: 35%;
  background: radial-gradient(circle closest-side, var(--aurore-2), var(--aurore-2-nul));
  animation-duration: 34s;
  animation-direction: reverse;
  /* Un délai NÉGATIF démarre l'animation en cours de route : les trois taches ne sont
     jamais au même endroit de leur orbite, comme les déphasages de `AuroraPainter`. */
  animation-delay: -11s;
}

.aurore__tache--3 {
  left: 50%;
  top: 90%;
  background: radial-gradient(circle closest-side, var(--aurore-3), var(--aurore-3-nul));
  animation-duration: 24s;
  animation-delay: -17s;
}

@keyframes derive {
  0% {
    transform: translate(-50%, -50%) translate(10vw, 0);
  }
  25% {
    transform: translate(-50%, -50%) translate(0, 8vh);
  }
  50% {
    transform: translate(-50%, -50%) translate(-10vw, 0);
  }
  75% {
    transform: translate(-50%, -50%) translate(0, -8vh);
  }
  100% {
    transform: translate(-50%, -50%) translate(10vw, 0);
  }
}

/*
 * ── LE VERRE ─────────────────────────────────────────────────────────────────
 *
 * Le préfixe `-webkit-` n'est pas une relique : Safari, y compris sur iOS 18, ne
 * connaît que celui-là. Sans lui, le verre est simplement un aplat translucide sur
 * tous les iPhone — pas une erreur, juste le fond qu'on croyait flouté.
 */
.verre {
  background: var(--verre);
  -webkit-backdrop-filter: blur(var(--verre-flou)) saturate(150%);
  backdrop-filter: blur(var(--verre-flou)) saturate(150%);
  box-shadow: inset 0 0 0 1px var(--verre-liseré);
}

h1,
h2,
h3,
h4,
p,
ul,
ol,
dl,
figure,
blockquote {
  margin: 0;
}

ul,
ol {
  padding-left: 1.25em;
}

img,
svg {
  max-width: 100%;
  display: block;
}

/*
 * `hidden` doit rester EFFECTIF — la règle juste au-dessus le lui retirait.
 *
 * `[hidden] { display: none }` vient de la feuille du NAVIGATEUR, et une règle d'auteur la
 * bat quelle que soit sa spécificité : un sélecteur de type (`img`) suffit. Le
 * `display: block` posé sur toute image rendait donc `carte.hidden = true` sans aucun
 * effet, et c'est le défaut que le propriétaire a vu en production le 8 septembre 2026 : la
 * carte tranchée restait peinte, revenait à la transformation de sa classe dès que le
 * script effaçait ses styles en ligne — `scale(0.92)` pour la seconde, donc « décalée » —
 * et disparaissait 1,2 s plus tard, au rechargement du paquet.
 *
 * Rien ne le signale : l'attribut est bien posé, l'inspecteur le montre dans le DOM, et il
 * ne cache rien. Le piège attend toute image, tout SVG et tout élément qu'un reset dote
 * d'un `display` — donc la parade est générale et non locale à la démonstration.
 */
[hidden] {
  display: none !important;
}

a {
  color: var(--lien);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-thickness: 2px;
}

/*
 * Le focus se voit, et il se voit pareil partout : c'est la seule règle d'accessibilité
 * qu'on ne rattrape pas composant par composant.
 */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--pas-xs);
}

:focus:not(:focus-visible) {
  outline: none;
}

/* Un lien d'évitement invisible tant qu'il n'a pas le focus, jamais `display: none` :
   un lecteur d'écran doit pouvoir l'atteindre. */
.evitement {
  position: absolute;
  left: var(--pas-lg);
  top: var(--pas-lg);
  z-index: 20;
  padding: var(--pas-sm) var(--pas-lg);
  border-radius: var(--rayon-pilule);
  background: var(--primaire);
  color: var(--sur-primaire);
  font-weight: 700;
  text-decoration: none;
  transform: translateY(-200%);
}

.evitement:focus {
  transform: translateY(0);
}

/* ── Gabarit ─────────────────────────────────────────────────────────────────── */

.conteneur {
  width: 100%;
  max-width: var(--largeur);
  margin: 0 auto;
  padding-inline: var(--pas-xl);
}

.bande {
  padding-block: var(--pas-4xl);
}

.bande__titre {
  max-width: 28ch;
}

@media (min-width: 900px) {
  .bande {
    padding-block: var(--pas-5xl);
  }
}

/* ── Titres ──────────────────────────────────────────────────────────────────── */

/*
 * Fraunces Black pour l'affichage, comme `displayLarge` dans l'app — c'est sa signature,
 * on la garde. Plus Jakarta Sans pour tout le reste. La hiérarchie se fait par la TAILLE
 * et le poids : aucun titre ne dépend d'une couleur pour se distinguer.
 */
.affichage {
  font-family: 'Fraunces', Georgia, 'Times New Roman', serif;
  font-weight: 900;
  line-height: 1.03;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

h1.affichage {
  font-size: clamp(2.4rem, 8.5vw, 4.25rem);
}

h2.affichage {
  font-size: clamp(1.75rem, 4.6vw, 2.6rem);
}

.surtitre {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--texte-muet);
}

.chapeau {
  font-size: clamp(1.0625rem, 2.3vw, 1.3rem);
  line-height: 1.5;
  color: var(--texte-muet);
  max-width: 42ch;
  text-wrap: pretty;
}

/* ── En-tête ─────────────────────────────────────────────────────────────────── */

/*
 * La barre reste en haut, et elle est en verre — comme la WheelNav de l'app.
 *
 * C'est le seul `backdrop-filter` de toutes les pages sauf l'accueil, qui en a deux. Un
 * verre COLLANT posé sur un fond qui bouge est la combinaison la plus chère du site :
 * le flou est recalculé à chaque frame de l'aurore. Il est donc borné à une barre de
 * 64 px de haut, et le flou à 14 px. Ne pas en ajouter un troisième sans mesurer.
 */
/*
 * COLLANTE À PARTIR DE 900 px SEULEMENT.
 *
 * Sur un téléphone, la barre tient sur trois rangées — logo, ancres, langues et « Aide » —
 * soit environ 150 px. Collée, elle mangerait 18 % d'un écran de 844 en permanence, et
 * pour un site où l'on lit des textes légaux c'est le pire endroit où prendre de la
 * place. Les cinq liens sont de toute façon dans le pied de page. Relevé à l'écran sur
 * une capture de 390 × 844.
 */
.entete {
  z-index: 10;
  padding-block: var(--pas-md);
}

@media (min-width: 900px) {
  .entete {
    position: sticky;
    top: 0;
  }
}

.entete__verre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pas-md);
  flex-wrap: wrap;
  /* Le verre vit sur le conteneur, pas sur la barre : au-delà de 1120 px la page a des
     marges, et une barre pleine largeur y ferait une bande qui coupe l'aurore en deux. */
  padding: var(--pas-sm) var(--pas-lg);
  border-radius: var(--rayon-pilule);
  background: var(--verre);
  -webkit-backdrop-filter: blur(var(--verre-flou)) saturate(150%);
  backdrop-filter: blur(var(--verre-flou)) saturate(150%);
  box-shadow: inset 0 0 0 1px var(--verre-liseré);
}

@media (min-width: 1180px) {
  .entete__verre {
    /*
     * La pilule dépasse de 8 px la grille de 1120 px, de chaque côté : sans ce
     * débordement, son liseré vient effleurer le mot « AmidAmi » et la flèche d'« Aide ».
     *
     * C'est la LARGEUR qu'on élargit, jamais la marge. `.conteneur` centre par
     * `margin: 0 auto`, et un `margin-inline` négatif ÉCRASE ces deux `auto` : la pilule
     * se collait alors au bord gauche de la fenêtre — mesuré `left: -8` pour 1120 px de
     * large dans une fenêtre de 1440, donc 328 px de vide à droite et un logo décalé de
     * l'aplomb du titre juste dessous. Rien ne le signale : la barre reste jolie, elle
     * n'est simplement plus alignée sur la page.
     *
     * Le retrait intérieur compense d'autant, pour que le logo reste à l'aplomb du
     * contenu — 8 px de plus de verre à l'extérieur, pas 8 px de moins de marge dedans.
     */
    max-width: calc(var(--largeur) + 2 * var(--pas-sm));
    padding-inline: calc(var(--pas-xl) + var(--pas-sm));
  }
}

.marque {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-md);
  color: var(--texte);
  text-decoration: none;
  font-weight: 800;
  font-size: 1.125rem;
  letter-spacing: -0.01em;
}

.marque svg {
  width: 34px;
  height: 34px;
  flex: none;
  /* La marque hérite l'encre de la page : claire sur sombre, sombre sur clair. */
  color: var(--texte);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--pas-md);
  flex-wrap: wrap;
  font-size: 0.9375rem;
}

/*
 * Les ancres de l'accueil : un soulignement qui POUSSE au survol, pas un
 * `text-decoration` qui apparaît d'un coup. `transform: scaleX` sur un trait de 2 px
 * est composé, donc gratuit.
 */
.nav__lien {
  position: relative;
  color: var(--texte-muet);
  text-decoration: none;
  padding: var(--pas-sm) var(--pas-xs);
  min-height: var(--cible);
  display: inline-flex;
  align-items: center;
  transition: color var(--duree) ease;
}

.nav__lien::after {
  content: '';
  position: absolute;
  left: var(--pas-xs);
  right: var(--pas-xs);
  bottom: 6px;
  height: 2px;
  border-radius: 2px;
  background: currentcolor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duree) ease;
}

.nav__lien:hover {
  color: var(--texte);
}

.nav__lien:hover::after {
  transform: scaleX(1);
}

/*
 * « AIDE » — la seule entrée qui QUITTE la page.
 *
 * « La soirée » et « Télécharger » sont des ancres : elles font défiler. « Aide » ouvre
 * une autre page. Sous le même style, les trois se valent et le visiteur ne sait pas
 * laquelle lui fera perdre sa place. D'où une pastille bordée, une flèche sortante, et
 * la dernière position — après le sélecteur de langue, donc détachée du groupe des
 * ancres. La distinction est portée par la FORME, pas par une couleur ni une opacité.
 */
.nav__aide {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 var(--pas-md) 0 var(--pas-lg);
  border-radius: var(--rayon-pilule);
  box-shadow: inset 0 0 0 1.5px var(--contour-action);
  color: var(--texte);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--duree) ease,
    color var(--duree) ease;
}

.nav__aide svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.nav__aide:hover,
.nav__aide[aria-current='page'] {
  background: var(--primaire);
  color: var(--sur-primaire);
  box-shadow: inset 0 0 0 1.5px var(--primaire);
}

/*
 * LE SÉLECTEUR DE LANGUE — un menu compact, pas trois pastilles.
 *
 * Il occupait la moitié de la barre avec « Français English Español », et sur un écran de
 * 390 px il poussait l'en-tête à TROIS rangées. Ce n'est pas la fonctionnalité du produit :
 * il tient donc en une pastille « globe + FR + chevron », et la liste ne s'ouvre qu'à la
 * demande.
 *
 * `<details>` natif : l'ouverture ne coûte pas une ligne de script, donc elle survit à un
 * `site.js` qui ne charge pas, et le navigateur expose déjà l'état déplié du `<summary>`.
 * Le script n'ajoute que la fermeture au clic dehors et à Échap.
 *
 * Les noms de langues restent écrits dans la liste, chacun dans sa propre langue
 * (« Français », « English », « Español » ne se traduisent jamais). Le code ISO seul serait
 * ambigu — « ES » se lit « espagnol » en France et « España » ailleurs — il n'est donc que
 * le RÉSUMÉ, jamais le libellé du choix.
 */
.langues {
  position: relative;
}

.langues__resume {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-xs);
  min-height: 34px;
  padding: 0 var(--pas-sm) 0 var(--pas-md);
  border-radius: var(--rayon-pilule);
  color: var(--texte-muet);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  /* Le marqueur par défaut est un triangle qu'on ne peut pas placer : le chevron est un
     SVG, à la même graphie que la flèche d'« Aide ». */
  list-style: none;
  /* Repli d'abord : `color-mix` n'existe pas avant Chrome 111 / Safari 16.2, et un fond
     absent laisserait la pastille sans limite visible. */
  background: rgba(255, 255, 255, 0.08);
  background: color-mix(in srgb, var(--texte) 8%, transparent);
  transition: color var(--duree) ease;
}

.langues__resume::-webkit-details-marker {
  display: none;
}

.langues__resume:hover {
  color: var(--texte);
}

.langues__globe {
  width: 15px;
  height: 15px;
  flex: none;
}

.langues__chevron {
  width: 14px;
  height: 14px;
  flex: none;
  transition: transform var(--duree) var(--standard);
}

.langues[open] .langues__resume {
  color: var(--texte);
}

.langues[open] .langues__chevron {
  transform: rotate(180deg);
}

/*
 * La liste dépliée. Fond OPAQUE (`--surface`) et non du verre : le site s'en tient à deux
 * `backdrop-filter` par page — la barre et la carte « ce qu'AmidAmi ne fait pas » — et un
 * troisième posé au-dessus du premier ferait flouter un flou.
 *
 * `right: 0` plutôt que `left` : la pastille vit à droite de la barre, donc une liste
 * alignée à gauche sortirait de l'écran sur un téléphone.
 */
.langues__liste {
  position: absolute;
  z-index: 20;
  top: calc(100% + var(--pas-sm));
  right: 0;
  display: grid;
  gap: 2px;
  min-width: 11rem;
  max-width: 15rem;
  padding: var(--pas-xs);
  border-radius: var(--rayon);
  background: var(--surface);
  box-shadow:
    inset 0 0 0 1px var(--bordure),
    0 18px 40px rgba(0, 0, 0, 0.35);
}

.langues__lien {
  display: flex;
  align-items: center;
  /* La note « page en français seulement » passe à la ligne : sans ce `wrap`, le
     `white-space: nowrap` la garderait sur une seule ligne et pousserait le menu hors de
     l'écran à 390 px. */
  flex-wrap: wrap;
  gap: var(--pas-md);
  min-height: var(--cible);
  padding: var(--pas-xs) var(--pas-md);
  border-radius: var(--pas-md);
  color: var(--texte-muet);
  text-decoration: none;
  font-size: 0.9375rem;
  font-weight: 600;
  white-space: nowrap;
  transition:
    background-color var(--duree) ease,
    color var(--duree) ease;
}

.langues__lien:hover {
  color: var(--texte);
  background: var(--surface-alt);
}

/*
 * La page n'existe pas dans cette langue : le lien mène à l'accueil, et il le DIT.
 *
 * Un lien qui n'emmène pas là où il promet est pire qu'un lien absent, même quand la page
 * d'arrivée est bonne — c'est la règle « aucun bouton qui ne mène nulle part », vue de
 * l'autre côté. L'encre est `--texte-muet` sur `--surface`, le couple déjà mesuré pour
 * cette liste (8,04:1 en sombre, 7,27:1 en clair).
 */
.langues__note {
  flex-basis: 100%;
  margin-inline-start: calc(2ch + var(--pas-md));
  color: var(--texte-muet);
  font-size: 0.75rem;
  font-weight: 500;
  white-space: normal;
}

/* Le code ISO en tête de ligne : il fait la colonne qui aligne les trois noms, et c'est
   lui qu'on retrouve dans le résumé une fois le choix fait. */
.langues__code {
  min-width: 2ch;
  font-weight: 800;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--texte-muet);
}

/* La langue courante est un aplat, pas une encre plus vive : la hiérarchie se fait par
   la forme. `--sur-primaire` sur `--primaire` est le couple déjà mesuré de `.bouton`. */
.langues__lien.est-actif,
.langues__lien.est-actif .langues__code {
  color: var(--sur-primaire);
}

.langues__lien.est-actif {
  background: var(--primaire);
  font-weight: 700;
}

/*
 * Sous 560 px, la barre tient sur DEUX rangées : la marque, puis la navigation entière.
 * C'est le sélecteur compact qui le permet — avec les trois pastilles, il en fallait
 * trois. Pas de menu hamburger pour autant : quatre entrées ne le justifient pas, et un
 * menu de navigation exige un script pour s'ouvrir, donc une barre morte le jour où le
 * script ne charge pas.
 */
@media (max-width: 559px) {
  .entete__verre {
    row-gap: var(--pas-sm);
    /* Deux rangées dans une pilule à 999 px de rayon donnent une forme de losange :
       au-delà d'une rangée, c'est un rectangle arrondi. */
    border-radius: var(--rayon-lg);
    padding: var(--pas-md) var(--pas-lg);
  }
  .nav {
    width: 100%;
    gap: var(--pas-sm);
  }
  /* « Aide » reste à droite, donc toujours détachée du groupe des ancres, et le sélecteur
     se colle à elle. */
  .langues {
    margin-left: auto;
  }
  .nav__aide {
    margin-left: 0;
  }
}

/* ── Héros ───────────────────────────────────────────────────────────────────── */

.heros {
  padding-block: var(--pas-2xl) var(--pas-4xl);
}

.heros__texte {
  display: grid;
  gap: var(--pas-xl);
  max-width: 40ch;
}

.heros h1 {
  margin-block: var(--pas-sm);
}

@media (min-width: 980px) {
  .heros .conteneur {
    display: grid;
    /* `minmax(0, …)` des deux côtés : le minimum automatique d'un élément de grille est
       sa taille `min-content`, et la piste du carrousel — cinq téléphones — élargirait
       le document entier. Le même piège que le tableau des données de la politique. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: center;
    gap: var(--pas-3xl);
  }
}

/* ── Boutons et pastilles ────────────────────────────────────────────────────── */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-md);
}

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pas-sm);
  min-height: var(--cible);
  padding: var(--pas-md) var(--pas-xl);
  border: 0;
  border-radius: var(--rayon-pilule);
  background: var(--primaire);
  color: var(--sur-primaire);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--duree) ease;
}

.bouton:hover {
  transform: translateY(-1px);
}

/*
 * « Bientôt » — l'état du bouton secondaire, dit sur le bouton lui-même.
 *
 * L'app n'est pas publiée ; un « Télécharger » qui n'annonce rien serait une promesse
 * qu'on ne tient qu'après un défilement. `--card` / `--encre-card` est le couple d'une
 * surface claire, mesuré, et la pastille est un aplat : jamais une encre affaiblie.
 */
.pastille {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--pas-sm);
  border-radius: var(--rayon-pilule);
  background: var(--card);
  color: var(--encre-card);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.bouton--contour {
  background: transparent;
  color: var(--texte);
  /* 1,5 px et non 1 px : sur un écran à 1× un contour de 1 px se perd dans l'arrondi. */
  box-shadow: inset 0 0 0 1.5px var(--contour-action);
}

/*
 * Les deux pastilles de store. Ce ne sont PAS des liens : l'app n'est pas publiée, et un
 * bouton qui ne mène nulle part est un dark pattern. Elles deviendront des `<a>` avec les
 * badges officiels d'Apple et de Google le jour de la publication — voir
 * `docs/SITE_VITRINE.md` § « Ce qui reste à poser ».
 */
.stores {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-md);
}

.store {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-md);
  min-height: var(--cible);
  padding: var(--pas-md) var(--pas-xl);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  box-shadow: inset 0 0 0 1px var(--bordure);
}

.store svg {
  width: 22px;
  height: 22px;
  flex: none;
  color: var(--texte);
}

.store__pile {
  display: grid;
}

.store__pile > span {
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--texte-muet);
  line-height: 1.4;
}

.store__pile b {
  font-size: 1rem;
  line-height: 1.25;
}

.mention {
  font-size: 0.9375rem;
  color: var(--texte-muet);
  max-width: 46ch;
}

/* ── Téléphones dessinés ─────────────────────────────────────────────────────── */

/*
 * Le cadre a EXACTEMENT le ratio des captures — 1320 × 2868 depuis le 18 septembre 2026.
 * Un cadre à un autre ratio étirerait l'interface d'un pour cent ou deux : invisible à
 * l'œil nu, et pourtant c'est ce qui fait qu'un visuel « sonne faux ». Ce ratio est celui
 * d'un iPhone 16 Pro Max, parce que les captures sont prises à sa taille LOGIQUE
 * (1320 × 2868 en densité 480 = 440 × 956 points) : l'app s'y remet en page d'elle-même,
 * rien n'est étiré, et la même capture sert les deux fiches de store.
 *
 * L'image porte ses `width`/`height` en attributs : sans eux la page saute au chargement
 * (CLS), et le saut est d'autant plus visible que le téléphone est grand.
 */
.telephone {
  position: relative;
  width: 100%;
  max-width: 300px;
  margin-inline: auto;
  padding: 8px;
  border-radius: 34px;
  background: var(--chassis);
  box-shadow:
    inset 0 0 0 1px var(--chassis-liseré),
    0 24px 60px -24px rgba(0, 0, 0, 0.65);
}

.telephone img {
  width: 100%;
  height: auto;
  aspect-ratio: 1320 / 2868;
  border-radius: 27px;
  /* Un fond sous l'image : tant qu'elle charge, le cadre reste un téléphone éteint et
     non un trou dans la page. */
  background: var(--fond);
}

/*
 * ── LE CARROUSEL DU HÉROS ────────────────────────────────────────────────────
 *
 * Cinq écrans réels qu'on prend en main, et qui tournent seuls quand personne n'y touche.
 *
 * ## DEUX modes dans une seule feuille, et c'est voulu
 *
 * **Sans script** (pas de classe `js`) : un défilement natif à crans. Les cinq téléphones
 * existent, dans l'ordre, tous lisibles ; le glissement au doigt est celui du système ;
 * les points sont de vrais liens d'ancre — `#ecran-3` défile le conteneur sans une ligne
 * de JavaScript. C'est la version précédente, intacte, et c'est le repli.
 *
 * **Avec script** (`.est-scene`, posée par `site.js`) : une SCÈNE en `transform`, pilotée
 * au pointeur. Le défilement natif est coupé.
 *
 * Pourquoi ce second mode existe, alors que le premier suffisait : le natif ne sait pas
 * régler en une durée CONNUE (`scrollTo({behavior:'smooth'})` n'a pas de durée réglable,
 * et sous `scroll-snap-type: mandatory` le snap se rejoue au relâchement avec la sienne),
 * et il ne se glisse pas à la SOURIS. Les deux étaient demandés.
 *
 * ## Le clip vit sur `.carrousel`, jamais sur la piste
 *
 * Un `overflow: hidden` posé sur la piste se déplacerait AVEC elle : le transform d'un
 * élément emporte son propre clip, donc il ne clipperait plus rien. Il est sur le parent,
 * qui ne bouge pas.
 *
 * ## LE DÉGRADÉ LATÉRAL EST PARTI, et il ne reviendra pas
 *
 * Les bords étaient fondus sur 14 % de la largeur : deux voiles larges et bien visibles,
 * et le propriétaire les a relevés comme « trop présents ». Les voisins se lisent déjà
 * comme des voisins par l'ÉCHELLE et la lumière — comme les couches du shell de l'app.
 * Reste la coupe franche du clip, et c'est très bien : un téléphone tranché à la limite
 * d'un cadre se lit comme une rangée qui continue, pas comme un débordement raté.
 *
 * **Et un masque n'était de toute façon plus possible ici.** Un `mask-image` sur un
 * élément dont un descendant est PROMU EN COUCHE (`will-change: transform`, ce que la
 * scène exige) fait disparaître tout le sous-arbre en Chrome sans GPU : mesuré, les cinq
 * téléphones absents de la capture alors que leur géométrie était juste au pixel
 * (`.carrousel` 732 × 588, écran 1 à x = 876, image chargée). Aucune erreur, aucun
 * avertissement — et une zone vide dans le héros ressemble à une image qui n'a pas
 * chargé. Retirer `will-change` les faisait revenir, ce qui désigne la combinaison sans
 * ambiguïté.
 */
.carrousel {
  min-width: 0;
  overflow: hidden;
}

/*
 * Le centrage du PREMIER et du DERNIER écran demande cette marge intérieure en mode
 * natif : sans elle, `scroll-snap-align: center` ne peut pas centrer un élément de bord
 * (il faudrait défiler avant zéro), et l'écran 1 reste collé à gauche. En mode scène,
 * c'est la translation qui centre, donc la marge disparaît.
 */
.carrousel__piste {
  --ecran-l: clamp(158px, 56vw, 236px);

  display: flex;
  gap: var(--pas-lg);
  margin: 0;
  padding: 0 calc(50% - var(--ecran-l) / 2);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* La barre de défilement n'apporte rien ici — les points disent déjà où l'on est — et
     elle mangerait le bas des téléphones. */
  scrollbar-width: none;
}

.carrousel__piste::-webkit-scrollbar {
  display: none;
}

/*
 * LA SCÈNE. `translate3d` et rien d'autre : pas de mise en page, pas d'opacité, donc tout
 * est composé. Le règlement dure `--long` (450 ms) au plus, avec la courbe d'entrée de
 * l'app (`emphasizedDecelerate`) — part vite, se pose doucement.
 *
 * `touch-action: pan-y` : le geste horizontal est à nous, le défilement VERTICAL de la
 * page reste au système. Sans lui, un doigt posé sur les téléphones ne pourrait plus
 * descendre la page — sur le premier écran, donc.
 */
.est-scene .carrousel__piste {
  padding: 0;
  overflow: visible;
  scroll-snap-type: none;
  touch-action: pan-y;
  cursor: grab;
  will-change: transform;
  transition: transform var(--long) var(--entree);
}

/* Pendant le geste, rien ne règle : la scène suit le pointeur au pixel. */
.est-tenu .carrousel__piste {
  transition: none;
  cursor: grabbing;
}

.est-tenu .ecran {
  transition: none;
}

/*
 * La profondeur, comme `_DepthBranches` dans l'app : les voisins reculent d'un cran
 * d'échelle et perdent en présence. `--profondeur` est posé par `site.js` d'après la
 * position CONTINUE de la scène — sans script, tous les écrans valent 0, donc tous sont
 * nets, et le carrousel est simplement une rangée qui défile.
 */
.ecran {
  --profondeur: 0;

  flex: 0 0 var(--ecran-l);
  scroll-snap-align: center;
  display: grid;
  gap: var(--pas-md);
  justify-items: center;
  transform: scale(calc(1 - 0.16 * var(--profondeur)));
  transition:
    transform var(--long) var(--entree),
    filter var(--long) var(--entree);
  /* `brightness` et non `opacity` : une opacité animée ouvre une couche par frame, et le
     recul se lit aussi bien par la lumière. C'est la même règle que dans l'app. */
  filter: brightness(calc(1 - 0.32 * var(--profondeur)));
}

/* Un voisin est cliquable — il vient au centre. Le centré ne mène nulle part. */
.est-scene .ecran:not([data-actif]) {
  cursor: pointer;
}

.ecran .telephone {
  max-width: none;
  width: 100%;
}

/* L'image ne se prend pas au doigt : c'est la SCÈNE qu'on glisse, et le comportement
   natif de traînée d'image annulerait le geste dès le premier pixel, à la souris. */
.est-scene .ecran img {
  -webkit-user-drag: none;
  user-select: none;
  pointer-events: none;
}

.ecran__legende {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--texte-muet);
  transition: color var(--duree) ease;
}

.ecran[data-actif] .ecran__legende {
  color: var(--texte);
}

.carrousel__points {
  display: flex;
  justify-content: center;
  gap: var(--pas-xs);
  margin-top: var(--pas-lg);
}

/*
 * Un point fait 10 px, mais sa CIBLE en fait 44 : le carré tappable est le lien, le
 * disque n'est que son enfant. Cinq points de 10 px collés seraient intappables au
 * pouce, et c'est le visiteur le plus fréquent.
 */
.point {
  display: grid;
  place-items: center;
  width: var(--cible);
  height: 32px;
  text-decoration: none;
}

.point > span {
  width: 9px;
  height: 9px;
  border-radius: var(--rayon-pilule);
  background: var(--texte-muet);
  transition:
    width var(--duree) ease,
    background-color var(--duree) ease;
}

.point[aria-current='true'] > span {
  width: 26px;
  background: var(--texte);
}

/* Deux téléphones côte à côte dans une section. */
.duo {
  display: flex;
  justify-content: center;
  gap: var(--pas-lg);
}

.duo .telephone {
  max-width: 232px;
  margin-inline: 0;
}

.duo .telephone:last-child {
  transform: translateY(var(--pas-xl));
}

/*
 * Sur un téléphone, la paire DÉFILE au doigt au lieu de rétrécir.
 *
 * Deux appareils côte à côte dans les 342 px utiles d'un écran de 390 tiennent — la
 * `flex-shrink` fait son travail — mais à 165 px chacun l'interface n'est plus lisible :
 * il ne reste que la silhouette d'un écran. Or c'est le visiteur le plus fréquent, celui
 * qui ouvre le lien depuis une conversation.
 *
 * Chacun prend donc 78 % de la largeur et la rangée défile. Le second appareil, visible
 * au quart, EST l'invitation à glisser : un défilement horizontal sans rien qui dépasse
 * ne se devine pas. Le conteneur borne son propre défilement (`min-width: 0` — la page
 * elle-même ne bouge pas), et `scroll-snap` évite de s'arrêter entre deux.
 */
@media (max-width: 599px) {
  .duo {
    justify-content: flex-start;
    gap: var(--pas-md);
    min-width: 0;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* De la place pour la barre de défilement là où elle se superpose au contenu. */
    padding-bottom: var(--pas-sm);
    /* Le débord se fait jusqu'au bord de l'écran, pas jusqu'à la marge du conteneur :
       c'est ce qui donne l'aperçu du second appareil. */
    margin-inline: calc(-1 * var(--pas-xl));
    padding-inline: var(--pas-xl);
  }
  .duo .telephone {
    flex: none;
    width: 78%;
    max-width: 300px;
    scroll-snap-align: center;
  }
  /* Le décalage vertical n'a plus de sens dans une rangée qui défile. */
  .duo .telephone:last-child {
    transform: none;
  }
}

/* ── LA DÉMONSTRATION DU GESTE, QU'ON PREND EN MAIN ─────────────────────────── */

/*
 * Le geste central de l'app, jouable au doigt et à la souris dans un téléphone dessiné.
 *
 * ## Ce qui est à l'écran est VRAI
 *
 * Les deux cartes sont les cartes d'invitation de l'app, découpées au pixel (rectangle
 * 1272 × 1906 à +24+589) dans DEUX captures de l'accueil : celle du paquet au repos, et
 * celle d'APRÈS un glissement, donc la deuxième carte promue en tête. Le fond est la même
 * capture dont on a effacé la zone de carte ET la ligne « N événements restants », en
 * reconstituant le dégradé d'écran ligne par ligne. Les trois sont refaits par
 * `apps/site/scripts/generer-ecrans.js`, dans les trois langues. Les deux sont rejoués ici parce qu'ils
 * doivent BOUGER : la carte suit le doigt, le compteur décrémente.
 *
 * ## Trois réponses, et seulement les trois de l'app
 *
 * | Sens    | Réponse       | Teinte                     | Glyphe                       |
 * | ------- | ------------- | -------------------------- | ---------------------------- |
 * | droite  | J'y vais      | `--capture-succes`         | `Icons.check_rounded`        |
 * | gauche  | Je viens pas  | `--capture-danger`         | `Icons.close_rounded`        |
 * | haut    | Peut-être     | `--capture-avertissement`  | `Icons.help_outline_rounded` |
 *
 * Les libellés sont ceux de `rsvp_labels.dart`, les glyphes ceux de `RsvpLabels.icon`, et
 * les teintes celles de la palette SOMBRE, figées : tout ceci est posé sur une capture,
 * qui ne suit aucun thème (voir la famille `--capture-*` en tête de feuille). Rien d'autre
 * n'est possible : pas de quatrième sens, pas d'effet, pas de son.
 *
 * Les trois remplissages tiennent aussi comme ÉLÉMENTS d'interface sur la capture, seuil
 * 3:1 : vert 7,37, ambre 10,06, rouge **4,46** — le plus serré, là encore.
 *
 * ## Deux modes, et le repli tourne toujours
 *
 * Avec `site.js` (classe `.est-pilotee`), le script écrit les transformations et les
 * animations CSS sont coupées. Sans lui, la boucle `@keyframes` d'origine joue « J'y
 * vais » indéfiniment : la page reste complète et démontre le geste, elle ne se prend
 * simplement pas en main.
 *
 * ## Ce que reproduit le geste, valeur par valeur (`swipe_deck.dart`)
 *
 * | Ce qu'on voit                    | D'où ça vient                                     |
 * | -------------------------------- | ------------------------------------------------- |
 * | rotation pendant le glissement   | `(_drag.dx / largeur) * 0.35` rad                 |
 * | seuil de décision                | `_swipeThreshold = 80` dp — **pas** 0,3 de largeur |
 * | vitesse qui emporte la décision  | 800 dp/s                                          |
 * | zone morte au départ             | 8 dp                                              |
 * | la carte suivante grandit        | `0.92 + 0.08 * (distance / 200)`                  |
 * | le voile part du bord VISÉ       | `SwipeIntentOverlay`, arrêts 0 / 22 / 55 %, α 0,34 |
 * | la pastille n'arrive qu'au tiers | `opacity: ((t - 0.3) / 0.7)`                      |
 * | la pastille grossit de 0,7 à 1,2 | `scale: 0.7 + 0.5 * t`                            |
 *
 * Le seuil est le piège de ce portage : 80 dp sur les 448 dp de la capture font **17,9 %**
 * de la largeur de l'écran, pas 30 %. Les 0,3 de largeur et les 600 px/s appartiennent à
 * `cibleApresGlissement` — le glissement entre HUBS — et les appliquer ici rendrait la
 * carte deux fois plus dure à trancher que dans l'app.
 */
.demo {
  /* La réponse visée pilote la teinte. `going` par défaut, donc l'état de repos et le
     repli sans script sont ceux de « J'y vais ». L'encre, elle, ne change pas : les trois
     remplissages sont clairs, donc `inkOn` rend la même encre sombre pour les trois. */
  --teinte: var(--capture-succes);
  --encre: var(--capture-encre-reponse);
  --sens: to left;

  width: 100%;
  max-width: 300px;
  margin-inline: auto;
}

.demo[data-reponse='notGoing'] {
  --teinte: var(--capture-danger);
  --sens: to right;
}

.demo[data-reponse='pending'] {
  --teinte: var(--capture-avertissement);
  --sens: to bottom;
}

/*
 * `container-type: inline-size` : le compteur doit faire la TAILLE qu'il a dans l'app —
 * 42 px, soit 3,19 % de la largeur d'une capture de 1320. Une taille en `px`
 * ne suivrait pas le téléphone quand il rétrécit, et une taille en `vw` suivrait la
 * FENÊTRE, pas le téléphone.
 */
.demo__ecran {
  position: relative;
  aspect-ratio: 1320 / 2868;
  border-radius: 27px;
  overflow: hidden;
  background: var(--fond);
  container-type: inline-size;
  /* Une frontière de peinture, l'équivalent du `RepaintBoundary` de l'app : la couche
     animée ne remonte pas jusqu'à la page, donc la carte qui glisse ne fait pas repeindre
     la section — ni le verre de l'en-tête au-dessus. */
  contain: paint;
}

.demo__ecran .demo__fond {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
}

/*
 * Le compteur, à la place EXACTE qu'il occupe dans la capture : les glyphes de
 * `store/captures/fr/accueil.png` vont de x = 661 à 1139 et de y = 531 à 565, sur
 * 1320 × 2868.
 *
 * Ces deux nombres ne se posent PAS tels quels : la boîte du paragraphe déborde du texte
 * (bearing à droite, demi-interligne en bas), donc `right`/`bottom` déduits du seul
 * rectangle des glyphes mettent le compteur 3 px trop à gauche et 8 px trop haut. Les
 * valeurs ci-dessous sont mesurées par SUPERPOSITION — la page rendue à la largeur de la
 * capture, puis comparée à elle au pixel (`docs/SITE_VITRINE.md` § 14).
 *
 * Son encre est `--texte-muet`, relevée au pixel sur la capture (#b9a9e0) — 8,16:1 sur le
 * fond d'écran à cette hauteur.
 */
.demo__compteur {
  position: absolute;
  right: 13.48%;
  bottom: 79.99%;
  z-index: 1;
  margin: 0;
  /* L'encre atténuée de la palette SOMBRE, figée : elle est posée sur la capture, pas sur
     la page. En thème clair, `--texte-muet` y tomberait à 2,31:1. */
  color: var(--capture-encre-muette);
  font-size: 3.19cqw;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}

/*
 * La zone qu'on prend en main. `touch-action: none` : ici le geste est à nous dans les
 * DEUX axes — un « peut-être » se fait vers le haut, donc laisser `pan-y` au système
 * rendrait cette réponse inatteignable au doigt.
 *
 * Elle est `tabindex="0"` et porte son libellé : le paquet se pilote au doigt, geste qu'un
 * lecteur d'écran ne peut pas produire, et les flèches sont le seul chemin qui reste. Pas
 * de `role="img"` sur la démonstration — il écarterait tout le sous-arbre, donc la zone
 * focalisable avec.
 */
.demo__paquet {
  position: absolute;
  /* La place EXACTE de la carte dans la capture, pas l'écran entier : la zone qu'on prend
     en main doit être la carte, sinon un doigt posé sur l'en-tête « Alice » ou sur la barre
     de navigation ferait glisser une invitation. */
  left: 1.82%;
  top: 20.54%;
  width: 96.36%;
  aspect-ratio: 1272 / 1906;
  z-index: 2;
  touch-action: none;
  cursor: grab;
}

.est-tenue .demo__paquet {
  cursor: grabbing;
}

/*
 * La carte occupe la place EXACTE qu'elle occupe dans l'app : 24 px de marge et
 * 1272 × 1906 dans une capture de 1320 × 2868, soit 1,82 % / 20,54 % / 96,36 % / 66,46 %.
 * Une position approchée se voit tout de suite, parce que le fond porte les bords réels du
 * reste de l'écran. Ces quatre nombres se REMESURENT à chaque série, et ils ont déjà bougé
 * deux fois : le 8 septembre 2026 la fermeture du bandeau « ton récap est prêt » a fait
 * monter la carte de 322 px, et le 18 septembre le passage des captures au format de
 * l'App Store a changé l'écran sous elle. Les six captures d'accueil des trois langues
 * donnent le même rectangle — c'est ce qui prouve que c'est une géométrie et non un
 * accident de rendu.
 */
/*
 * La carte remplit son paquet, qui occupe déjà la place exacte. Le ratio du rectangle
 * DÉCOUPÉ (1272 × 1906) est écrit sur le paquet : `.telephone img` imposerait sinon celui
 * d'un écran entier, et la carte sortirait deux fois trop haute.
 */
.demo__ecran .demo__carte {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Le rayon de `EventSwipeCard` : 32 dp, soit 96 px sur une capture de 1320 — donc
     7,27 cqw, et non une constante en `px` qui ne suivrait pas le téléphone quand il
     rétrécit. Il doit valoir le rayon RÉEL de la carte découpée : plus petit, les coins de
     l'image (du fond d'écran, découpé avec elle) dépassent du rayon et se voient dès que la
     carte glisse au-dessus du voile ; plus grand, il rognerait la carte. */
  border-radius: 7.27cqw;
  background: none;
  will-change: transform;
  /* L'ombre de `EventSwipeCard` : `brand` à 28 %, flou 28, décalage 14. Elle se renforce
     quand la carte quitte la pile — c'est ce qui la décolle du fond pendant le geste. */
  box-shadow: 0 calc(3px + 5px * var(--elan, 0)) calc(9px + 14px * var(--elan, 0))
    color-mix(in srgb, var(--marque) 28%, transparent);
  animation: demo-carte 12s var(--standard) infinite;
}

.demo__carte--active {
  z-index: 2;
}

.demo__carte--suivante {
  z-index: 1;
  transform: scale(0.92);
  animation-delay: -6s;
}

/*
 * ── LE REPLI SANS SCRIPT ──
 *
 * Le cycle est à DEUX cartes, et c'est ce qui le rend sans couture : les deux jouent la
 * MÊME animation décalée d'une demi-période, chacune passe devant, glisse, sort, et
 * revient derrière l'autre PENDANT que celle-ci la couvre. Il n'y a donc aucun instant de
 * raccord à masquer. Le prix est un `z-index` qui saute au lieu de s'interpoler, et c'est
 * exactement ce qu'on veut.
 */
@keyframes demo-carte {
  /* Deux secondes de repos : le temps de lire la carte avant que le doigt parte. */
  0%,
  16% {
    transform: translateX(0) rotate(0deg) scale(1);
    opacity: 1;
    z-index: 2;
  }
  /* Le doigt tire jusqu'au seuil de décision. */
  27% {
    transform: translateX(19%) rotate(3.7deg) scale(1);
    opacity: 1;
    z-index: 2;
  }
  /* Relâché : la carte part, et elle accélère. */
  35% {
    transform: translateX(132%) rotate(18deg) scale(1);
    opacity: 1;
    z-index: 2;
  }
  36% {
    transform: translateX(132%) rotate(18deg) scale(1);
    opacity: 0;
    z-index: 2;
  }
  36.01%,
  44% {
    transform: translateX(0) rotate(0deg) scale(0.92);
    opacity: 0;
    z-index: 1;
  }
  50%,
  66% {
    transform: translateX(0) rotate(0deg) scale(0.92);
    opacity: 1;
    z-index: 1;
  }
  84%,
  100% {
    transform: translateX(0) rotate(0deg) scale(1);
    opacity: 1;
    z-index: 1;
  }
}

/*
 * Quand le script pilote, les boucles CSS s'arrêtent : deux sources de vérité sur la même
 * transformation donneraient une carte qui saute d'un état à l'autre à chaque frame.
 */
.est-pilotee .demo__carte,
.est-pilotee .demo__voile,
.est-pilotee .demo__pastille,
.est-pilotee .demo__libelle {
  animation: none;
}

/*
 * Le voile teinté, du bord VISÉ vers le centre. Il indique la direction autant que le
 * choix, et il s'arrête avant le milieu : la carte qu'on juge doit rester lisible jusqu'au
 * bout. Les arrêts (0 / 22 / 55 %) et le plafond d'alpha (0,34) sont ceux de
 * `SwipeIntentOverlay` ; `--sens` bascule avec la réponse visée.
 */
.demo__voile {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  /* Décoratif ET au-dessus du geste : un `DecoratedBox` — comme une boîte à dégradé —
     répond vrai au test de pointeur sur toute sa surface, transparent ou non. Sans ça, le
     voile tuerait le geste qui le fait apparaître. */
  pointer-events: none;
  background: linear-gradient(
    var(--sens),
    color-mix(in srgb, var(--teinte) 34%, transparent) 0%,
    color-mix(in srgb, var(--teinte) 9%, transparent) 22%,
    transparent 55%
  );
  animation: demo-voile 6s var(--standard) infinite;
}

@keyframes demo-voile {
  0%,
  30% {
    opacity: 0;
  }
  44% {
    opacity: 0.55;
  }
  54%,
  64% {
    opacity: 1;
  }
  73%,
  100% {
    opacity: 0;
  }
}

/*
 * La pastille : un disque plein de la teinte de la réponse portant son glyphe, à l'échelle
 * du 96 dp de l'app (24 % de la largeur d'un écran de 400 dp).
 *
 * L'encre est `--encre` et non `--texte` : la teinte est un REMPLISSAGE, et l'app choisit
 * l'encre la plus contrastée par `palette.inkOn(fond)`. Sur le vert clair de la palette
 * sombre c'est l'encre sombre — 8,14:1, contre 2,28:1 pour le blanc. Une coche est un
 * élément d'interface, donc 3:1 suffirait : le blanc n'y arrivait même pas.
 */
.demo__pastille {
  position: absolute;
  left: 50%;
  top: 49%;
  z-index: 4;
  width: 24%;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--teinte);
  color: var(--encre);
  box-shadow: 0 0 32px 4px color-mix(in srgb, var(--teinte) 45%, transparent);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.7);
  pointer-events: none;
  animation: demo-pastille 6s var(--standard) infinite;
}

/* Un seul glyphe à la fois, celui de la réponse visée — jamais deux superposés. */
.demo__glyphe {
  display: none;
  width: 52%;
  height: 52%;
}

.demo[data-reponse='going'] .demo__glyphe--going,
.demo[data-reponse='notGoing'] .demo__glyphe--notGoing,
.demo[data-reponse='pending'] .demo__glyphe--pending {
  display: block;
}

@keyframes demo-pastille {
  /* Rien avant 38 % : c'est le tiers du geste, exactement le seuil de
     `((t - 0.3) / 0.7)` de `SwipeIntentOverlay`. Avant, on hésite encore, et un symbole
     qui clignote à chaque micro-mouvement du doigt est fatigant. */
  0%,
  38% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.7);
  }
  54%,
  64% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.2);
  }
  73%,
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.25);
  }
}

/*
 * Le libellé, au-dessus de la pastille. L'app n'en met pas — le glyphe suffit au milieu
 * d'un geste, et il ne demande aucune traduction. Une page, elle, doit NOMMER le geste :
 * le visiteur ne l'a jamais fait, et « J'y vais » est le mot du produit.
 *
 * Encre pleine sur remplissage plein, jamais d'alpha : le libellé est du TEXTE, donc il
 * lui faut 4,5:1, et la moindre transparence entamerait le couple le plus serré des trois
 * (l'encre sombre sur le rouge clair, 4,93:1).
 */
.demo__libelle {
  position: absolute;
  left: 50%;
  /* AU-DESSUS de la pastille, et pas dessous : sous elle, le libellé tombe pile sur la
     ligne « Chloé Rousseau · t'invite » de la vraie carte. Relevé à l'écran. */
  top: 35%;
  z-index: 4;
  padding: 5px var(--pas-md);
  border-radius: var(--rayon-pilule);
  background: var(--teinte);
  color: var(--encre);
  font-size: 0.8125rem;
  font-weight: 800;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-50%);
  pointer-events: none;
  animation: demo-libelle 6s var(--standard) infinite;
}

@keyframes demo-libelle {
  0%,
  42% {
    opacity: 0;
  }
  54%,
  64% {
    opacity: 1;
  }
  73%,
  100% {
    opacity: 0;
  }
}

/*
 * L'annonce du résultat : hors du téléphone, et invisible à l'œil.
 *
 * Pas `display: none` ni `visibility: hidden` — les deux retirent le nœud de l'arbre
 * d'accessibilité, donc `aria-live` n'annoncerait plus rien. La technique est celle du
 * lien d'évitement : un rectangle de 1 px, découpé.
 */
.demo__annonce {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── RÉVÉLATIONS AU DÉFILEMENT ───────────────────────────────────────────────── */

/*
 * La cascade de `AmidAmiCascade` : un bloc arrive de 14 px plus bas, et les blocs d'une
 * même section se suivent de 50 ms — `AppMotion.cascadeStepMs`.
 *
 * Le préfixe `.js` est la règle qui compte : sans lui, une page dont le script ne s'est
 * pas chargé resterait **vide**, et rien ne le signalerait. L'état masqué n'existe donc
 * que si `site.js` a pu poser la classe sur `<html>` — c'est-à-dire seulement si
 * quelque chose est là pour le retirer.
 *
 * Le total ne dépasse jamais 380 ms (280 de transition, 100 de décalage au plus) : une
 * révélation qu'on attend est une révélation qui gêne.
 */
.js .revele {
  opacity: 0;
  transform: translateY(14px);
}

.js .revele.est-visible {
  opacity: 1;
  transform: none;
  transition:
    opacity 280ms var(--entree),
    transform 280ms var(--entree);
  transition-delay: calc(var(--cran, 0) * 50ms);
}

/* ── PRESSION ────────────────────────────────────────────────────────────────── */

/*
 * `AmidAmiPressable` : un enfoncement léger au doigt, et rien d'autre. Pas de
 * grossissement au survol — un bouton qui grandit quand la souris l'approche déplace ce
 * qui l'entoure, et sur une rangée de deux boutons ça se voit.
 */
.pressable {
  transition: transform 120ms var(--standard);
}

.pressable:active {
  transform: scale(0.97);
}

/* ── Sections « un bénéfice » ────────────────────────────────────────────────── */

.recit {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--pas-4xl);
}

.benefice {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--pas-xl);
  align-items: center;
}

.benefice__texte {
  display: grid;
  gap: var(--pas-md);
  max-width: 46ch;
}

.benefice__texte p {
  color: var(--texte-muet);
}

@media (min-width: 860px) {
  .benefice {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--pas-3xl);
  }
  /* L'alternance : une section sur deux met le visuel à gauche. Sur `nth-child` et non
     sur une classe posée à la main — ajouter une section au milieu ne doit pas obliger à
     réécrire les suivantes. */
  .benefice:nth-child(even) .benefice__visuel {
    order: -1;
  }
}

/* ── Bloc « ce qu'AmidAmi ne fait pas » ──────────────────────────────────────── */

/*
 * La carte de section en verre — le SECOND et dernier `backdrop-filter` de l'accueil.
 *
 * Son remplissage est translucide, donc il se compose avec l'aurore : ses encres sont
 * `--texte` et `--texte-muet`, jamais `--encre-card*`. C'est le piège le plus coûteux du
 * design system côté app : `onCardMuted` posé sur du verre donne 1,8:1, relevé à l'écran
 * deux fois dans la même journée. Un `GlassCard` n'est pas une `card`.
 */
.franchise {
  border-radius: var(--rayon-lg);
  padding: var(--pas-2xl);
  display: grid;
  gap: var(--pas-xl);
}

@media (min-width: 900px) {
  .franchise {
    padding: var(--pas-3xl);
  }
}

.franchise ul {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--pas-lg);
}

@media (min-width: 700px) {
  .franchise ul {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--pas-lg) var(--pas-2xl);
  }
}

.franchise li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--pas-md);
  align-items: start;
}

.franchise li b {
  display: block;
}

.franchise li p {
  color: var(--texte-muet);
  font-size: 0.9375rem;
  margin-top: 2px;
}

/* La coche est décorative : elle double l'information du texte, donc `aria-hidden`.
   3:1 lui suffit (élément d'interface), et `--succes` le dépasse dans les deux thèmes. */
.coche {
  width: 22px;
  height: 22px;
  flex: none;
  margin-top: 3px;
  color: var(--succes);
}

/* ── Appel final ─────────────────────────────────────────────────────────────── */

.final {
  display: grid;
  gap: var(--pas-xl);
  justify-items: center;
  text-align: center;
}

.final .chapeau {
  max-width: 48ch;
}

/* ── Pied de page ────────────────────────────────────────────────────────────── */

.pied {
  border-top: 1px solid var(--bordure);
  padding-block: var(--pas-2xl) var(--pas-3xl);
  margin-top: var(--pas-2xl);
  font-size: 0.9375rem;
}

.pied .conteneur {
  display: grid;
  gap: var(--pas-xl);
}

@media (min-width: 760px) {
  .pied .conteneur {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--pas-3xl);
  }
}

.pied p {
  color: var(--texte-muet);
  max-width: 40ch;
}

.pied nav ul {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--pas-md);
}

@media (min-width: 520px) {
  .pied nav ul {
    grid-template-columns: repeat(2, auto);
    gap: var(--pas-md) var(--pas-2xl);
  }
}

.pied nav a {
  color: var(--texte);
  text-decoration: none;
}

.pied nav a:hover {
  text-decoration: underline;
}

/* ── Pages de texte (légal, aide) ────────────────────────────────────────────── */

.doc {
  padding-block: var(--pas-2xl) var(--pas-4xl);
}

/*
 * `minmax(0, 1fr)` et non `1fr` : le minimum automatique d'un élément de grille est sa
 * taille `min-content`, donc le tableau des données (`min-width: 34rem`) élargissait le
 * document ENTIER à 544 px sur un téléphone de 390 — l'`overflow-x: auto` de son
 * enveloppe n'y changeait rien, puisque c'est l'enveloppe elle-même qui était trop large.
 * Rien ne le signale : à l'écran, c'est une page dont tous les paragraphes sont coupés à
 * droite, et ça ressemble à un problème de police.
 */
.doc__corps {
  max-width: var(--largeur-texte);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--pas-xl);
}

.doc h1 {
  font-size: clamp(2rem, 6vw, 3rem);
}

.doc h2 {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: clamp(1.3rem, 3vw, 1.6rem);
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin-top: var(--pas-xl);
  text-wrap: balance;
}

.doc h3 {
  font-size: 1.0625rem;
  font-weight: 800;
  margin-top: var(--pas-md);
}

.doc p,
.doc li {
  color: var(--texte);
}

/*
 * DEUX PARAGRAPHES QUI SE SUIVENT DOIVENT SE VOIR.
 *
 * `.doc__corps` est une grille et son `gap` sépare les sections, mais l'intérieur d'une
 * `<section>` est un flux normal — et le socle met `margin: 0` sur `p`. Deux paragraphes
 * consécutifs se collaient donc, à 1,6 d'interligne : le lecteur voit un seul bloc, et
 * l'article « Qui peut l'utiliser » des CGU en fait trois d'affilée. Le défaut existait
 * depuis la v1 ; il s'est vu en relisant les mêmes pages dans trois langues.
 *
 * Des règles de fratrie explicites plutôt qu'une grille par section : une grille ferait
 * s'ajouter le `gap` à la `margin-top` du titre suivant, et l'espace entre sections
 * doublerait sans qu'on voie pourquoi.
 */
.doc section > p + p,
.doc section > p + ul,
.doc section > p + ol,
.doc section > ul + p,
.doc section > ol + p,
.doc section > p + div,
.doc section > div + p {
  margin-top: var(--pas-md);
}

.doc ul,
.doc ol {
  display: grid;
  gap: var(--pas-sm);
}

.doc__meta {
  font-size: 0.9375rem;
  color: var(--texte-muet);
}

/*
 * Le numéro de version d'une nouveauté : une pastille pleine, jamais une nuance d'opacité.
 *
 * `--surface` porte la limite et `--texte` l'encre — les deux jetons du fond d'écran, parce
 * que la pastille est posée sur lui et non sur une card. La hiérarchie se fait par la forme
 * et la taille, comme partout ailleurs dans ce produit.
 */
.nouveaute__version {
  display: inline-block;
  margin-inline-start: var(--pas-xs);
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: var(--surface);
  color: var(--texte);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

/*
 * Le maillage de l'accueil vers les trois pages de contenu.
 *
 * Trois tuiles OPAQUES (`--surface`), pas du verre : le plafond est de deux
 * `backdrop-filter` par page, et la barre de navigation plus la carte « ce qu'AmidAmi ne
 * fait pas » les occupent déjà tous les deux.
 */
.guides {
  display: grid;
  gap: var(--pas-xl);
}

.guides ul {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--pas-md);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

.guides li {
  background: var(--surface);
  border-radius: var(--rayon);
  box-shadow: inset 0 0 0 1px var(--bordure);
  padding: var(--pas-lg);
  display: grid;
  gap: var(--pas-xs);
  align-content: start;
}

/* `--lien` sur `--surface` : 7,65:1 en sombre, 8,98:1 en clair — mesuré, comme tout
   nouveau couple. `--accent` ne pouvait pas servir ici (4,48:1 sur le pire mélange
   d'aurore), c'est la raison d'être de ce jeton. */
.guides li a {
  color: var(--lien);
  font-weight: 700;
  text-decoration: none;
}

.guides li a:hover,
.guides li a:focus-visible {
  text-decoration: underline;
}

.guides li p {
  margin: 0;
  color: var(--texte-muet);
  font-size: 0.9375rem;
}

/*
 * Une capture dans une page de documentation. `.telephone` porte déjà le châssis et son
 * `max-width: 300px` : il ne reste que la marge et la légende.
 */
.doc__figure {
  margin: var(--pas-xl) 0;
  display: grid;
  gap: var(--pas-md);
}

.doc__figure figcaption {
  text-align: center;
  color: var(--texte-muet);
  font-size: 0.9375rem;
}

/* L'appel à l'action d'une page de contenu : UN seul, et à la fin. */
.doc__cta {
  margin-top: var(--pas-xl);
}

/* Encadré d'information : la surface porte la limite, la bordure n'est que décor. */
.encart {
  background: var(--surface);
  border-radius: var(--rayon);
  box-shadow: inset 0 0 0 1px var(--bordure);
  padding: var(--pas-xl);
  display: grid;
  gap: var(--pas-md);
}

.encart--fort {
  background: var(--card);
  color: var(--encre-card);
  box-shadow: none;
}

/* Sur une surface CLAIRE, les encres changent de jeton : `--texte-muet` y donnerait
   1,5:1. C'est le piège le plus fréquent de cette palette. */
.encart--fort p,
.encart--fort li {
  color: var(--encre-card);
}

.encart--fort .doc__meta,
.encart--fort .discret {
  color: var(--encre-card-muette);
}

.encart--fort a {
  color: var(--encre-card);
  font-weight: 700;
}

.discret {
  color: var(--texte-muet);
  font-size: 0.9375rem;
}

/*
 * Un tableau de données large ne fait JAMAIS défiler le corps de la page : il défile
 * dans son propre conteneur. `tabindex="0"` sur l'enveloppe pour que ce défilement soit
 * atteignable au clavier.
 */
.tableau {
  /* La seconde moitié du correctif ci-dessus : sans lui, `min-width: 34rem` du tableau
     remonte en minimum de son enveloppe dès qu'elle est posée dans une grille. */
  min-width: 0;
  overflow-x: auto;
  border-radius: var(--rayon);
  box-shadow: inset 0 0 0 1px var(--bordure);
}

.tableau table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.tableau caption {
  text-align: left;
  padding: var(--pas-md) var(--pas-lg);
  color: var(--texte-muet);
  font-size: 0.9375rem;
}

.tableau th,
.tableau td {
  text-align: left;
  vertical-align: top;
  padding: var(--pas-md) var(--pas-lg);
  border-bottom: 1px solid var(--bordure);
}

.tableau thead th {
  background: var(--surface);
  font-weight: 800;
}

.tableau tbody tr:last-child th,
.tableau tbody tr:last-child td {
  border-bottom: 0;
}

/* Sommaire d'une page longue. */
.sommaire {
  background: var(--surface);
  border-radius: var(--rayon);
  box-shadow: inset 0 0 0 1px var(--bordure);
  padding: var(--pas-xl);
}

/*
 * `display: block` explicite, et ce n'est pas de la décoration : la règle générale
 * `.doc ul, .doc ol { display: grid }` faisait de cette liste un conteneur de grille, et
 * les colonnes multiples ne s'appliquent PAS à une grille. Le sommaire restait donc sur
 * une seule colonne, sans qu'aucune règle ne paraisse en cause.
 */
.sommaire ol {
  display: block;
  columns: 2 15rem;
  column-gap: var(--pas-2xl);
  margin-top: var(--pas-md);
}

.sommaire li {
  break-inside: avoid;
  margin-bottom: var(--pas-sm);
}

/* Une question fréquente : le détail natif, donc pas une ligne de JavaScript. */
.qr {
  display: grid;
  gap: var(--pas-md);
}

.qr details {
  background: var(--surface);
  border-radius: var(--rayon);
  box-shadow: inset 0 0 0 1px var(--bordure);
  padding: var(--pas-lg) var(--pas-xl);
}

.qr summary {
  cursor: pointer;
  font-weight: 800;
  min-height: var(--cible);
  display: flex;
  align-items: center;
  /* Le marqueur par défaut de Chrome est un triangle minuscule, mal placé sur plusieurs
     lignes. Le nôtre est une pastille alignée sur la première ligne. */
  list-style: none;
  gap: var(--pas-md);
}

.qr summary::-webkit-details-marker {
  display: none;
}

.qr summary::before {
  content: '+';
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: var(--rayon-pilule);
  background: var(--surface-alt);
  color: var(--texte);
  display: grid;
  place-items: center;
  font-size: 1.125rem;
  line-height: 1;
}

.qr details[open] summary::before {
  content: '−';
}

.qr details[open] summary {
  margin-bottom: var(--pas-md);
}

.qr details > *:not(summary) {
  color: var(--texte-muet);
}

/* Le fil d'Ariane d'une page de texte : un simple retour, pas une arborescence. */
.retour {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-sm);
  color: var(--texte-muet);
  text-decoration: none;
  font-size: 0.9375rem;
  min-height: var(--cible);
}

.retour:hover {
  color: var(--texte);
  text-decoration: underline;
}

/* ── MOUVEMENT RÉDUIT : ce que la règle générale ne suffit pas à régler ─────────
 *
 * La règle générale du haut de la feuille écrase les durées à 0,01 ms et force une
 * seule itération. Pour une transition, c'est exactement ce qu'il faut. Pour une
 * ANIMATION en boucle, c'est un piège : l'élément est projeté sur son DERNIER cran.
 * Pour la démonstration du geste, le dernier cran est « la carte est partie » —
 * c'est-à-dire l'inverse de ce qu'on veut montrer à qui a demandé que rien ne bouge.
 *
 * D'où `animation: none` explicite, puis un état statique choisi : la carte est posée,
 * le voile est là, la pastille et son libellé sont visibles. On voit donc CE QUE LE
 * GESTE FAIT, sans que rien ne se déplace. Rien n'est perdu au passage.
 *
 * L'aurore, elle, se contente de `animation: none` : ses crans de début et de fin sont
 * identiques, et les taches restent au centre de leur orbite — exactement le `drift: 0`
 * de `AuroraPainter` sous `disableAnimations`.
 */
@media (prefers-reduced-motion: reduce) {
  .aurore__tache,
  .demo__carte,
  .demo__voile,
  .demo__pastille,
  .demo__libelle {
    animation: none !important;
  }

  .demo__voile {
    opacity: 0.75;
  }

  .demo__pastille {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.1);
  }

  .demo__libelle {
    opacity: 1;
  }

  /* Sans transition, la profondeur du carrousel se pose au premier frame. Elle n'est pas
     du mouvement : c'est une mise en page qui dit lequel des cinq écrans est regardé. */
  .ecran {
    transition: none !important;
  }
}

@media print {
  .aurore,
  .nav,
  .carrousel,
  .demo,
  .retour {
    display: none;
  }
  body {
    background: #fff;
    color: #000;
  }
}
