/* Circut — composants SPÉCIFIQUES à la landing (refonte).
   Chargé EN PLUS de site.css. Additif : ne redéfinit rien de la page d'attente
   ni de /s. Tokens et composants de base viennent de site.css.
   Quand la landing passe en prod, garder ce fichier à côté de site.css. */

/* Rythme des fonds (alternance) */
section.alt { background: var(--bg-2); }
.packs { background: var(--bg); }          /* on pilote le rythme ici, pas via site.css */

/* ── Récit (section fiabilité) : un paragraphe raconté, pas une carte ── */
.story {
  max-width: 640px; margin: 0 auto 44px;
  font-size: 1.08rem; line-height: 1.7; color: var(--text);
}
.story em { font-style: normal; color: var(--accent-text); font-weight: 600; }

/* ── Témoignage ── */
.quote { max-width: 620px; margin: 44px auto 0; text-align: center; }
.quote blockquote {
  font-family: var(--font-head); font-weight: 600;
  font-size: 1.22rem; line-height: 1.5; letter-spacing: -0.01em;
  color: var(--text); margin: 0 0 12px;
}
.quote figcaption { color: var(--text-2); font-size: 0.92rem; }

/* ── Visuel palette (⌘CC) : identité SOMBRE CONSTANTE, comme le vrai panneau
      (doctrine de l'app : fond opaque anthracite quel que soit le thème) ── */
.palette-demo {
  max-width: 480px; margin: 36px auto 0;
  background: #23232c; color: #ececf1;
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 14px; box-shadow: var(--shadow-lg);
  padding: 10px; text-align: left;
}
.palette-field {
  display: flex; align-items: center;
  font-size: 1.02rem; color: #ececf1;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 9px; padding: 11px 14px; margin-bottom: 8px;
}
.palette-caret {
  display: inline-block; width: 2px; height: 1.05em; margin-left: 2px;
  background: #6b66f2; animation: blink 1s steps(1) infinite;
}
.palette-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: 8px; font-size: 0.94rem;
}
.palette-row span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #b9b9c2; }
.palette-row .trigger-badge { flex: none; background: rgba(107, 102, 242, 0.22); color: #a8a3ff; box-shadow: inset 0 0 0 1px rgba(107, 102, 242, 0.35); }
.palette-row kbd {
  flex: none; font-family: var(--font-mono); font-size: 0.78rem; color: #b9b9c2;
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 5px; padding: 1px 6px;
}
.palette-row.selected { background: rgba(107, 102, 242, 0.18); }
.palette-row.selected span { color: #ececf1; }
.palette-note { text-align: center; color: var(--text-2); font-size: 0.95rem; max-width: 560px; margin: 26px auto 0; }

/* ── Feature-split : texte + visuel, deux colonnes ── */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; align-items: center; }
.split + .split { margin-top: 56px; }
.split.reverse .split-text { order: 2; }
.split h3 { font-size: 1.5rem; margin: 0 0 0.5em; }
.split p { color: var(--text-2); margin: 0; }
.split .eyebrow { margin-bottom: 10px; }

/* Le COMPARATIF `.compare` a DÉMÉNAGÉ dans site.css le 20/08/2026 : il sert
   désormais sur deux pages (la landing et /details/), et /details/ ne charge pas
   cette feuille-ci. Un composant partagé vit dans la feuille partagée — sans quoi
   il rend nu là où on l’emploie, en silence (mordu le jour même). */

/* ── Liste à puces chevron (section support) ── */
.feature-list { list-style: none; margin: 22px 0 0; padding: 0; display: grid; gap: 16px; }
.feature-list li { display: flex; gap: 12px; align-items: flex-start; }
.feature-list .chev { width: 17px; height: 17px; color: var(--accent-text); flex: none; margin-top: 4px; }
.feature-list strong { font-family: var(--font-head); }
.feature-list span { color: var(--text-2); }
.feature-list b { display: block; margin-bottom: 2px; }

/* ── Typographie : avant / après ── */
.ba { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.ba-item {
  flex: 1 1 200px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 16px 18px; background: var(--bg-card); box-shadow: var(--shadow-sm);
}
.ba-item .lbl {
  font-family: var(--font-head); font-weight: 600; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-2);
  display: block; margin-bottom: 8px;
}
.ba-item .txt { font-size: 1.15rem; }
.ba-item.good { border-color: var(--accent-line); }
.ba-item.good .txt { color: var(--text); }
.ba-item.bad .txt { color: var(--text-2); }

/* ── Meet the makers (deux personnes) ── */
.maker .card { max-width: 760px; margin: 0 auto; display: flex; gap: 28px; align-items: flex-start; padding: 32px; }
.maker-faces { display: flex; flex-direction: column; gap: 12px; flex: none; }
.avatar {
  width: 84px; height: 84px; border-radius: 50%; flex: none;
  background: linear-gradient(150deg, var(--brand-1), var(--brand-2));
  color: #fff; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-head); font-weight: 700; font-size: 1.7rem;
  box-shadow: var(--shadow-sm);
}
.maker-body h3 { margin: 0 0 4px; font-size: 1.25rem; }
.maker-role { color: var(--accent-text); font-family: var(--font-head); font-weight: 600; font-size: 0.9rem; margin: 0 0 14px; }
.maker-body p { color: var(--text-2); margin: 0 0 12px; }
.maker-body p:last-child { margin-bottom: 0; }

/* Responsive */
@media (max-width: 760px) {
  .split { grid-template-columns: 1fr; gap: 26px; }
  .split.reverse .split-text { order: 0; }   /* le texte repasse au-dessus du visuel */
  .maker .card { flex-direction: column; gap: 20px; align-items: center; text-align: center; }
}

/* ── Modale waitlist : ses styles ont DÉMÉNAGÉ le 17/08/2026 dans
      `site-drafts/assets/css/waitlist.css`, le jour où la modale est devenue
      commune à toutes les pages du chantier. Les deux landings chargent cette
      feuille-là en plus de celle-ci. Ne pas les réécrire ici. ── */
.hidden { display: none; } /* déclaré en dernier — gagne à spécificité égale */

/* ═════ Le hero sert DEUX publics (14/08/2026) — celui qui ignore ce
   qu'est l'expansion de texte, et celui qui la connaît et veut savoir en
   quoi Circut diffère. Deux dispositifs, deux poids.

   1. Le LIEN DE DÉFINITION du sous-titre, DISCRET par construction : il
      n'a pas à rivaliser avec le bouton d'accès (le hero n'a qu’une
      action primaire — budget d'accent), il est là pour qui bute sur le
      terme. D'où la couleur héritée et le seul soulignement, en teinte
      de bordure. Il porte en prime l'ancre interne « expansion de
      texte » vers la page qui vise exactement cette requête. */
.hero-sub a { color: inherit; text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 3px; }
.hero-sub a:hover { text-decoration-color: currentColor; }
/* 2. Les DEUX PORTES, sous la démo. Symétriques à dessein : servir le
      seul novice recréerait l'asymétrie en sens inverse. Elles vivent
      APRÈS le carrousel parce que les neuf cas sont la meilleure
      explication de la page — on ne renvoie personne ailleurs avant de
      les avoir montrés. Registre NEUTRE (gris, chevron de marque) :
      ce sont des sorties, pas des actions. */
.hero-doors { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 26px; margin: 24px 0 0; font-size: 0.95rem; }
.hero-doors a { color: var(--text-2); text-decoration: none; display: inline-flex; align-items: center; gap: 5px; }
.hero-doors a:hover { color: var(--text); }
.hero-doors .chev { width: 11px; height: 11px; opacity: 0.65; }

/* La note de configuration est une précision de BAS DE BOUTON, pas une
   seconde phrase du hero (demande Jonathan, 15/08) : un cran plus petite,
   et resserrée contre le bouton qu'elle qualifie. Override local — la page
   d'attente en prod, qui partage .hero-cta et .cta-note, garde les siennes. */
/* Le paragraphe porteur du bouton devient un CONTENEUR FLEX. Sans ça, le
   bouton (inline-flex) vit dans une ligne de texte, et l'interligne du
   paragraphe glisse ~17 px sous lui : mesuré, l'écart VISUEL était de 26 px
   là où le `gap` en annonçait 9. Le gap ne pilotait qu’un tiers de ce qu'on
   voyait. DEUX contributeurs invisibles, mesurés l'un après l'autre : cet
   interligne, et la marge par défaut du navigateur sur le <p> (17 px, jamais
   réinitialisée). Les deux neutralisés, le gap devient enfin la valeur
   réelle. Repère de justesse, la référence
   de Jonathan : sur /buy/, la note « Paiement sécurisé via Polar » est à
   14 px de son bouton — et là-bas c'est vrai au pixel, le bouton y étant
   frère direct de la note, sans paragraphe entre les deux. */
.waitlist-cta, .waitlist-cta-done { margin: 0; }
/* `:not(.hidden)` n'est PAS décoratif — `.hidden { display: none }` est
   déclarée en fin de fichier pour gagner à spécificité égale, et ce bloc-ci
   vient APRÈS elle. Un `display: flex` nu la battrait donc, et le bouton
   resterait affiché après une inscription réussie (waitlist.js échange
   #wl-cta et #wl-cta-done par cette classe). */
.waitlist-cta:not(.hidden) { display: flex; justify-content: center; }
.hero-cta { gap: 14px; }
.cta-note { font-size: 0.8rem; }

/* Le bandeau des « Pas de… » a rejoint le HERO (idée Jonathan, 15/08) : il y
   sert de rupture avant le carrousel, et il remonte au-dessus de la ligne de
   flottaison quatre différenciateurs — ce que cherche justement le visiteur
   qui connaît DÉJÀ l'expansion de texte. Sa marge haute vaut celle des
   onglets qui le suivent (36 px), pour qu'il respire autant des deux côtés :
   sans elle, son filet supérieur se colle à la note de configuration (0 px
   mesuré — `.trust` n'a aucune marge propre, son filet est le bord même de
   la liste). Portée limitée au hero : ailleurs, le bandeau garde son
   comportement de section. */
.hero .trust { margin-top: 36px; }

/* L’AIGUILLAGE — les deux sorties du hero, CÔTE À CÔTE depuis le 17/08/2026.
   Il remplace les deux écriteaux qui encadraient la démo (15/08) et, avec eux,
   `.demo-title`, `.demo-outro-q`, `.demo-sub` et `.hero-doors`, tous devenus
   sans balisage — supprimés le même jour, l’historique les garde.

   REGISTRE : gris, pas d’accent. Ce sont des SORTIES, pas des actions ; le seul
   bouton de cette moitié de page est celui de l’accès. La question est en pleine
   encre (elle doit se reconnaître d’un balayage), la réponse en gris, et son
   fragment cliquable garde le soulignement qui CHUCHOTE (35 %, 1 px, décalé de 4)
   — sans lui, la couleur serait le seul indice de clicabilité, et elle ne suffit
   pas à qui distingue mal les teintes.
   LE FILET vertical fait tout le travail de séparation : deux cartes auraient créé
   deux objets pleins sous le bouton, une bande de fond en aurait fait une ÉTAPE
   du parcours. En étroit il bascule à l’horizontale, seule façon de rester une
   frontière quand les colonnes s’empilent.
   900 px de large : plus étroit, les deux questions françaises wrappent ; plus
   large, les colonnes s’éloignent au point qu’on ne les compare plus. */
.signposts {
  display: grid; grid-template-columns: 1fr 1fr;
  max-width: 900px; margin: 44px auto 0;
}
.signpost { padding: 0 34px; }
.signpost + .signpost { border-left: 1px solid var(--border); }
/* SANS CHEVRON (17/08, relevé par Jonathan : « ça fait pas un peu beaucoup ? »).
   Compté : le hero en portait HUIT — quatre au bandeau, un au bouton, deux aux
   flèches du carrousel, et ces deux-ci. Un glyphe répété huit fois dans un écran
   ne guide plus rien. Le filet sépare, la pleine encre annonce ; le chevron ne
   faisait que s’ajouter. */
.signpost-q {
  margin: 0 0 5px;
  font-family: var(--font-head); font-weight: 600; font-size: 1.05rem; color: var(--text);
}
.signpost-a { color: var(--text-2); font-size: 0.98rem; margin: 0; }
.signpost-a a { text-decoration-line: underline; text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); text-underline-offset: 4px; text-decoration-thickness: 1px; }
.signpost-a a:hover { text-decoration-color: currentColor; }
@media (max-width: 700px) {
  .signposts { grid-template-columns: 1fr; max-width: 460px; }
  .signpost { padding: 0; }
  .signpost + .signpost { border-left: 0; border-top: 1px solid var(--border); margin-top: 18px; padding-top: 18px; }
}


/* ═══════════ SOUS LA TRANSITION — FORMULE DU 16/08/2026 ═══════════
   AJOUT SEULEMENT, comme tout ce qui touche cette feuille PARTAGÉE avec la
   page d’attente en production. Aucun sélecteur ci-dessous ne vise un élément
   que waitlist.js bascule par `.hidden` (déclarée ligne 206 pour gagner à
   spécificité égale, donc TOUT ajout arrive après elle).

   La v4 empilait huit sections de même forme, toutes centrées : rien ne
   dépassait, donc rien ne ressortait. La formule retenue avec Jonathan met
   TOUT à gauche, dans la colonne de prose de 660 px de /about et /privacy,
   avec leur H2 à chevron. Le rythme n’est plus porté par des côtés qui
   alternent mais par les FONDS, et par la coupure nette entre la thèse (le
   problème, d’un bloc) et la réponse (un argument par bloc). */

/* Cibles d’ancre : la nav est collante, sans quoi le titre passerait dessous. */
#thesis, #features, .arg { scroll-margin-top: 88px; }

/* ── La colonne. Même mesure que /about et /privacy — 660 px : c’est de la
   prose qui se LIT, et l’œil doit retomber au même point à chaque ligne
   (doctrine du 11/08, « ce qui s’affiche se centre, ce qui se lit se ferre à
   gauche »). Centrée en BLOC, texte à gauche. ── */
.prose-col { max-width: 660px; margin: 0 auto; }
/* La grille des frictions ne vit PAS dans cette colonne : elle est sa sœur
   dans le conteneur, faute de quoi elle resterait bridée à 660 px (un enfant à
   100 % d'une colonne bridée ne déborde pas). Voir sa doctrine plus bas. */
/* Le chevron PEND DANS LA MARGE (Jonathan, 16/08) : sans lui, le titre
   démarrait 26 px à droite de sa propre prose, et le bord gauche de la colonne
   faisait un escalier. Un margin-left négatif de la largeur exacte du chevron
   plus sa gouttière remet le premier mot du titre sur le bord commun, eyebrow
   et paragraphes compris. Il redevient un chevron EN FLUX sous 700 px, où il
   déborderait du padding de .container. */
.prose-col h2 {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 0 18px; margin-left: -26px; line-height: 1.25; font-size: 1.5rem;
}
/* Le TITRE s'aligne sur la ligne de base (`align-items` ci-dessus), pas sur
   son centre : sur un H2 qui passe à deux lignes, un alignement centré
   renverrait le chevron au milieu du bloc, loin du premier mot qu'il est
   censé ouvrir. La hauteur du chevron sur cette première ligne se règle à UN
   endroit, `.h-anchor .chev` de site.css. */
.prose-col h2 .chev { width: 16px; height: 16px; flex: none; }
/* Le chevron est aussi le LIEN D'ANCRE de la section (idée Jonathan) : on peut
   pointer #thesis ou #features sans lire l'URL. Il est déjà là, il est déjà
   dessiné, le rendre cliquable ne coûte aucun pixel. Discret au repos, pleine
   encre au survol — la règle du site veut qu’un survol qui éclaircit annonce
   un contrôle, donc que la zone soit VRAIMENT cliquable. */
.prose-col h2 .h-anchor {
  flex: none; line-height: 0; text-decoration: none;
  color: color-mix(in srgb, var(--accent-text) 55%, transparent);
  transition: color 120ms ease;
}
.prose-col h2 .h-anchor:hover,
.prose-col h2 .h-anchor:focus-visible { color: var(--accent-text); }
/* Les H3 sont les ARGUMENTS, rangés sous le chapeau « La réponse de Circut ».
   Même grammaire que les H2 — chevron pendant, texte aligné sur la prose —, un
   cran plus bas en corps. Le retrait est le MÊME (26 px) : c'est le bord gauche
   commun qui compte, pas la hiérarchie. */
.prose-col h3 {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 0 16px; margin-left: -26px; line-height: 1.3; font-size: 1.24rem;
}
/* 16 px comme le chevron des H2, et non 15 : le retrait vaut 26 px pour tous
   les titres, donc un glyphe plus étroit décalerait le texte du H3 d'un pixel
   vers la gauche. Le bord commun prime ; la hiérarchie se dit par le CORPS du
   titre, jamais par la taille du chevron. */
.prose-col h3 .chev { width: 16px; height: 16px; flex: none; }
.prose-col h3 .h-anchor {
  flex: none; line-height: 0; text-decoration: none;
  color: color-mix(in srgb, var(--accent-text) 55%, transparent);
  transition: color 120ms ease;
}
.prose-col h3 .h-anchor:hover, .prose-col h3 .h-anchor:focus-visible { color: var(--accent-text); }

/* Le SURTITRE (`.eyebrow` dans le code, « surtitre » en typographie de presse)
   n'a PAS de chevron ici — celui-ci vit sur le titre, où il pend dans la marge.
   C'est un simple libellé, aligné sur le même bord gauche que tout le reste.
   Il sert aux DEUX niveaux : au-dessus d'un H2 il nomme la partie, au-dessus
   d'un H3 il nomme la fonctionnalité. Même style, la hiérarchie étant portée
   par le corps du titre qui suit — un surtitre rapetissé au second niveau
   deviendrait illisible avant d'être perçu comme subordonné. */
.prose-col .eyebrow { margin-bottom: 10px; }
.prose-col p { color: var(--text-prose); margin: 0; }
.prose-col p + p { margin-top: 14px; }
/* Les visuels sont posés ENTRE le titre et le texte (ordre voulu par
   Jonathan : H2, visuel, texte). Ils respirent des deux côtés, et ils se
   CENTRENT dans la colonne — ils illustrent, ils ne se lisent pas.
   `margin: 0 auto 22px` et non un `margin-left: auto` posé ailleurs : ce
   sélecteur vaut (0,2,0) et écraserait un `.emptylib` seul (0,1,0). C'est
   exactement ce qui est arrivé, et le visuel est resté ferré à gauche pendant
   que son voisin `.deadsearch`, absent de la liste, se centrait bien. Le
   centrage doit vivre ICI, où rien ne peut le battre. */
.prose-col .emptylib, .prose-col .seed, .prose-col .keys,
.prose-col .stack, .prose-col .compare, .prose-col .grid-4,
.prose-col .deadsearch, .prose-col .missednotif,
.prose-col .emojimap, .prose-col .varmenu,
.prose-col .shot-editor { margin: 0 auto 22px; }
/* Un visuel qui SUIT un paragraphe se collait au texte : la règle ci-dessus ne
   pose aucune marge haute, parce qu’un visuel placé juste sous son titre n'en
   veut pas. Ce sélecteur vaut (0,2,1) et bat donc la règle partagée (0,2,0) —
   la spécificité, cette fois anticipée plutôt que payée. */
.prose-col p + .emptylib, .prose-col p + .seed, .prose-col p + .keys,
.prose-col p + .stack, .prose-col p + .compare,
.prose-col p + .grid-4, .prose-col p + .deadsearch,
.prose-col p + .shot-editor { margin-top: 32px; }
/* Les ICÔNES de section suivent la même règle depuis qu’elles ont
   quitté le dessous du H3 (29/08) : leur 6 px était l’air d’un titre, pas
   celui d’un paragraphe. Le clavier de #keyboard-first, lui, suit toujours
   son H3 — le sélecteur `p +` le laisse donc tranquille. */
.prose-col p + .pz-mac, .prose-col p + .qol-grid { margin-top: 32px; }
.arg .quote { margin-top: 40px; }

/* ── La voix du lecteur. Ce n’est pas un témoignage (celui-là a .quote, centré
   et attribué) mais une objection rapportée, celle que tout le monde a déjà
   formulée. D’où le filet à gauche, la mesure du parlé, et aucune signature :
   c’est le lecteur qui parle. ── */
.voice {
  margin: 22px 0; padding: 4px 0 4px 20px;
  border-left: 3px solid var(--accent-line);
  font-size: 1.14rem; color: var(--text); font-style: italic;
}

/* ── Les quatre frictions. Le chevron tient lieu de puce, comme les .proofs
   de /privacy — ce sont des constats qu’on avance, pas un inventaire. ── */
/* ── LES FRICTIONS EN CARTES (17/08, essai demandé par Jonathan) — elles
   comblent le vide laissé par le retrait du second visuel, et elles rompent un
   long pavé de prose en fin de section.

   LE VISUEL DE CHAQUE CARTE EST SON NUMÉRO, pas un pictogramme. C'est un choix,
   pas une facilité : aucune icône ne dit « la recherche ne regarde pas au bon
   endroit » sans devenir un glyphe générique, et un pictogramme générique est
   une décoration, pas un signal — précisément ce que le budget d'accent
   proscrit. Le chiffre, lui, ne prétend rien : il compte, il structure, et il
   fait FAMILLE avec le grand « 0 » de la bibliothèque vide, deux écrans plus
   haut. Même gris, même poids, même rôle.

   La grille SORT de la colonne de prose (660 px) pour prendre la largeur du
   conteneur : à quatre cartes sur deux rangs dans 660 px, chacune tomberait à
   320 px pour trois lignes de texte. C'est aussi ce qui donne à la fin de
   section une texture différente de son début.

   Si l'essai ne convainc pas, le retour aux puces est un `display: block` et
   trois lignes en moins. ── */
/* DEUX PAR LIGNE. La pile a été essayée le 17/08, à l'arrivée d'une cinquième
   friction, puis abandonnée le jour même : l'abonnement a quitté la liste pour
   devenir la CHUTE de la section (voir .frictions-outro), et quatre cartes
   remplissent la grille sans trou. Chiffres du jour, si le sujet revient —
   grille de 4 : 396 px, grille de 5 : 602 px (avec le trou), pile de 5 : 788 px.
   Une pile devrait être BORNÉE (~720 px) : en pleine largeur, une carte donne
   des lignes de 150 caractères, deux fois la mesure de confort. */
.frictions {
  list-style: none; margin: 26px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;
  max-width: none;
}
.frictions li {
  position: relative; color: var(--text);
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 22px 24px 22px 76px;
}
/* L'ICÔNE TEINTÉE remplace le chiffre (Jonathan, 17/08) — même forme que les
   tuiles de /text-expansion/ et de l'écran Collections de l'app : un carré
   arrondi de 40 px, fond à 14 % de la teinte, glyphe à pleine encre. Les
   glyphes viennent de la famille anguleuse du site (20×20, trait 1,8), pas
   d'une bibliothèque tierce.

   QUATRE TEINTES et non l'accent partout : quatre tuiles indigo auraient
   dépensé le budget d'accent sur un CONSTAT, alors qu'il est réservé aux
   signaux. Ce sont des vignettes de décor, comme les feux de la fenêtre plus
   haut — d'où des teintes franches mais des fonds à 14 %, qui posent la couleur
   sans la faire crier. Les quatre sont tenues à distance de l'indigo de marque,
   pour qu'aucune ne se lise comme un signal du produit.

   Elles sont posées en RETRAIT, dans la gouttière : dans le flux, elles
   pousseraient le titre et casseraient l'alignement des quatre cartes. */
.fr-ico {
  position: absolute; left: 22px; top: 20px;
  width: 38px; height: 38px; border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
}
.fr-ico svg { width: 19px; height: 19px; }
.fr-ico-1 { background: rgba(224, 138, 31, 0.14); color: #c9761a; }  /* lenteur */
.fr-ico-2 { background: rgba(214, 69, 52, 0.14);  color: #cf4433; }  /* rupture */
.fr-ico-3 { background: rgba(45, 122, 200, 0.14); color: #2b78c6; }  /* oubli   */
.fr-ico-4 { background: rgba(24, 150, 116, 0.14); color: #16906f; }  /* recherche */
@media (prefers-color-scheme: dark) {
  /* En sombre, les mêmes teintes s'assombrissent trop : les glyphes montent
     d'un cran en clarté, les fonds descendent d'un cran en opacité. */
  .fr-ico-1 { background: rgba(240, 170, 70, 0.16);  color: #f0aa46; }
  .fr-ico-2 { background: rgba(240, 110, 95, 0.16);  color: #f06e5f; }
  .fr-ico-3 { background: rgba(90, 165, 240, 0.16);  color: #5aa5f0; }
  .fr-ico-4 { background: rgba(60, 195, 155, 0.16);  color: #3cc39b; }
}
.frictions b { display: block; margin-bottom: 5px; font-family: var(--font-head); }
.frictions li > span:last-child { color: var(--text-2); font-size: 0.96rem; }

@media (max-width: 760px) {
  .frictions { grid-template-columns: 1fr; }
}

/* LA CHUTE DE LA SECTION (17/08/2026, arbitrage de Jonathan). L'abonnement a
   d'abord été écrit en cinquième carte ; il en est sorti pour une raison de
   NATURE : les quatre frictions sont des obstacles d'USAGE, celui-ci est un
   grief commercial. En carte, il passait pour un obstacle d'usage de plus ; en
   chute, il couronne les autres, ce qu'il est vraiment. Il reprend la mesure et
   le fer de la prose de la section, avec un blanc franc qui le détache des
   cartes — c'est une conclusion, pas une cinquième entrée. */
.frictions-outro { max-width: 660px; margin: 32px auto 0; color: var(--text-prose); }

/* ── LE MUR, montré plutôt que dit : une bibliothèque à zéro entrée. Le zéro
   est le héros du visuel, d’où sa taille. L’outil n’est pas nommé et ne peut
   pas l’être — ce serait aussi bien le premier lancement de Circut, ce qui
   est exactement le propos. ── */
.emptylib {
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; background: var(--bg-window); box-shadow: var(--shadow-sm);
}
.emptylib-bar {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 14px; border-bottom: 1px solid var(--border); background: var(--bg-2);
}
/* Les FEUX macOS, aux vraies couleurs (Jonathan, 17/08). En gris, la fenêtre
   avait l'air éteinte ; ce sont les seules touches de couleur de toute la
   moitié haute de la page, et elles ne coûtent aucun budget d'accent puisque
   ce ne sont pas des signaux de l'interface mais du décor système. Teintes de
   macOS, pas approximées. */
.emptylib-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; background: var(--border); }
.emptylib-dot:nth-child(1) { background: #ff5f57; }
.emptylib-dot:nth-child(2) { background: #febc2e; }
.emptylib-dot:nth-child(3) { background: #28c840; }
.emptylib-name { margin-left: 8px; font-size: 0.86rem; color: var(--text-2); }
.emptylib-body { padding: 34px 20px 30px; text-align: center; }
.emptylib-zero {
  font-family: var(--font-head); font-weight: 800; font-size: 3.2rem; line-height: 1;
  color: var(--text-2); opacity: 0.35;
}
.emptylib-msg { margin-top: 10px; color: var(--text-2); font-size: 0.98rem; }
.emptylib-btn {
  display: inline-block; margin-top: 18px; padding: 7px 15px;
  border: 1px dashed var(--border); border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 0.9rem;
}

/* Le SECOND H2 de la thèse (l'ancien H3). C'est la seconde moitié du
   diagnostic, pas une sous-partie de la première : même corps que le premier,
   et un blanc généreux au-dessus pour marquer la bascule. */
/* `.prose-col h2` vaut (0,2,1) et écrasait `.thesis-second` (0,1,0) : le titre
   restait collé au paragraphe du dessus, malgré la règle. */
.prose-col h2.thesis-second { margin-top: 64px; }

/* ── La « graine » : une question de l’installation devient un circut. C’est
   le seul visuel vraiment NEUF de cette passe, parce que l’argument qu’il
   porte (les suggestions personnalisées du questionnaire) n’était écrit nulle
   part et qu’aucun composant existant ne le montrait. ── */
.seed { display: grid; gap: 0; }
.seed-step {
  display: grid; gap: 4px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px 20px; box-shadow: var(--shadow-sm);
}
.seed-lbl {
  font-family: var(--font-head); font-weight: 600; font-size: 0.76rem;
  letter-spacing: 0.02em; color: var(--text-2);
}
.seed-q { font-size: 1.02rem; color: var(--text); }
.seed-a, .seed-r { font-size: 1.02rem; color: var(--text-2); }
.seed-out { border-color: var(--accent-line); }
.seed-out .seed-r { color: var(--text); }
/* Le trait qui relie les deux étages porte le chevron de marque tourné vers le
   bas : c’est le glyphe « suite » du site, pas une flèche de plus. */
.seed-link { display: flex; justify-content: center; align-items: center; height: 34px; position: relative; }
.seed-link::before { content: ""; position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); }
.seed-chev { width: 17px; height: 17px; color: var(--accent-text); transform: rotate(90deg); background: var(--bg-2); position: relative; }
/* Le chevron mange le trait sur le fond de SA section. Les deux blocs qui
   portent une .seed sont en `.alt` ; si l’un passait sur le fond de base, il
   faudrait lui donner var(--bg) ici, sinon le trait ressort derrière. */
.arg:not(.alt) .seed-chev { background: var(--bg); }

/* ── La séquence de touches. Elle MONTRE ce que le texte affirme (trois
   secondes sans lâcher le clavier) : chaque étape est une frappe, pas un clic.
   La dernière porte le liseré d’accent, c’est celle qui conclut. ── */
.keys { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.keys li {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 11px 14px;
}
.keys li:last-child { border-color: var(--accent-line); }
.keys kbd {
  flex: none; font-family: var(--font-mono); font-size: 0.82rem; color: var(--text);
  border: 1px solid var(--border); border-radius: 5px; padding: 2px 7px; background: var(--bg);
}
.keys .trigger-badge { flex: none; font-family: var(--font-mono); }
.keys span { color: var(--text-2); font-size: 0.94rem; }

/* Deux vignettes empilées dans un même visuel (la palette qui retrouve, la
   notification qui rappelle : un argument, deux preuves). Le margin-top propre
   à .notif est neutralisé, c’est le gap qui espace. */
.stack { display: grid; gap: 14px; }
.stack .notif { margin-top: 0; }

/* ── La grille : l’INVENTAIRE. Des titres qui sont des NOMS, une ligne chacun.
   C’est cette FORME, et non l’URL, qui l’empêche de faire doublon avec les
   arguments, dont les titres sont des AFFIRMATIONS. Elle sort de la colonne de
   prose : une grille n’est pas de la prose. ── */
.feat-grid {
  list-style: none; margin: 26px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px;
}
.feat-grid li {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px 22px;
}
.feat-grid b {
  display: block; margin-bottom: 6px;
  font-family: var(--font-head); font-weight: 700; font-size: 1.02rem; color: var(--text);
}
.feat-grid span { color: var(--text-2); font-size: 0.94rem; }

@media (max-width: 900px) {
  .feat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .feat-grid { grid-template-columns: 1fr; }
  .prose-col h2 { font-size: 1.32rem; }
  .prose-col h3 { font-size: 1.18rem; }
}


/* ── Réglages du 16/08 au soir, sur retours de Jonathan à l'écran ── */

/* L'écart hero → thèse valait 172 px (88 de bas de hero, 84 de haut de
   section) : un trou, pas une respiration. `.hero` est PARTAGÉE avec la page
   d'attente en production, donc on ne la touche QUE là où #thesis existe,
   c'est-à-dire sur les deux landings et jamais en prod. */
body:has(#thesis) .hero { padding-bottom: 48px; }
.thesis { padding-top: 48px; }

/* Les deux vignettes de bibliothèque tenaient toute la colonne (660 px) pour
   dire une seule chose. Bridées, elles redeviennent une illustration au lieu
   d'occuper l'écran. Ferrées à gauche, comme tout le reste. */
.emptylib { max-width: 380px; }

/* LE CHEVRON PEND, MÊME EN ÉTROIT (21/08/2026, relevé de Jonathan à l’écran).
   La règle d’avant annulait le retrait sous 700 px, au motif que « le chevron
   entrerait dans le padding de .container ». Elle déplaçait le défaut plus
   qu’elle ne le réglait : rendu au FLUX, le chevron prenait le bord commun
   — 24 px, celui du surtitre et de la prose — et repoussait le TEXTE du titre
   à 50. Le titre devenait le seul élément de la colonne à ne pas s’aligner,
   ce qui se voit d’autant plus que tout le reste est réglé au pixel.
   C’est l’inverse qu’il faut : le début du titre sur le bord commun, le
   chevron DEHORS. Il n’entre donc plus dans le padding, il l’OCCUPE — c’est
   la fonction d’un chevron pendant, et 24 px sont exactement la marge qu’il
   lui faut. Le retrait passe de 26 à 22 px pour cela : 16 de glyphe + 6 de
   gouttière, ce qui le pose à 2 px du bord de l’écran au lieu de −2, où il
   était coupé. La gouttière perd 4 px par rapport au large, imperceptible à
   cette taille et invisible ailleurs.
   LE SEUIL EST 720 ET NON 700, et c’est une bande cassée de plus qui tombe :
   la colonne décolle du padding quand (largeur − 660) / 2 dépasse 24, soit à
   708{NB}px. Entre 701 et 711, l’ancienne règle rendait donc le retrait plein de
   26{NB}px à une colonne encore collée à 24 — chevron à −2, coupé par le bord.
   Le défaut préexistait, sur onze pixels de large ; s’arrêter à 700 l’aurait
   laissé derrière. 720 est la première valeur ronde au-dessus de 708, et à
   720 la colonne est déjà à 30, où le retrait plein pose le chevron à 4.
   PORTÉE, mesurée avant d’écrire : les DEUX H2 de #thesis, seuls titres
   ferrés à gauche des deux landings — les 20 autres sont centrés depuis le
   18/08, chevron en flux dans le groupe centré, et les cinq autres pages du
   chantier n’en ont aucun. Les centrés gardent `margin-left: 0` par une
   spécificité plus forte : cette règle ne les atteint pas (vérifié). */
@media (max-width: 720px) {
  .prose-col h2, .prose-col h3 { margin-left: -22px; gap: 6px; }
}


/* ── LA RECHERCHE QUI NE TROUVE RIEN (16/08, sur validation de Jonathan) —
   remplace la bibliothèque endormie sous « Même les convaincus n'en tirent
   qu’une fraction ». Elle dit la MÊME friction de façon frontale, et surtout
   elle prépare la palette de Circut plus bas : même requête, deux réponses.
   La requête n'est pas un mot que l'on nommerait un snippet — c'est un mot du
   CONTENU, à demi retenu, exactement ce qu'on tape quand on ne se souvient plus
   du nom. C'est ce décalage que la note du bas rend absurde.
   BALISAGE RETIRÉ le 17/08 — Jonathan préfère voir la page sans, quitte à le
   rappeler plus tard. Les styles restent, le balisage se recolle en une minute
   depuis git. La .deadlib qui l'avait précédée, elle, est morte deux fois :
   ses styles ont été supprimés pour de bon. ── */
.deadsearch {
  max-width: 400px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-card); box-shadow: var(--shadow-sm); overflow: hidden;
}
.ds-field {
  display: flex; align-items: center; gap: 9px;
  padding: 13px 15px; border-bottom: 1px solid var(--border);
}
.ds-glass { width: 15px; height: 15px; color: var(--text-2); flex: none; }
.ds-q { color: var(--text); font-size: 1rem; }
.ds-caret {
  width: 1.5px; height: 1.05em; background: var(--accent-text); margin-left: -3px;
  animation: ds-blink 1.1s steps(1) infinite;
}
@keyframes ds-blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }
.ds-empty { padding: 26px 15px 22px; text-align: center; color: var(--text-2); font-size: 0.98rem; }
.ds-foot {
  padding: 11px 15px; border-top: 1px solid var(--border); background: var(--bg-2);
  font-size: 0.86rem; color: var(--accent-text);
}

/* ── LE LOGO, SEUL (16/08, troisième version) — la piste de chevrons faisait
   guirlande, et Jonathan a raison : la marque suffit. Le boost n'a pas disparu,
   il s'est REPLIÉ DANS le logo. Ses deux chevrons s'allument l'un après
   l'autre, gauche puis droite, et la tuile respire avec eux. C'est le pad
   d'accélération réduit à son essence, et ça reste sobre — ce que l'app est
   partout ailleurs.

   Les arrêts du dégradé passent par des VARIABLES : sur un fond anthracite la
   tuile garde ses couleurs vraies (celles de favicon.svg), sur une bande
   indigo il faut les éclaircir sous peine de la voir se fondre dans son propre
   fond. Chaque ambiance les règle plus bas. ── */
.boost { display: flex; justify-content: center; margin: 0 0 34px; }
.boost-s1 { stop-color: var(--boost-1, #4945D1); }
.boost-s2 { stop-color: var(--boost-2, #282483); }
.boost-ring { stroke-opacity: var(--boost-ring, 0.18); }
.boost-mark {
  width: 76px; height: 76px; display: block; border-radius: 17px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.30);
  animation: none 3s cubic-bezier(0.34, 1.2, 0.64, 1) 1;
}
/* La tuile respire, elle ne saute pas : 1,04 au sommet, et vingt pour cent du
   cycle en mouvement. Le reste du temps elle est immobile — une animation
   permanente sur une page qu'on lit est une nuisance, pas une signature. */
@keyframes boost-mark {
  0%, 24%, 100% { transform: scale(1);     box-shadow: 0 8px 26px rgba(0, 0, 0, 0.30); }
  9%            { transform: scale(1.04);  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.30),
                                                       0 0 26px var(--boost-halo, rgba(107, 102, 242, 0.5)); }
}
/* Les deux chevrons prennent la vague l'un après l'autre : c'est ce décalage
   qui fait la POUSSÉE. Allumés ensemble, le logo clignoterait. */
.boost-v { stroke: rgba(255, 255, 255, 0.92); animation: none 3s cubic-bezier(0.4, 0, 0.6, 1) 1; }
.boost-v:nth-of-type(1) { animation-delay: 0s; }
.boost-v:nth-of-type(2) { animation-delay: 0.12s; }
@keyframes boost-v {
  0%, 20%, 100% { stroke: rgba(255, 255, 255, 0.92); filter: none; }
  7%            { stroke: #fff; filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.9)); }
}
/* UNE FOIS, QUAND ON ARRIVE (03/10/2026, Grégoire : « l’icône pulse de manière
   un peu aléatoire, c’est assez surprenant »). La vague revenait toutes les
   3 s, à l’infini, sans rien qui la déclenche : le lecteur la surprenait du
   coin de l’œil, au hasard de sa lecture. Le pad s’allume désormais quand on
   passe dessus, une fois par visite : site.js pose `.is-lit` quand le logo
   entre entier dans les deux tiers hauts de l’écran, et ne la retire plus.
   Les animations ne portent plus que leur NOM sous la classe ;
   l’ombre de la tuile et la teinte des chevrons au repos, que les images clés
   portaient seules, vivent dans le style de base. Sans JavaScript, le logo
   reste immobile. */
.boost.is-lit .boost-mark { animation-name: boost-mark; }
.boost.is-lit .boost-v { animation-name: boost-v; }

/* ── LA BANDE DE RÉPONSE — la rupture de la page.

   LA CONTRAINTE QUI DÉCIDE, et qu’un premier jet avait manquée : en mode
   SOMBRE, une bande « plus sombre » n'existe pas. `--bg` vaut #0f0f14 et
   `--bg-2` #15151c ; un anthracite à #141420 s'y confond avec les deux, et les
   trois grandes bandes de la page deviennent indiscernables. La rupture en
   sombre ne peut venir que de la TEINTE ou de l'ÉLÉVATION. Toute ambiance
   proposée ici est donc vérifiée dans les DEUX thèmes, jamais dans un seul.

   Le deuxième garde-fou est la doctrine du CLAUDE.md : l'indigo est un BUDGET
   DE SIGNAL, pas une décoration. Une bande à pleine saturation le dépense en
   papier peint et il ne reste plus rien pour ce qui doit ressortir. D'où le
   choix : un indigo PROFOND ET DÉSATURÉ, qui tient la couleur de marque sans
   crier, et laisse l'accent vif au logo, au surtitre et aux liens.

   ARRÊTÉ par Jonathan le 17/08 après comparaison à l'écran de quatre ambiances
   (dégradé de marque plein, indigo sourd, surface élevée sans teinte, constat
   voilé d'indigo). L'outil qui permettait de les comparer — essai-fonds.js, son
   script dans les deux landings et les trois ambiances perdantes — a été
   supprimé le jour même : un sélecteur de thème qu'on oublie de retirer devient
   une fonctionnalité que personne n'a demandée. Les valeurs écartées sont dans
   l'historique, commit f27128f et antérieurs. ── */
/* ── LE PALIER DU CONSTAT. `.alt` (--bg-2) ne suffit pas à séparer TROIS
   grandes bandes : mesuré à ΔE 3,4 du fond de page en sombre, c'est-à-dire à
   la limite du perceptible. Le site s'en contente pour alterner des sections
   voisines ; ici on marque une FRONTIÈRE, ce qui demande un cran de plus.
   Valeur choisie pour rendre ~ΔE 7 dans les deux thèmes, soit franchement vu
   sans devenir une seconde couleur. ── */
.thesis { background: light-dark(#f1f1f7, #1b1b25); }
/* La section a PERDU sa classe `alt` dans le HTML, et c'est le fond du
   correctif : `section.alt` vaut (0,1,1) et battait `.thesis` (0,1,0), si bien
   que la bande gardait --bg-2 quoi qu'on écrive ici. Plutôt que de surenchérir
   en spécificité, on a retiré le conflit — cette bande ne participe plus à
   l'alternance générique, elle porte sa propre couleur. */

.answer {
  background: #262150; color: #fff;
  --boost-1: #5B57E8; --boost-2: #312CA0; --boost-ring: 0.26;
  --boost-halo: rgba(168, 163, 255, 0.55);
}

/* Toutes les encres de la bande sont réécrites : les jetons --text et --text-2
   sont pensés pour des fonds neutres, et laisseraient un paragraphe gris
   anthracite sur un fond sombre. */
.answer .eyebrow { color: #b8b3ff; }
.answer .prose-col h2 { color: #fff; }
.answer .prose-col p { color: rgba(255, 255, 255, 0.84); }
/* Les liens du chapeau CHUCHOTENT (17/08/2026, demande de Jonathan) : ils
   reprennent au mot le langage des écriteaux du hero — 35 % de l'encre
   courante, 1 px, décalé de 4 — au lieu des 45 % qu'ils portaient. Deux liens
   dans une phrase de trois lignes, sur un aplat de marque, c'était deux
   sollicitations là où le lecteur vient lire une thèse. Le blanc PUR contre les
   84 % du paragraphe reste la distinction principale ; le trait dit seulement
   que c'est cliquable, et le survol le rend franc. */
.answer .prose-col a:not(.h-anchor) {
  color: #fff;
  text-decoration-line: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 4px; text-decoration-thickness: 1px;
}
.answer .prose-col a:not(.h-anchor):hover { text-decoration-color: currentColor; }
.answer .prose-col h2 .h-anchor { color: rgba(255, 255, 255, 0.45); }
.answer .prose-col h2 .h-anchor:hover,
.answer .prose-col h2 .h-anchor:focus-visible { color: #fff; }

/* CENTRAGE de la bande (essai demandé par Jonathan). Il se défend : ce bloc
   n'est pas de la prose, c'est un CARTON DE TITRE — deux lignes qui ne wrappent
   pas longuement. La doctrine du 11/08 dit exactement cela, « ce qui s'affiche
   se centre, ce qui se lit se ferre à gauche ». Et centré, il devient le SEUL
   bloc de la page à l'être, ce qui renforce la rupture au lieu de passer pour
   un accident. Le chevron cesse alors de pendre : sur un titre centré, un
   glyphe en marge décentrerait l'ensemble. */
.answer .prose-col { text-align: center; }
/* Les deux lignes du paragraphe centré tombaient de largeurs très inégales, la
   première étant remplie au maximum. `text-wrap: balance` répartit les mots
   entre les lignes plutôt que de bourrer la première ; il ne s'applique qu'aux
   blocs courts (quelques lignes), donc il est sans effet ailleurs. */
.answer .prose-col h2, .answer .prose-col p { text-wrap: balance; }
.answer .prose-col h2 { justify-content: center; margin-left: 0; }

@media (prefers-reduced-motion: reduce) {
  .boost.is-lit .boost-mark, .boost.is-lit .boost-v { animation-name: none; }
  .ds-caret { animation: none; }
}


/* ── LE SOMMAIRE FLOTTANT (16/08, demande de Jonathan) — la page devient
   longue, et rien ne permettait de sauter d'une section à l'autre une fois
   engagé dedans. Il se remplit tout seul à partir des liens d'ancre des H2
   (voir site-drafts/assets/js/toc.js) : aucune liste à tenir à jour.

   IL N'EXISTE PAS SOUS 1180 px. La colonne de prose fait 660 px dans un
   conteneur de 1000 ; en dessous, il n'y a plus de place à gauche sans
   chevaucher le texte, et un sommaire qui recouvre ce qu'il indexe est pire
   que pas de sommaire. Le seuil se calcule : 1000 de conteneur + 2 × 24 de
   marge minimale + 132 de sommaire.

   Il est ABSENT au chargement et apparaît une fois le hero passé — en haut de
   page, le lecteur n'a rien à survoler et le sommaire volerait l'attention du
   bouton. L'opacité et le léger déport font l'arrivée ; `visibility` coupe le
   survol tant qu'il est absent, ce que l'opacité seule ne fait pas. ── */
.toc { display: none; }

@media (min-width: 1180px) {
  /* ANCRÉ EN HAUT depuis le 20/08/2026, et c'est le dépliage qui l'exige : le
     rail change de hauteur quand on passe d'une famille à l'autre, et un bloc
     centré verticalement SAUTE à chaque changement. Ancré par le haut, seules
     les entrées situées sous la famille ouverte descendent. */
  .toc {
    display: block; position: fixed; z-index: 5;
    top: 108px; left: 28px; transform: translateX(-8px);
    max-width: 190px;
    opacity: 0; visibility: hidden;
    transition: opacity 220ms ease, transform 220ms ease, visibility 220ms;
  }
  .toc.toc-on { opacity: 1; visibility: visible; transform: translateX(0); }
  .toc ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
  .toc a {
    display: block; padding: 6px 10px; border-left: 2px solid var(--border);
    color: var(--text-2); font-size: 0.84rem; line-height: 1.35;
    text-decoration: none; transition: color 120ms ease, border-color 120ms ease;
  }
  .toc a:hover { color: var(--text); border-left-color: var(--text-2); }
  /* L'entrée courante prend l'accent : c'est un état, pas une décoration —
     le budget d'accent du CLAUDE.md l'autorise expressément. */
  .toc a[aria-current] { color: var(--accent-text); border-left-color: var(--accent); }
  /* SECOND NIVEAU — les arguments, sous le chapeau de la réponse. Le retrait
     et le corps plus petit disent la subordination ; le filet de gauche reste
     commun aux deux niveaux, c'est lui qui fait la colonne. */
  /* Les arguments ne s'affichent QUE dans leur famille (toc.js pose .toc-open).
     `grid-template-rows` plutôt que `height` : la hauteur d'une entrée n'est pas
     connue d'avance, et `auto` ne s'anime pas. */
  .toc .toc-n2 { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 180ms ease; }
  .toc .toc-n2 > a { overflow: hidden; }
  .toc .toc-n2.toc-open { grid-template-rows: 1fr; }
  .toc .toc-n2 a { padding-left: 22px; font-size: 0.8rem; }
  .toc .toc-n2:not(.toc-open) a { padding-top: 0; padding-bottom: 0; }
@media (prefers-reduced-motion: reduce) { .toc .toc-n2 { transition: none; } }
}

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


/* ── L'ÉCHO qui referme un argument (16/08) : une phrase courte qui renvoie au
   mur nommé dans la thèse. Elle n'est pas un paragraphe de plus, c'est une
   CHUTE — d'où l'encre d'accent, le corps un cran au-dessus et le blanc qui la
   décolle. Une par argument au plus, sinon le procédé s'use et la page se met
   à faire des effets de manche. ── */
/* LA FIGURE DE #native (08/09/2026) — DEUX chiffres VÉRIFIABLES, pas deux
   adjectifs : le DMG (8,7 Mo) et le banc du 03/09 (0,815 µs pour reconnaître
   un déclencheur parmi 5 667). Un troisième, « 2 dépendances », a vécu un
   premier jet : seul chiffre de CAUSE parmi des chiffres d'EFFET, il ouvrait
   la question qu'il prétendait fermer (remarque de Jonathan). Il reste sur
   la fiche de presse, où un journaliste sait le lire. Même famille
   que .compare-row : carte, bordure, ombre légère. Un visuel qui SUIT un
   paragraphe reprend les 32 px de la règle du 16/08. */
.native-figs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 0 auto 22px; }
.prose-col p + .native-figs { margin-top: 32px; }
.native-figs .fig { padding: 18px 14px 16px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg-card); box-shadow: var(--shadow-sm); text-align: center; }
.native-figs .n { display: block; font-family: var(--font-head); font-weight: 700; font-size: 1.75rem;
  line-height: 1.1; color: var(--text); letter-spacing: -0.01em; }
.native-figs .l { display: block; margin-top: 7px; font-size: 0.86rem; line-height: 1.35; color: var(--text-2); }
@media (max-width: 560px) { .native-figs { grid-template-columns: 1fr; gap: 10px; } }

.arg-echo {
  margin-top: 22px; color: var(--accent-text);
  font-family: var(--font-head); font-weight: 600; font-size: 1.08rem;
}


/* ── RYTHME VERTICAL (16/08, deuxième demande de Jonathan sur le sujet) —
   site.css donne 84 px de padding vertical à TOUTE <section>, donc 168 px
   entre deux blocs, mesurés jusqu'à 258 là où le dernier élément portait sa
   propre marge. C'est un trou, pas une respiration.

   On ne touche PAS la règle de site.css : elle sert la page d'attente en
   production. On surcharge les seules classes de la landing. Le résultat est
   112 px entre deux blocs, moitié moins que le pire cas mesuré. ── */
.thesis, .answer, .arg, .more,
.switchers, .maker, .faq { padding: 56px 0; }
.thesis { padding-top: 48px; }
/* La carte d'appel finale GARDE ses 84 px : c'est la seule section qu'on veut
   voir arriver, et la respirer sert son propos. Resserrer partout ailleurs
   n'aurait aucun sens si la fin de page se resserrait aussi. */

/* LE SOUS-TITRE QUI SUIT LA DÉMO (17/08/2026, relevé par Jonathan : « cette
   phrase est comme assombrie par le carrousel »). Le diagnostic n’est pas celui
   qu’on croit : les deux lignes du bloc sont dans le MÊME paragraphe et rendent
   la même valeur, rgb(155,155,166) — mesurée. Ce qui noircit la première, c’est
   l’OMBRE PORTÉE de la fenêtre, `rgba(0,0,0,0.55) 0 24px 70px` : décalée de 24 px
   vers le bas et floutée sur 70, elle se peint PAR-DESSUS le texte, qui la
   collait à 0 px. La seconde ligne, plus basse, y échappait — d’où l’impression de
   deux gris différents dans une seule phrase.
   Deux gestes, un par symptôme : le `z-index` fait passer le texte AU-DESSUS de
   l’ombre (elle reste, elle ne le touche plus), et la marge lui rend l’air qui
   manquait. Le sélecteur vise le sous-titre QUI SUIT la démo : dans l’ordre
   d’origine, où il la précédait, ni l’un ni l’autre n’avait lieu d’être. */
.visually-hidden + .hero-sub { position: relative; z-index: 1; margin-top: 44px; }

/* ── ARGUMENT 2, LA PALETTE (17/08/2026, tard). Le visuel .palette-demo est celui de
   la v4 (identité sombre constante, la doctrine du vrai panneau ⌘CC, qui garde
   son anthracite quel que soit le fond). Il vient d’un temps où il vivait sous
   un titre centré, d’où sa marge haute de 36 px. Dans la colonne de prose il se
   pose comme les autres visuels : entre le titre et le texte, centré, sans
   marge haute. (0,2,0) bat la règle d’origine (0,1,0), la spécificité cette fois
   anticipée. ── */
/* 560 px et non les 480 de la v4 : à 480, les trois lignes du visuel sortaient
   toutes en points de suspension (mesuré, 358 px pour 479 de texte) ; à 560
   elles tiennent entières, en français comme en anglais. Le vrai panneau fait
   640 pt : la proportion s’en rapproche. */
.prose-col .palette-demo { margin: 0 auto 22px; max-width: 560px; }
.prose-col p + .palette-demo { margin-top: 32px; }
/* La barre d’indices en pied, comme le vrai panneau (↩ Insérer · ⌘↩ Modifier,
   et esc isolé à droite : l’écart le distingue des gestes contextuels, c’est la
   disposition de l’app). Trois indices et pas l’inventaire : une ligne
   d’indices est une AMORCE. */
.palette-hints {
  display: flex; align-items: center; gap: 14px;
  margin: 6px 3px 0; padding: 9px 8px 2px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 0.78rem; color: #9b9ba6;
}
.palette-hints span { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.palette-hints .palette-hint-esc { margin-left: auto; }
.palette-hints kbd {
  font-family: var(--font-mono); font-size: 0.72rem; line-height: 1.5; color: #c8c8d2;
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 4px; padding: 0 5px;
}

/* LE RUBAN (.packs-strip, .packs-scroll, .pack-card, .packs-nav et ses deux
   media queries) vit dans site.css depuis le 16/09/2026 : le bandeau des
   témoignages de /buy/ le partage. Sa doctrine est là-bas. Les surcharges de la
   vitrine (.showcase .packs-*) restent ici et gagnent comme avant, site.css se
   chargeant en premier. */
/* Les puces de la prose d’un argument (le texte de Jonathan pour les
   Collections en porte deux) : puces normales, encre de prose, retrait modeste. */
.prose-col .arg-list { margin: 14px 0 0; padding-left: 1.25em; color: var(--text-prose); }
.prose-col .arg-list li + li { margin-top: 8px; }
.prose-col .arg-list + p { margin-top: 14px; }

/* Les ÉTAPES d’un argument (les trois gestes de la palette) : liste numérotée,
   encre de prose, même retrait que .arg-list. */
.prose-col .arg-steps { margin: 14px 0 0; padding-left: 1.4em; color: var(--text-prose); }
.prose-col .arg-steps li + li { margin-top: 6px; }
.prose-col .arg-steps + p { margin-top: 14px; }

/* ── TÊTES D’ARGUMENT CENTRÉES (18/08/2026, intuition de Jonathan, ADOPTÉE à
   l’écran le jour même) : surtitre et H3 se centrent au-dessus de leur
   visuel, lui-même centré ; la prose reste
   ferrée à gauche. Portée : .arg seulement (les H2 du constat, section de prose,
   restent à gauche ; la bande #features est déjà centrée). Le chevron ne pend
   plus dans la marge (un glyphe en marge décentrerait l’ensemble, leçon de la
   bande) : il vient EN FLUX devant le titre, dans le groupe centré. `text-align`
   sert au titre qui passe sur deux lignes (l’item flex anonyme du texte est
   ferré à gauche sans lui). La page ne garde plus qu’UN régime de têtes centrées
   (bande #features, arguments, .section-head de #switchers/#maker/#faq) ;
   seule la THÈSE, section de prose, reste ferrée à gauche. ── */
.arg .prose-col .eyebrow { display: flex; justify-content: center; }
/* Le titre repasse en BLOC (le flex des têtes ferrées à gauche gardait le
   chevron à l’extrême gauche d’un titre centré sur deux lignes, détaché de
   son premier mot) : le lien d’ancre devient un inline-block qui SUIT la
   première ligne, et `text-wrap: balance` équilibre les deux lignes, comme
   sur la bande. Coût mesuré : le chevron en flux prend 26 px au titre — le
   titre de la palette (638 px à gauche) passe sur deux lignes. */
.arg .prose-col h3 { display: block; margin-left: 0; text-align: center; text-wrap: balance; }
.arg .prose-col h3 .h-anchor { display: inline-block; margin-right: 10px; vertical-align: baseline; }
.arg .prose-col h3 .chev { display: inline-block; }

/* ── LA SÉQUENCE DE TOUCHES DE #speed (18/08/2026) : .keys vient de la réserve du
   16/08 ; ses touches passent en KEYCAP indigo (le langage de l’app, celui du
   surtitre de la palette), d’où l’override du kbd neutre d’origine (.keys kbd,
   0,1,1, battrait .keycap, 0,1,0). Bridée à 520 px et centrée, comme un
   visuel : c’est le story-board de la vidéo à venir. ── */
.prose-col .keys { max-width: 520px; }
/* Le badge de déclencheur (mono) prenait 3 px de plus que les keycaps : même
   hauteur de ligne pour les quatre rangées. */
.keys .trigger-badge { line-height: 1.35; }
.keys kbd.keycap {
  font-family: inherit; font-size: 0.9em; color: var(--accent-text);
  background: var(--accent-soft); border: 0; box-shadow: inset 0 0 0 1px var(--accent-line);
  padding: 0.08em 0.42em; border-radius: 6px;
}

/* ── LE SOUCI DU DÉTAIL, #qol (18-19/08/2026) : deux phrases, TROIS exemples, un
   renvoi — tout dans la colonne de prose. La grille de huit détails du 18/08 a
   été écartée par Jonathan (« pas particulièrement impressionné ») : une liste
   moyenne dilue, trois exemples choisis frappent. Puces normales, ce qu’on voit en
   gras, le pourquoi en encre de prose. ── */
.prose-col .qol-list { margin: 14px 0 0; padding-left: 1.25em; color: var(--text-prose); }
.prose-col .qol-list li + li { margin-top: 10px; }
.prose-col .qol-list b { color: var(--text); font-weight: 600; }
.prose-col .qol-more { margin-top: 18px; }
.qol-more a { color: var(--text-prose); text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); text-underline-offset: 4px; text-decoration-thickness: 1px; }
.qol-more a:hover { color: var(--text); text-decoration-color: currentColor; }

/* ── LES ARGUMENTS DU 19/08 — deux composants repris d’ailleurs demandent leur
   règle ici : .formats n’était stylée que sous .switchers (section retirée de la
   landing le 18/08, dont #import hérite la liste), et .pv que sous .privacy (la
   bande indigo de la v4). Sur un fond neutre, les pastilles reprennent le
   vocabulaire des cartes. ── */
/* CENTRÉES depuis le 28/08 (demande de Jonathan) : la colonne de prose est
   ferrée à gauche, mais une rangée de pastilles n'est pas du texte — c'est un
   INVENTAIRE, et un inventaire se lit mieux groupé qu'aligné sur une marge. Le
   dernier rang, forcément incomplet, ne laisse plus un blanc à droite. */
.arg .formats {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
  margin: 0 0 22px; padding: 0; list-style: none;
}
.arg .formats li {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 6px 16px 6px 8px; font-size: 0.94rem; color: var(--text-2); background: var(--bg-card);
}
/* LES ICÔNES (28/08) — vraies icônes macOS quand elles existent, jamais un kit
   de marque : c'est ce que le lecteur a dans son Dock. Régénérées par
   `scripts/build_import_row.py`, qui REFUSE de construire si une icône n'a pas
   de mesure (même garde que build_app_wall.py, née de la même erreur).
   `--s` porte la règle d'échelle : 81 / emprise peinte, sur le plus grand côté.
   Le transform agit sur le CONTENU, pas sur la boîte — les gouttières restent
   égales. `fmt-cut` arrondit les carrés pleins (coins opaques), comme le Dock. */
.fmt-i { width: 22px; height: 22px; flex: none; transform: scale(var(--s, 1)); }
.fmt-cut { border-radius: 23%; }
.arg .pv {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 16px; font-family: var(--font-head); font-weight: 600; font-size: 0.94rem;
  display: flex; align-items: center; gap: 9px; color: var(--text);
}
.arg .pv .chev { width: 14px; height: 14px; color: var(--accent-text); flex: none; }

/* ── L’INVENTAIRE DES FONCTIONNALITÉS (20/08/2026) — sous le chapeau de la
   bande, les mêmes entrées que le rail flottant, en pastilles. Il répond à deux
   choses d’un coup : le rail n’existe qu’au-dessus de 1180 px (« la plupart des
   gens pourraient passer à côté », Jonathan), et une colonne de vingt-deux entrées
   sur 706 px de haut fait peur, là où la même liste en quelques rangées
   horizontales se lit comme un PÉRIMÈTRE. Sur la bande indigo, les pastilles
   empruntent le blanc translucide des preuves de la v4 — jamais l’accent, ce
   sont des portes, pas des états. ── */
/* GROUPÉ PAR FAMILLE depuis le 20/08/2026 (idée de Jonathan) : chaque rangée sous
   son OMBRELLE, elle-même un lien vers la famille. Une liste plate donnait le
   périmètre mais pas la CARTE. L'ombrelle est plus PETITE que les pastilles et
   moins contrastée : elle range, elle n'appelle pas — sans quoi cinq libellés
   gagneraient l'œil avant les dix-sept portes qu'ils coiffent. */
.feature-index .fx-group { margin-top: 20px; }
.feature-index .fx-group ul { margin-top: 9px; }
/* PIÈGE D'HÉRITAGE (20/08/2026, vu à l'écran par Jonathan : « le format bouton ne
   marche pas du tout ») — l'ombrelle est un `<a>`, donc la règle des PASTILLES lui
   donnait bordure, fond, rayon et padding : cinq gros boutons vides pleine largeur.
   Ma règle gagnait bien en spécificité, mais on ne bat que ce qu'on DÉCLARE ; tout
   ce qu'elle ne redisait pas restait. **Un élément qui emprunte la balise d'un
   composant en hérite le style entier, pas seulement ce qu'on a pensé à écraser.**
   D'où les quatre remises à zéro explicites ci-dessous. */
.answer .prose-col .feature-index .fx-label {
  display: block; text-align: center; text-decoration: none;
  border: 0; background: none; padding: 0; border-radius: 0;
  font-family: var(--font-head); font-weight: 600; font-size: 0.72rem;
  letter-spacing: 0.07em; text-transform: uppercase;
  /* 0,94 A ÉTÉ ESSAYÉ PUIS REFUSÉ À L’ÉCRAN (29/08, Jonathan : « moins
     bien qu’avant »). La mesure disait pourtant une anomalie réelle —
     l’ombrelle est à 5,1 de contraste sur l’indigo quand les pastilles
     qu’elle chapeaute sont à 10,8, soit plus pâle que ses enfants. Le remède
     arithmétique (0,94 → 13,3) ne vaut pas à l’œil : six capitales très
     contrastées prennent le pas sur les pastilles, qui sont le SUJET de
     l’inventaire. Une hiérarchie de contraste inversée n’est donc pas
     toujours un défaut : ici la discrétion de l’ombrelle est ce qui laisse
     lire la liste.
     Écarté avant elle, et mesuré aussi : l’accent (`accent-text` ne monte
     qu’à 6,6 sur ce fond, MOINS que les pastilles — il aurait éteint
     l’ombrelle en croyant la souligner). Et les ICÔNES de famille, proposées
     par Jonathan le même jour : les six noms sont des abstractions, qui ne se
     dessinent qu’en clichés, et une icône TIENT LIEU de figure dans cette
     page (règle du 29/08). */
  color: rgba(255, 255, 255, 0.52);
  transition: color 120ms ease;
}
/* LE MÊME PIÈGE, UN ÉTAGE PLUS LOIN (20/08/2026) : les quatre remises à zéro
   ci-dessus ne valaient que pour le REPOS. Au survol, la règle des pastilles
   redonnait fond et bordure à l'ombrelle — qui est en `block`, d'où une bande
   pleine largeur (la capture de Jonathan). **Un état non redit est un état
   hérité.** Ne reste que l'éclaircissement du TEXTE : l'ombrelle est un lien, et
   un contrôle qui ne répond pas au survol ne dit pas qu'il en est un. */
.answer .prose-col .feature-index .fx-label:hover {
  color: rgba(255, 255, 255, 0.9);
  background: none; border: 0;
}
.feature-index ul {
  list-style: none; margin: 22px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
}
/* PIÈGE DE SPÉCIFICITÉ, encore : `.answer .prose-col a:not(.h-anchor)` (0,3,1)
   habille les liens CHUCHOTÉS du chapeau et gagnait sur `.feature-index a`
   (0,1,0) — la couleur blanche pleine passait, le soulignement aussi. Le
   sélecteur ci-dessous vaut (0,3,1) et vient APRÈS, donc il gagne à poids
   égal. Vérifié à la mesure, pas au fichier. */
.answer .prose-col .feature-index a {
  display: inline-block; padding: 6px 13px; border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.20); color: rgba(255, 255, 255, 0.84);
  background: rgba(255, 255, 255, 0.07);
  font-size: 0.86rem; text-decoration: none;
  line-height: 1.4;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.answer .prose-col .feature-index a:hover { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.38); color: #fff; }
@media (prefers-reduced-motion: reduce) { .answer .prose-col .feature-index a { transition: none; } }

/* ── LES SIX FAMILLES (20/08/2026, deuxième version) — dix-neuf arguments au
   même niveau, c’était ce que le sommaire rendait visible. Ils sont groupés sous six
   têtes — mais CES TÊTES NE S’AFFICHENT PAS (arbitrage de Jonathan, le jour même) :
   « La fiabilité » suivi de « Fiabilité », « Le clavier » suivi de « Vitesse »,
   c’était un titre qui se répète lui-même, et certaines familles étaient à peine
   distinctes de leur premier argument. **Une famille se SENT, elle ne se lit pas.**
   Le H2 reste, en `visually-hidden` : il porte le NOM COURT, il tient la hiérarchie
   du document (H2 → H3), il donne son ancre à la section, et c’est lui que le
   sommaire affiche. Rien à l’écran.
   LA FRONTIÈRE SE DIT DONC PAR LE FOND ET PAR L’AIR, et l’écart doit être FRANC :
   60 px entre deux arguments d’une même famille, 168 px entre deux familles (2,8×),
   plus le changement de fond. En dessous de ce rapport, le groupe ne se lit plus. */
/* ── LE FOND DES FAMILLES (20/08/2026) — « on a toujours le problème de manque de
   différenciation entre les familles » (Jonathan). Mesuré avant de toucher : le
   contraste entre `--bg` et `--bg-2` vaut **1,052 : 1**, soit six points de
   luminosité sur 255. **La frontière entre deux familles reposait sur un signal de
   5 %** — ce n’était pas une impression.
   Porté à **1,17 : 1** (`#1f1f2a` en sombre, `#ececf3` en clair), ce qui se voit sans
   trancher. Une COULEUR PAR FAMILLE a été écartée : elle contredirait « une couleur
   par fonction, pas une par page » (17/08) et ferait perdre à l’indigo son statut de
   signal.
   VARIABLE PROPRE AU CHANTIER, et pas un réglage de `--bg-2` dans site.css : cette
   feuille-là est partagée avec la production (le garde exige qu’elle reste purement
   additive), et `--bg-2` y sert AUSSI de fond à des composants — les entêtes de
   maquettes, la ligne de code des visuels maison. Les toucher tous pour régler une
   frontière de section serait un effet de bord, pas une décision. */
/* LA ZONE DES FONCTIONNALITÉS EST TEINTÉE (20/08/2026, essai demandé par
   Jonathan : « difficile de voir où s'arrêtent les fonctionnalités »). L'alternance
   des familles ne se joue plus entre un fond de page et un gris, mais entre DEUX
   indigos très faibles : la zone entière porte donc une couleur, et **les deux
   clôtures se distinguent toutes seules en revenant au fond neutre**. Une seule
   décision règle les deux questions — où commence l'argumentaire, où il finit.
   5 % et 12 % d'accent, pas davantage : au-delà, du texte long sur aplat coloré, et
   l'indigo perdrait son statut de signal (le budget d'accent). L'écart entre les
   deux reste celui qu'on avait réglé à la mesure (1,17). */
:root {
  /* La teinte se pose SUR l'alternance, elle ne la remplace pas. Premier essai :
     5 % et 12 % d'accent sur le même fond — la zone se voyait, mais l'écart entre
     deux familles tombait de 1,17 à **1,073** (deux indigos faibles sont plus
     proches en luminance qu’un gris et un fond). On teinte donc les DEUX fonds
     d'origine du même 6 % : l'alternance garde sa valeur réglée à la mesure, et la
     zone entière prend sa couleur. */
  --fam-zone: color-mix(in srgb, var(--accent) 6%, var(--bg));
  --fam-alt: color-mix(in srgb, var(--accent) 6%, var(--solo-alt));
  /* Les CLÔTURES restent NEUTRES : c'est tout l'intérêt de la zone teintée —
     elles sortent de la couleur, donc on voit où l'argumentaire s'arrête. Leur
     alternance garde donc le gris d'avant, jamais `--fam-alt`. */
  --solo-alt: #ececf3;
}
@media (prefers-color-scheme: dark) { :root { --solo-alt: #1f1f2a; } }

.fam { padding: 0 0 80px; }   /* le haut est porté par l’étiquette, qui vit dans le flux */
.fam.alt { background: var(--fam-alt); }
.fam .arg { padding: 30px 0; background: none; }
.fam .arg:first-of-type { padding-top: 0; }
/* Une ancre d’argument arriverait SOUS l’étiquette collée (69 de nav + 29 px)
   si on ne la décalait pas — le piège du 17/08, troisième reprise. */
.fam .arg { scroll-margin-top: 116px; }
.fam .arg:last-child { padding-bottom: 0; }
@media (max-width: 700px) { .fam { padding: 0 0 56px; } .fam .arg { padding: 24px 0; } }

/* LES DEUX CLÔTURES, hors famille (20/08/2026) — #qol est une méta-section (elle
   parle de ce qui n’est PAS sur la page) et le prix n’est pas une fonctionnalité :
   c’est la réponse à la dernière phrase de la thèse. Mariés faute d’une troisième
   sous une famille « Le contrat », ils n’allaient ensemble sous aucun nom — la
   famille est dissoute.
   Elles reprennent le RYTHME d’une famille (88/80, pas les 30 d’un argument
   intérieur) et l’ALTERNANCE de fond là où elle en est : après #yours (alt), #qol
   sans fond, le prix en alt, la FAQ sans. Rien d’autre à inventer : une section qui
   ne porte qu’UN argument se voit comme telle, sans qu’on ait à le dire. */
.arg.solo { padding: 88px 0 80px; }
/* Leur titre est un H2 — elles ne dépendent d’aucune famille, et en H3 elles se
   rattachaient documentairement à la dernière (relevé de Jonathan). Mais il garde
   l’APPARENCE d’un argument : après dix-neuf blocs au même corps, un titre plus
   gros dirait une importance qu’elles n’ont pas. Sémantique juste, rythme intact.
   Effet de bord heureux : le rail les range tout seul au rang des familles, et les
   deux béquilles qu’il avait fallu pour les y forcer ont pu être retirées. */
.arg.solo .prose-col h2 {
  display: block; margin-left: 0; text-align: center; text-wrap: balance;
  font-size: 1.24rem; line-height: 1.3; margin-bottom: 16px;
}
.arg.solo .prose-col h2 .h-anchor { display: inline-block; margin-right: 10px; vertical-align: baseline; }
.arg.solo .prose-col h2 .chev { display: inline-block; }
.arg.solo.alt { background: var(--solo-alt); }
@media (max-width: 700px) { .arg.solo { padding: 64px 0 56px; } }

/* ══ LA VITRINE DU BLOC #qol (20/08/2026) — VENUE DE /details/, où elle était
   née la veille : quelques détails qui défilent avec leur visuel. Décision de
   Jonathan : la page de liste n’en avait pas besoin (elle a ses dépliants), la
   landing si — c’est là qu’il faut MONTRER, pas énumérer.
   **SON CONTENU EST UN PLACEHOLDER** (ses mots) : les quatre cartes viennent de
   la page de détails telles quelles, elles seront reprises. Seule la place est
   prise.
   MOTEUR : celui du ruban des collections (packs.js), au contrat près —
   [data-packs-strip], .packs-scroll, .pack-card, .packs-nav. Le fichier gère
   DÉJÀ plusieurs rubans par page (querySelectorAll + forEach, tout est porté par
   le conteneur), donc la vitrine cohabite avec le ruban de #collections sans une
   ligne de plus. Seul le STYLE diffère : une carte pleine largeur de la colonne
   de prose, au lieu d’un ruban qui dépasse — d’où l’absence de fondu aux bords,
   il n’y a rien à laisser deviner.
   LES VISUELS SONT MAISON, en HTML/CSS : jamais périmés par une capture, nets à
   toute échelle, thème-conscients, et rien à retourner quand l’interface bouge.
   Une vidéo ne se justifierait que pour ce qui est vraiment dynamique. */
.showcase { position: relative; max-width: 660px; margin: 30px auto 0; }
/* CE QUE LA VITRINE DOIT DÉFAIRE (20/08/2026, mordu en la déménageant ici).
   Elle réutilise le CONTRAT de packs.js, donc ses NOMS DE CLASSES — et sur cette
   page, ces noms portent déjà le style du ruban des Collections, qui n’est PAS
   préfixé. Sur /details/, où landing.css n’est pas chargé, rien de tout cela
   n’existait : le composant a donc changé d’aspect en changeant de page, sans
   qu’une seule de ses propres règles bouge. Trois héritages à défaire, chacun
   mesuré :
   · `align-items: center` du ruban — les quatre cartes prenaient des hauteurs
     INÉGALES (277, 225, 278, 262 px mesurés) au lieu de s’étirer à la plus haute ;
   · le `mask-image` de fondu aux bords — il a un sens pour un ruban qui dépasse,
     aucun pour une carte pleine largeur ;
   · la cale `::after` en bout de ruban — elle sert à ce que la DERNIÈRE carte
     d’un ruban puisse s’aligner à gauche ; ici chaque carte occupe déjà toute la
     largeur, la cale n’ajoute qu’un vide.
   RÈGLE À RETENIR : un composant qui emprunte le contrat d’un autre hérite aussi
   de son style dès qu’ils partagent une page. */
.showcase .packs-scroll { align-items: stretch; -webkit-mask-image: none; mask-image: none; }
.showcase .packs-scroll::after { content: none; }
.showcase .packs-scroll {
  display: flex; gap: 16px; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding-inline: 0; padding: 0;
  scrollbar-width: none;
}
.showcase .packs-scroll::-webkit-scrollbar { display: none; }
.showcase .packs-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius); }
.showcase .pack-card {
  flex: 0 0 100%; margin: 0; scroll-snap-align: start;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 22px 24px; box-shadow: var(--shadow-sm);
}
.showcase figcaption { display: block; margin-top: 16px; color: var(--text-2); font-size: 0.94rem; }
.showcase figcaption b { display: block; margin-bottom: 4px; font-family: var(--font-head); font-size: 1.02rem; color: var(--text); }
.showcase .packs-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; padding: 0; cursor: pointer; opacity: 0.85;
  background: var(--bg-card); color: var(--text-2); border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  transition: opacity 120ms ease, color 120ms ease; }
.showcase .packs-nav:hover { opacity: 1; color: var(--text); }
.showcase .packs-nav .chev { width: 11px; height: 11px; }
.showcase .packs-prev { left: -44px; }
.showcase .packs-prev .chev { transform: rotate(180deg); }
.showcase .packs-next { right: -44px; }
/* LES FLÈCHES SE CALENT SUR LE VISUEL, PAS SUR LA CARTE (21/08/2026, relevé au
   premier passage mobile). Au-dessus de 820 px elles flottent HORS de la carte,
   où `top: 50 %` est très bien. En dessous elles passent DEDANS — et là, un
   centrage sur la carte les fait tomber dans le TEXTE : la légende se replie de
   81 à 132 px en mobile, la carte enfle de 278 à 363, et la flèche descend avec
   elle. Mesuré à 375 px : elle recouvrait le titre de la carte sur 11 × 24 px,
   le « L » de « L’apostrophe… » passant dessous.
   POURQUOI 64 px, et pas une fraction : le visuel commence toujours à 23 px du
   haut (22 de padding de carte, 1 de bordure), mais sa hauteur varie d’une carte
   à l’autre (83 à 144 px selon la largeur). La zone commune à TOUTES les cartes,
   flèche de 32 px comprise, est [39, 90] au plus serré — mesurée à 375, 480, 600,
   700 et 819 px. 64 en est le centre exact, donc la valeur qui reste dans le
   visuel partout, y compris si une carte future est un peu plus courte.
   Le ruban de #collections n’est PAS concerné : ses cartes n’ont pas de légende
   qui se replie, son `top: 50 %` tombe déjà sur l’image (vérifié). */
@media (max-width: 820px) {
  .showcase .packs-prev { left: 4px; }
  .showcase .packs-next { right: 4px; }
  .showcase .packs-nav { top: 64px; }
}
/* Les visuels MAISON de la vitrine. Chacun tient en quelques lignes : c’est tout
   l’intérêt, une capture aurait vieilli à la première retouche d’interface. */
.shot-line { font-family: var(--font-mono); font-size: 0.98rem; color: var(--text);
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 14px; }
.shot-nb { background: color-mix(in srgb, var(--text-2) 22%, transparent); border-radius: 3px; }
.shot-note { margin-top: 8px; font-size: 0.86rem; color: var(--text-2); }
.shot-palette { background: #23232c; border: 1px solid rgba(255, 255, 255, 0.10); border-radius: var(--radius); padding: 10px; }
.shot-field { font-size: 1rem; color: #ececf1; background: rgba(255, 255, 255, 0.06); border-radius: 9px; padding: 10px 13px; }
.shot-caret { display: inline-block; width: 2px; height: 1.05em; margin-left: 2px; background: #6b66f2; vertical-align: -2px; animation: blink 1s steps(1) infinite; }
.shot-row { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 8px; padding: 9px 11px; border-radius: 8px; font-size: 0.94rem;
  color: #ececf1; background: rgba(107, 102, 242, 0.18); }
.shot-row kbd { font-family: var(--font-mono); font-size: 0.78rem; color: #b9b9c2;
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 5px; padding: 1px 6px; }

/* ── LA MAQUETTE DU CHAMP DÉCLENCHEUR (#trigger-suggestions, 24/08/2026).
   Deux panneaux = deux MOMENTS du même champ, par nécessité : l'éditeur cache
   ses suggestions dès qu’un déclencheur est saisi (EditPanelView), aucun état
   réel ne montre pastilles ET verdict à la fois — un panneau unique mentirait.
   Thème par JETONS (l'idiome de .compare), pas la vitrine sombre .shot-palette :
   ce visuel vit dans la prose d'une section, il suit la page. Le caret est le
   .shot-caret de la vitrine (indigo plein, lisible sur les deux thèmes). Le
   contenu (pastilles, verdicts, mots cités) est VÉRIFIÉ SUR LE MOTEUR — voir le
   commentaire HTML du bloc dans fr/index.html. */
.shot-editor { display: grid; gap: 10px; }
.se-panel { padding: 14px 16px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-card); box-shadow: var(--shadow-sm); }
.se-label { font-family: var(--font-head); font-weight: 600; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-2); margin-bottom: 9px; }
.se-field { font-family: var(--font-mono); font-size: 0.98rem; color: var(--text);
  background: color-mix(in srgb, var(--text) 5%, transparent);
  border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; }
.se-field.se-ph { color: var(--text-2); }
/* Le TEXTE ÉTENDU du panneau 1 : la CAUSE des pastilles. Sans lui, les
   suggestions sont trois soupes de lettres — avec lui, on lit la progression
   des initiales. Rendu comme le champ mais en retrait (c'est le contexte, pas
   le sujet du panneau), et sur la police de PROSE : dans l'app ce champ porte
   du texte, pas un déclencheur. */
.se-text { font-size: 0.94rem; line-height: 1.5; color: var(--text-2);
  white-space: pre-line;
  background: color-mix(in srgb, var(--text) 4%, transparent);
  border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; }
.se-label.se-second { margin-top: 14px; }

/* LE CHAMP DE MOTS-CLEFS (#keywords) : un champ qui CONTIENT des puces, plus une
   rangée de suggestions dessous. Les deux registres de l'app sont respectés — la
   capsule accent-soft pour les mots-clefs (ce qu'on RANGE), jamais le mono de
   .trigger-badge (réservé à ce qu'on TAPE). */
.se-field.se-rich { display: grid; gap: 8px; }
.se-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.se-chip, .se-kw { display: inline-flex; align-items: baseline; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-text);
  font-family: var(--font-body); font-size: 0.92rem; }
.se-x { font-size: 0.78em; opacity: 0.65; }
.se-input { display: flex; align-items: center; gap: 6px; color: var(--text); }
.se-input .se-ret { margin-left: auto; }
.se-kws { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.se-kw .se-n { font-size: 0.82em; opacity: 0.72; }

/* LA DÉMO DE #no-truncation NE SAUTE PAS EN HAUTEUR, et rien ici n'y contribue :
   c'est expansion.js qui pose le texte final dès le départ, invisible. D'où
   `.demo-reserve` — `visibility: hidden` GARDE la place, là où `display: none`
   la rendrait, et c'est toute la différence. Aucun nombre à écrire, donc rien
   qui ne vaille qu'à une seule largeur. Sans JavaScript, rien à réserver : le
   texte final est déjà dans le HTML. */
[data-expansion-demo] .compare-row code { display: block; line-height: 1.5; }
.demo-reserve { visibility: hidden; }
/* LE LANCEUR de la scène, sous « Réduire les animations » SEULEMENT — posé par
   expansion.js, jamais dans le HTML : sans JavaScript il n'y a rien à rejouer,
   et le repli statique se suffit. Il vit APRÈS le `.compare`, qui est
   `aria-hidden` (doctrine en tête d'expansion.js). Le margin-top négatif
   reprend les 22 px de marge basse du bloc pour se poser SOUS lui, en gardant
   l'air avant le paragraphe. */
.replay-scene { display: flex; align-items: center; gap: 7px; width: fit-content;
  margin: -12px auto 22px; padding: 5px 11px; cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 999px;
  color: var(--text-2); font-size: 0.85rem; font-weight: 500; line-height: 1;
  white-space: nowrap; }
.replay-scene:hover { color: var(--accent); border-color: var(--accent); }
.replay-scene:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.replay-scene .rs-play { font-size: 0.68em; }
.se-suggest { margin-top: 10px; font-size: 0.9rem; color: var(--text-2); }
.se-issue { display: flex; gap: 8px; align-items: baseline; margin-top: 10px;
  font-size: 0.9rem; line-height: 1.5; }
.se-issue .se-ico { flex: none; }
.se-warn { color: var(--text); }
.se-warn .se-ico { color: #d9822b; }
.se-note { color: var(--text-2); }
.se-note .se-ico { color: #4a7dbd; }
.se-issue .se-msg { flex: 1; }
/* Le conflit de bibliothèque : la pastille rouge du bandeau réel
   (exclamationmark.octagon.fill) et son « Afficher » — un lien DANS L'APP,
   ici une simple étiquette : la figure est décorative (aria-hidden). */
.se-block { color: var(--text); }
.se-oct { display: inline-flex; align-items: center; justify-content: center;
  width: 1.05em; height: 1.05em; border-radius: 50%; background: #e0533f;
  color: #fff; font-size: 0.82em; font-weight: 700; align-self: center; }
.se-open { flex: none; color: var(--accent-text); font-size: 0.88rem; font-weight: 500; }

/* ═════ LA SECTION « EXPANSION » DE L’ÉDITEUR (#expansion-mode, 27/08/2026) — le
   contrôle lui-même, et rien d’autre.
   UNE PREMIÈRE VERSION MONTRAIT AUSSI LA LISTE (gouttière de chevrons colorés à
   trois états, panneau de gauche, symbole #cc tracé sur BrandChevron.swift) :
   RETIRÉE par Jonathan le jour même, et sa raison vaut pour tout visuel de cette
   page — un MOTIF ne se lit que si on sait déjà ce que ses signes veulent dire,
   or la page s’adresse d’abord à des gens qui n’ont jamais ouvert l’app. Le
   récit complet et la géométrie du double chevron sont au README du chantier ;
   ni le CSS ni les symboles ne sont conservés ici, ils se refont en une minute.
   FIDÉLITÉ à EditPanelView.expansionSection : icône « développer » en SECONDAIRE
   devant le libellé (fieldLabel, HStack spacing 6), colonne d’icône FIXE à 21 px
   (ExpansionLabelStyle, sans quoi les trois titres se décalent, le clavier étant
   large et la pause étroite), description sous l’ICÔNE et non sous le titre (un
   frère du Label dans le VStack, pas son enfant), ⌘CC collé au TITRE.
   Le panneau prend la LARGEUR DE LA COLONNE : à 320 px, celle du popover de la
   liste, les trois descriptions passaient à deux lignes, là où l’app les tient
   sur une. */
/* LE PANNEAU SE RESSERRE SUR SA PLUS LONGUE LIGNE, ET SE CENTRE (demande de
   Jonathan, 27/08) : à la pleine largeur de la colonne, il laissait un grand
   vide à droite des descriptions. `fit-content` cale le bord droit au bout du
   texte le plus long, sans qu’aucune largeur ne soit écrite — donc rien qui ne
   vaille qu’à une seule langue : l’anglais, plus court, se resserre tout seul.
   `max-width: 100%` rend la main au wrapping en étroit. */
.se-modes { display: grid; gap: 12px; width: fit-content; max-width: 100%;
  justify-self: center; }
.se-modes-label { display: flex; align-items: center; gap: 6px; color: var(--text);
  font-family: var(--font-head); font-weight: 600; font-size: 0.98rem; }
.se-modes-label .oico { flex: none; width: 14px; height: 14px; color: var(--text-2); }
/* LE RADIO SE CENTRE SUR SA LIGNE PAR CONSTRUCTION, PAS PAR UN CHIFFRE
   (02/10/2026, relevé par Grégoire : « alignement entre radio et texte
   perfectible »). Il était posé en haut de l’option avec `margin-top: 3px`,
   un décalage juste pour une ligne de 19 px ; la ligne en fait 25, et son
   centre passait 3 px au-dessus de celui du libellé, de l’icône et de ⌘CC,
   qui eux sont alignés entre eux. L’option est donc une GRILLE : le radio et
   la ligne de titre partagent la première rangée, où le radio se centre
   (`align-self`), et la description prend la seconde, sous l’icône. Aucune
   hauteur de ligne n’est écrite, donc aucune ne peut redevenir fausse.
   `.se-optbody` n’est plus une boîte (`display: contents`) : ses deux enfants
   deviennent les cellules de la grille. `minmax(0, 1fr)` rend le retour à la
   ligne aux descriptions en étroit, ce que faisait son `min-width: 0`. */
.se-opt { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 9px; }
.se-radio { grid-column: 1; grid-row: 1; align-self: center;
  width: 13px; height: 13px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text-2) 65%, transparent); }
.se-opt-on .se-radio { background: var(--accent); box-shadow: none;
  background-image: radial-gradient(circle at 50% 50%, #fff 0 2.4px, transparent 2.5px); }
.se-optbody { display: contents; }
.se-optline { grid-column: 2; grid-row: 1;
  display: flex; align-items: center; gap: 4px; font-size: 0.95rem;
  color: var(--text); }
.se-optline .oico { flex: none; width: 21px; height: 16px; }
.se-optdet { grid-column: 2; grid-row: 2;
  margin-top: 1px; font-size: 0.84rem; line-height: 1.42; color: var(--text-2); }
/* La keycap entre dans un NOUVEAU conteneur flex — la liste jumelle de site.css
   (« SAUF EN ITEM FLEX ») demande à être complétée ; on le fait ici plutôt que
   là-bas, site.css étant partagé avec la prod et ce composant non. */
.se-optline .keycap { padding: 0.08em 0.42em; }

/* ═════ LA MÊME CHOSE, TROIS FOIS (#no-ai, 27/08/2026) — la variance montrée par
   la RÉPÉTITION. Le récit du visuel remplacé et les trois raisons de le
   remplacer sont dans le commentaire HTML du bloc, fr/index.html.
   PAS DE .compare ICI, alors que la page en porte cinq : la section
   IMMÉDIATEMENT au-dessus, #no-truncation, en a un ANIMÉ. Deux sections
   d’affilée dans la même grammaire ✗/✓ et la seconde s’efface derrière la
   première. On emprunte donc au vocabulaire de #trigger-suggestions et
   #keywords, panneaux et libellés en petites capitales.
   LE `fit-content` EST SUR LA FIGURE, PAS SUR LE PANNEAU (à la différence de
   #expansion-mode) : les deux blocs doivent partager LA MÊME largeur, sans quoi
   chacun se dimensionnerait sur sa propre plus longue ligne et ils feraient un
   escalier. Une grille à colonne unique les étire tous deux sur la plus large. */
.shot-thrice { width: fit-content; max-width: 100%; justify-self: center; }
.th-line { font-size: 0.94rem; line-height: 1.55; color: var(--text); }

/* ═════ LES STATISTIQUES (#stats, 27/08/2026) — trois chiffres, puis le palmarès
   par circut. Les chiffres sont CHOISIS et cohérents par construction (le
   calcul est détaillé dans le commentaire HTML du bloc) : le relevé réel de
   Jonathan, lu sur une copie de sa base, s’est révélé sincère mais atypique.
   POURQUOI LE PALMARÈS EN SECOND ET NON LA COURBE : la prose énumère déjà les
   tuiles, donc les montrer n’ajouterait que leurs CHIFFRES — ce qui est déjà
   beaucoup, un nombre disant ce qu’une catégorie ne dit pas. Le palmarès, lui,
   est d’une autre nature : c’est un MIROIR, la seule partie de l’écran qui
   parle de vous et pas de l’outil. La courbe quotidienne est jolie et ne dit
   rien de plus que le total ; elle reste dehors.
   Les barres portent leur longueur en `--b`, calculée sur le premier rang. */
/* DEUX COLONNES, PAS UNE PILE (27/08/2026, la figure faisait 957 px de haut et
   Jonathan la trouvait « clairement trop haute » — il envisageait un carrousel).
   Un tableau de bord est une GRILLE : l'app met déjà ses trois cartes de front,
   la figure ne faisait que les empiler. Mises côte à côte, « Temps gagné » avec
   la courbe et le palmarès avec la répartition par app, la hauteur tombe de 957
   à ~600 SANS RIEN RETIRER — là où un carrousel aurait caché quatre blocs sur
   cinq derrière un geste. La hauteur d'une ligne est celle de son bloc le plus
   haut : c'est le prix, et il est petit.
   BASCULE À 620 px, pas plus bas : à cette largeur les colonnes font encore
   ~280 px, soit ~250 de contenu, ce qu'une ligne « par application » (icône,
   nom, barre, total) réclame au minimum. À 560 px elle passait tout juste, et
   « tout juste » n'est pas une marge. */
/* `stretch` et non `start` : les deux blocs du bas (palmarès, répartition par
   app) doivent finir à la MÊME hauteur, sinon le plus court laisse un vide sous
   lui — relevé par Jonathan. Ils portent six lignes chacun, l'égalité est donc
   déjà presque acquise ; `stretch` la garantit quoi qu'il arrive au contenu. */
.shot-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px; align-items: stretch; }
.shot-stats > .st-cards, .shot-stats > .st-wide { grid-column: 1 / -1; }
@media (max-width: 620px) {
  .shot-stats { grid-template-columns: 1fr; }
  /* EN COLONNE UNIQUE, LA COURBE SORT. C'est le seul bloc qui n'ajoute aucune
     information que le triptyque ne donne pas — et à 293 px de large, trente
     jours de relevé ne se lisent plus, ils décorent. Le sacrifier rend 140 px
     à une figure qui en fait déjà 1 160 sur téléphone. */
  .shot-stats > .st-curve { display: none; }
}
/* LES CARTES SUIVENT `MetricCard` : pictogramme teinté dans un carré de 34 px au
   fond à 12 %, puis la valeur en 22 px semi-gras ARRONDI (l’app la rend en
   `design: .rounded`) à chiffres tabulaires, le libellé, la moyenne du jour.
   Les trois teintes sont celles de l’app — indigo de marque, turquoise, violet.
   Elles sortent du budget d’accent, et c’est assumé : la figure est une CAPTURE,
   et ces teintes vivent dans des carrés de 34 px à 12 % d’opacité. */
/* TROIS COLONNES ÉGALES, pas un flex qui enveloppe : en flex, la figure étant
   en `fit-content`, la troisième carte tombait à la ligne et prenait toute la
   largeur — deux cartes en haut, une en bas, ce que l'app ne fait jamais. La
   grille impose les trois de front et laisse la figure se dimensionner dessus.
   Sous 470 px elles s'empilent : à trois de front, chacune ferait 100 px et le
   libellé « Frappes économisées » y tiendrait sur quatre lignes. */
.st-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 470px) { .st-cards { grid-template-columns: 1fr; } }
.st-card { display: grid; gap: 2px; justify-items: start; padding: 14px 16px; }
.st-ico { display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 8px; margin-bottom: 10px; }
.st-ico .gico { width: 19px; height: 19px; }
.st-cmd { font-size: 1.05rem; line-height: 1; }
.st-accent { color: var(--accent-text); background: var(--accent-soft); }
.st-teal { color: #1a8fa3; background: rgba(48, 176, 199, 0.14); }
.st-violet { color: #8b41c4; background: rgba(175, 82, 222, 0.14); }
.st-clock { color: var(--text-2); background: color-mix(in srgb, var(--text) 8%, transparent); }
@media (prefers-color-scheme: dark) {
  .st-teal { color: #4fd0e6; }
  .st-green { color: #4cd964; }
  .st-violet { color: #c88bf5; }
}
/* LE BANDEAU « TEMPS GAGNÉ » SUIT SON HStack : le bloc de gauche, puis la
   colonne de vitesse à 220 px à droite — c'est ainsi que l'app le dessine, et
   c'est ce qui remplit le vide que laissait la courbe à côté (relevé de
   Jonathan). L'horloge est VERTE dans un carré de 30 px, pas 34 : le bandeau a
   son propre gabarit, plus petit que celui des cartes, et son libellé est SUR
   LA MÊME LIGNE que l'icône au lieu d'être dessous. Détails relus dans
   `timeSavedHero`, pas devinés.
   Le curseur apporte la seule touche d'accent PLEIN de la figure — et c'est
   juste, puisque dans l'app c'est le seul contrôle actif de l'écran. */
.st-hero { display: flex; align-items: flex-start; gap: 16px; padding: 14px 16px; }
.st-heroleft { flex: 1; min-width: 0; display: grid; gap: 2px; justify-items: start; }
.st-heroline { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.st-heroline .st-ico { width: 30px; height: 30px; margin-bottom: 0; }
.st-green { color: #1f9d55; background: rgba(52, 199, 89, 0.14); }
.st-speed { flex: none; width: 220px; display: grid; gap: 5px; }
/* Le curseur : piste neutre, portion remplie en accent, pastille blanche à
   ombre — le dessin d'un Slider macOS. 40 mots/min sur une plage de 20 à 120,
   donc 20 % de course. */
.st-slider { position: relative; height: 4px; border-radius: 999px; margin: 8px 0 7px;
  background: color-mix(in srgb, var(--text) 15%, transparent); }
.st-slider::before { content: ""; position: absolute; inset: 0 auto 0 0; width: var(--p);
  border-radius: 999px; background: var(--accent); }
.st-thumb { position: absolute; top: 50%; left: var(--p); width: 14px; height: 14px;
  margin: -7px 0 0 -7px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), inset 0 0 0 0.5px rgba(0, 0, 0, 0.1); }
.st-num { font-family: var(--font-head); font-weight: 600; font-size: 1.42rem;
  line-height: 1.15; color: var(--text); font-variant-numeric: tabular-nums; }
.st-lab { font-size: 0.88rem; color: var(--text-2); }
.st-day { font-size: 0.82rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.st-foot { margin-top: 6px; font-size: 0.82rem; color: var(--text-3, var(--text-2));
  opacity: 0.85; }
/* LA GRILLE VIT SUR LE CONTENEUR, PAS SUR LA RANGÉE — et c'est ce qui aligne
   les barres. Posées ligne par ligne, les colonnes en `auto` se dimensionnaient
   CHACUNE sur son propre badge : les barres partaient de six abscisses
   différentes. Les rangées passent donc en `display: contents` et leurs enfants
   deviennent les items de la grille du conteneur, qui possède les colonnes et
   les partage entre toutes les lignes. `auto` prend alors la largeur du plus
   large badge de la liste, sans qu'on ait à la mesurer ni à la figer par
   langue. */
.st-rank { display: grid; row-gap: 9px; column-gap: 12px; align-items: center; }
.st-crank { grid-template-columns: auto minmax(90px, 1fr) 52px; }
.st-arank { grid-template-columns: 24px auto minmax(56px, 90px) 40px; column-gap: 10px; }
.st-crank .st-row, .st-arank .st-arow { display: contents; }

/* ═════ L’UTILISATION D’UN CIRCUT (#stats, 28/08/2026) — le petit graphe que
   l’ÉDITEUR affiche pour le circut ouvert. Figure à PART de celle du tableau de
   bord : les deux vivent sur des écrans différents, les réunir aurait brouillé
   les deux. Fidèle à EditPanelView — libellé à icône `chart.bar`, lien d’accent
   à droite, barres de 36 px et comptes à leur DROITE, alignés en bas.
   Les barres sont un SVG en `xMinYMax` : elles s’étirent en largeur et gardent
   leur hauteur, comme un vrai graphe qui s’adapte au volet. Les jours à ZÉRO
   gardent un trait de 1,5 px — c’est ce que fait l’app, et une barre absente se
   lirait comme une donnée absente. */
.shot-usage { width: fit-content; max-width: 100%; justify-self: center; }
.us-panel { padding: 14px 16px; display: grid; gap: 8px; }
.us-head { display: flex; align-items: baseline; gap: 16px; }
.us-title { display: flex; align-items: center; gap: 6px; color: var(--text);
  font-family: var(--font-head); font-weight: 600; font-size: 0.98rem; }
.us-title .oico { flex: none; width: 15px; height: 15px; color: var(--text-2); }
.us-link { margin-left: auto; display: inline-flex; align-items: baseline; gap: 3px;
  color: var(--accent-text); font-size: 0.86rem; white-space: nowrap; }
/* À la hauteur des guillemets de la police système (doctrine des chevrons
   près de `.chev`, dans site.css) : centré sur la ligne, il était 1,4 px trop
   haut (02/10/2026). */
.us-link .chev { width: 9px; height: 9px; position: relative; top: calc(4.5px - 0.5ex); }
.us-body { display: flex; align-items: flex-end; gap: 14px; }
.us-bars { flex: 1 1 auto; min-width: 110px; width: 100%; height: 36px;
  fill: var(--accent-text); }
.us-bars .us-nil { fill: color-mix(in srgb, var(--text) 18%, transparent); }
.us-counts { flex: none; font-size: 0.86rem; color: var(--text-2);
  font-variant-numeric: tabular-nums; }

/* ═════ LA FEUILLE DE RÉCEPTION D'UN PARTAGE (#share, 28/08/2026) — ce que voit
   CELUI QUI REÇOIT, avant d'ajouter quoi que ce soit. C'est le moment de
   confiance de la section, et le seul que sa prose détaille.
   Fidèle à ShareImportSheet : titre, phrase de vérification, puis une ligne par
   circut (badge de déclencheur, aperçu du texte sur trois lignes au plus), et
   sous les lignes en conflit l'avertissement ORANGE à triangle. Le pied compte
   ceux qui arriveront désactivés, et les deux boutons ferment.
   L'orange est celui de .se-warn, déjà posé pour les verdicts de
   #trigger-suggestions : même grammaire pour le même sens. */
.shot-share { width: fit-content; max-width: 100%; justify-self: center; }
.sh-panel { padding: 16px 18px; display: grid; gap: 4px; }
.sh-title { font-family: var(--font-head); font-weight: 600; font-size: 1.02rem;
  color: var(--text); }
.sh-sub { font-size: 0.88rem; color: var(--text-2); margin-bottom: 8px; }
.sh-list { display: grid; gap: 12px; padding: 12px 0; border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border); }
.sh-item { display: grid; gap: 4px; justify-items: start; }
.sh-text { font-size: 0.92rem; line-height: 1.4; color: var(--text-2); }
.sh-warn { display: flex; align-items: baseline; gap: 6px; font-size: 0.84rem;
  color: #d9822b; }
.sh-foot { display: flex; align-items: center; gap: 12px; margin-top: 10px;
  font-size: 0.84rem; color: var(--text-2); }
.sh-btns { margin-left: auto; display: flex; gap: 8px; flex: none; }
.sh-btn { font-size: 0.86rem; padding: 4px 12px; border-radius: 7px;
  border: 1px solid var(--border); color: var(--text-2); white-space: nowrap; }
.sh-btn.sh-go { background: var(--accent); border-color: transparent; color: #fff; }
@media (max-width: 480px) {
  .sh-foot { display: grid; gap: 10px; }
  .sh-btns { margin-left: 0; }
}
/* En étroit, le compte passe SOUS les barres : côte à côte, il ne laissait que
   110 px au graphe et les trente jours devenaient un peigne illisible. */
@media (max-width: 520px) {
  .us-body { display: grid; gap: 8px; }
  .us-counts { text-align: right; }
  .us-head { flex-wrap: wrap; gap: 4px 12px; }
}
/* PREMIÈRE COLONNE EN `auto`, pas en 68 px : un badge de huit signes
   (`,adresse`, `:pouce:`) débordait de son cadre (relevé de Jonathan). Dans une
   grille, `auto` prend la largeur du PLUS LARGE badge de toutes les rangées, si
   bien que les barres restent alignées — ce qu'une largeur posée par ligne aurait
   perdu. La colonne s'adapte donc à la langue sans qu'on ait à la mesurer. */
/* La barre suit celle de l'app : 6 px, piste en accent-soft, remplissage en
   accent-text à 55 % — pas l'accent plein, qui criait deux fois trop fort. */
.st-bar { height: 6px; border-radius: 999px; background: var(--accent-soft);
  position: relative; }
.st-bar::before { content: ""; position: absolute; inset: 0 auto 0 0; width: var(--b);
  border-radius: 999px; background: var(--accent-text); opacity: 0.55; }
/* LA COURBE est l'AreaMark + LineMark de l'app, en un SVG étiré : les trente
   valeurs SOMMENT à 1 240, le chiffre du triptyque, et portent le creux du
   week-end — un graphe décoratif se voit tout de suite, un graphe qui compte
   juste ne se remarque pas, ce qui est le but. */
.st-plot { position: relative; }
.st-chart { display: block; width: 100%; height: 64px; overflow: visible; }
/* LE REPÈRE DE PIC reprend le `RuleMark` du survol de l'app : un trait vertical
   et la valeur du jour. Ancré sur le maximum réel de la série (168), pas sur
   une position choisie — le trait tombe donc pile sur la pointe. */
.st-peak { position: absolute; top: -2px; bottom: 0; left: var(--x); width: 1px;
  background: var(--accent-line); }
.st-peaklab { position: absolute; top: -4px; right: 8px; white-space: nowrap;
  font-size: 0.78rem; color: var(--text-2); font-variant-numeric: tabular-nums;
  background: var(--bg-card); padding: 1px 6px; border-radius: 5px;
  box-shadow: inset 0 0 0 1px var(--border); }
/* LA BULLE SUIT `hoverLabel` (relevé de Jonathan, 29/08 : « il manque la
   date ») : l’app affiche « <jour> <mois> · <N> expansions », la date en
   SECONDAIRE puis le compte en semi-gras, séparés par un point médian — et
   c’est ce point médian qui est posé en `::before`, jamais tapé dans le HTML.
   La date est un MERCREDI, comme le veut la série : le pic est à l’indice 23,
   les creux du week-end aux indices 5-6, 12-13, 19-20 et 26-27, donc l’indice 0
   est un lundi. Sans ANNÉE, comme le format `.day().month(.wide)` de l’app —
   c’est aussi ce qui l’empêche de vieillir (la dette des dates de #export). */
.st-peakday { font-style: normal; color: var(--text-2); opacity: 0.8; }
.st-peakday::after { content: " · "; opacity: 0.55; }
.st-peaklab { font-weight: 600; }
/* Le PointMark de l’app : un point sur la courbe à l’intersection du repère.
   Il manquait aussi — la bulle et le trait sans le point donnaient un repère
   qui vise sans toucher. */
.st-peak::after {
  /* MESURÉ, pas estimé : un pourcentage tombait 2 px au-dessus de la courbe.
     `st-peak` commence à `top: -2px` du graphe, et le graphe fait 64 px pour un
     viewBox de 64 unités — une unité vaut donc un pixel, et le pic (y = 4) est
     à 2 + 4 pixels du haut du repère. */
  content: ""; position: absolute; left: 50%; top: 6px;
  width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: var(--accent-text);
}
.st-area { fill: var(--accent-soft); }
.st-line { fill: none; stroke: var(--accent-text); stroke-width: 1.6;
  stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* Une ligne de la distribution par application : icône, nom, barre, total —
   le même langage que le palmarès, comme dans l'app. L'icône est à 81 % de sa
   boîte comme partout ailleurs (ce sont toutes des icônes macOS natives, donc
   aucune mise à l'échelle à poser ici). */
.st-arow img { width: 24px; height: 24px; object-fit: contain; }
.st-aname { font-size: 0.9rem; color: var(--text); }
.st-n { font-size: 0.88rem; color: var(--text-2); text-align: right;
  font-variant-numeric: tabular-nums; }

/* ═════ LE MUR D’APPS (#works-everywhere, 27/08/2026) — une rangée qui DÉBORDE,
   pas une grille rangée.
   POURQUOI CE N’EST PAS LA PLANCHE DE TYPINATOR : dix-huit tuiles centrées et
   comptables sont la grammaire visuelle des INTÉGRATIONS (« voici les apps
   prises en charge »), or la section dit précisément qu’il n’y en a aucune. Une
   liste finie affaiblit « partout ». Un rail qui sort du cadre et se fond aux
   bords dit « et ça continue » — c’est la seule chose à obtenir ici, et elle
   tient dans le débordement, pas dans le nombre.
   LE MASQUE FAIT LE DÉGRADÉ AUX DEUX BOUTS, et le rail défile pour de bon
   (`overflow-x: auto`) : un bord estompé qui promet une suite inatteignable
   serait une affordance qui ment.
   Les icônes sont servies à 64 px depuis des sources de 128 : 2× exactement,
   net sur les écrans Retina sans payer le double. */
.app-wall { position: relative; margin: 22px 0 26px; }
/* Les deux flèches, aux bords du rail, sur le fondu — le vocabulaire de
   .packs-nav et du carrousel (chevron de marque en pastille neutre), en plus
   petit : la bande ne fait que 68 px de haut, une pastille de 34 y tiendrait
   toute la hauteur. Z-INDEX EXPLICITE, la règle du dépôt pour tout contrôle
   superposé à un ruban. Éteintes aux extrémités : un contrôle qui ne fait rien
   est un contrôle qui ment. */
.aw-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 28px; height: 28px; border-radius: 50%; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-card); color: var(--text-2);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  cursor: pointer; opacity: 0.85;
  transition: opacity 120ms ease, color 120ms ease; }
.aw-nav:hover:not(:disabled) { opacity: 1; color: var(--text); }
.aw-nav:disabled { opacity: 0.25; cursor: default; }
.aw-nav .chev { width: 11px; height: 11px; }
.aw-prev { left: -4px; }
.aw-prev .chev { transform: rotate(180deg); }
.aw-next { right: -4px; }
/* LE FONDU N’EXISTE QUE DU CÔTÉ OÙ LE MUR CONTINUE (02/10/2026, frère du
   plafond de /versions relevé par Grégoire). Le masque était fixe, 56 px de
   chaque côté, alors que la première icône est à 2 px du bord : à
   l’ouverture, Safari était estompé sur presque toute sa largeur, devant
   rien, et ne pouvait JAMAIS se voir en entier (pour dégager le bord gauche
   il faut défiler, ce qui le sort de l’écran) ; même sort pour la dernière
   icône en bout de course. appwall.js publie ce qui reste caché de chaque
   côté (`--reste-g`, `--reste-d`) ; le fondu vaut ce reste, plafonné à
   `--fondu`. Sans script : rien à gauche, le fondu à droite. */
.aw-rail { display: flex; gap: 16px; overflow-x: auto; padding: 4px 2px 10px;
  scrollbar-width: none;
  --fondu: 56px;
  --fondu-g: min(var(--fondu), var(--reste-g, 0px));
  --fondu-d: min(var(--fondu), var(--reste-d, 9999px));
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--fondu-g), #000 calc(100% - var(--fondu-d)), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 var(--fondu-g), #000 calc(100% - var(--fondu-d)), transparent 100%); }
.aw-rail::-webkit-scrollbar { display: none; }
/* TOUTES LES ICÔNES N’ONT PAS LA MÊME EMPRISE DANS LEUR TOILE (relevé par
   Jonathan : « les logos semblent d’une taille différente »), et la sonde canvas
   l’a chiffré sans discussion : une icône macOS peint 81 % de sa toile — c'est
   le gabarit d’Apple, 824 sur 1024 — quand un apple-touch-icon en peint 100 %.
   À boîte égale, les secondes paraissent donc un huitième plus grosses.
   ON MET À L’ÉCHELLE LE CONTENU, JAMAIS LA BOÎTE : un `transform` ne change pas
   la taille de l’item flex, donc les gouttières restent rigoureusement égales.
   Réduire la largeur, elle, aurait rendu les écarts inégaux — 16 px entre deux
   pleines, 28 entre deux macOS. Le facteur de chaque icône est 81 divisé par son
   emprise mesurée ; il vit en `--s` sur la vignette, posé par script à partir de
   la mesure, jamais à l’œil. */
.aw-rail img, .aw-rail .aw-discord { transform: scale(var(--s, 1)); }
/* `object-fit` est SANS EFFET sur une icône carrée et protège les autres : le
   logo Gmail 2026 est le premier du mur à ne pas l’être (128 × 102), et sans
   lui il serait étiré d’un quart en hauteur. Posé sur toutes plutôt que sur la
   seule concernée — la prochaine arrivera sans qu’on y pense. */
.aw-rail img { flex: none; width: 64px; height: 64px; object-fit: contain; }
/* En étroit, le dégradé est ramené à 28 px : à 56 il mangeait deux icônes sur
   cinq visibles, et une bande estompée plus large que la bande nette ne dit plus
   « ça continue », elle dit « on ne voit rien ». */
/* LES TROIS RETOUCHES, chacune décidée sur une mesure (alpha des quatre coins,
   sonde canvas) et non à l’œil. Les icônes prises sur le Mac arrivent avec leur
   squircle et leur transparence ; celles des apps WEB viennent de leur
   apple-touch-icon officiel et sont des CARRÉS PLEINS — c’est macOS qui les
   arrondit au Dock, on fait donc ici ce qu’il ferait. */
.aw-rail img.aw-cut { border-radius: 22.5%; }
/* Gmail n’a pas d’icône d’app, seulement son logo sur transparent : on lui pose
   la tuile blanche que le Dock lui poserait. */
.aw-rail img.aw-tile { border-radius: 22.5%; background: #fff; padding: 11px;
  box-sizing: border-box; }
/* Discord ne publie que sa marque, en NOIR sur transparent, alors que son icône
   d’app est cette marque en BLANC sur son blurple. On la reconstitue — un logo
   noir sur fond clair n’existe nulle part. Un `span` à fond coloré plutôt qu’une
   `img` filtrée : un filtre s’appliquerait aussi au fond. */
.aw-rail .aw-discord { flex: none; width: 64px; height: 64px; border-radius: 22.5%;
  background: #5865f2 url("/assets/img/apps/discord-white.svg") center / 58% no-repeat; }
@media (max-width: 720px) { .aw-rail .aw-discord { width: 54px; height: 54px; } }
@media (max-width: 720px) {
  .aw-rail img { width: 54px; height: 54px; }
  .aw-rail { --fondu: 28px; }
}

/* LE LIEN VERS LA LISTE VIT DANS LA PHRASE (20/08/2026) : le bloc avait un CTA
   « Tous les détails → » en pied, à trois lignes d’un texte qui parle déjà de la
   liste — deux portes vers la même page, dont une qui APPELLE. Or ce bloc dit un
   souci du détail : il ne doit pas le vendre. Le lien discret dans la phrase
   laisse celui que ça intéresse aller voir, et ne pousse personne.
   Style de PROSE, jamais l’indigo par défaut de `a` (« ce qui commande une action
   se lit comme de la prose », 17/08) — même soulignement que l’ancien `.qol-more`,
   qui reste dormant dans cette feuille au cas où le CTA reviendrait. */
.prose-col p a.detail-link {
  color: var(--text-prose); text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 4px; text-decoration-thickness: 1px;
}
.prose-col p a.detail-link:hover { color: var(--text); text-decoration-color: currentColor; }

/* LE BOUTON .arg-cta (règles et doctrine) vit dans site.css depuis le
   16/09/2026 : le ruban des témoignages de /buy/ le reprend tel quel, même
   libellé que le bandeau de l’accueil. */

/* ── LA FAQ AU FORMAT DE LA PAGE (20/08/2026, demandes de Jonathan) — elle était
   restée à l'ancien format : chevron sur le SURTITRE, titre sans permalien, tête
   dans `.section-head`. Elle prend le gabarit commun (`.prose-col`), donc le
   chevron pend dans la marge DEVANT le titre et lui sert d'ancre, comme partout
   ailleurs. Effet de bord voulu : elle entre enfin dans le sommaire flottant, qui
   la lit comme le reste (le rail ne voit que les titres de `.prose-col`).
   **PLUS DE DÉPLIANTS.** Six réponses de deux lignes derrière six clics, c'était
   cacher ce qu'on a écrit pour être lu — et le site s'interdit par ailleurs les
   dépliants d'arguments. La question reste un H3 (l'outline et la navigation par
   titres y gagnent) mais prend l'apparence d'un gras de corps : on ne veut pas
   sept titres de section dans une page qui en a déjà vingt. */
/* LE RETOUR, 18/10/2026 (idée de Jonathan) : en section solo, sans fond, entre
   le prix et l’appel final. Elle ne redit pas les sections, elle répond à ce
   qu’elles ne disent pas et renvoie vers elles, par un lien posé sur les mots
   qui désignent la chose. Les questions sont ferrées à GAUCHE, quand les
   titres des `.arg` sont centrés : une liste de questions se lit, elle ne
   s’affiche pas.
   EN DÉPLIANTS, cette fois (décision de Jonathan, le même jour) : dix
   questions, dont une longue, et 1 800 px de page ouverts d’un coup ; la
   première version les avait retirés pour six réponses de deux lignes. Le
   montage est celui des rangées de /versions/, « UNE RANGÉE, UN GESTE » :
   toute la question déplie, un voile neutre au survol (là où le survol
   existe), le chevron de la marque tourné vers le bas puis retourné, hors
   flux et centré par construction. Chaque question porte son `id` (le lien
   qu’on envoie à quelqu’un), décalé sous la barre collante ; l’arrivée sur
   ce lien l’ouvre, et la déplier l’inscrit dans l’URL (site.js, 3). */
/* `site.css` porte encore le `.faq details` / `.faq summary` de l’ancienne
   page d’attente (18 px de rembourrage, la police des titres à 1,05 rem, un
   `display: flex`) : neutralisés ici par une spécificité supérieure, le
   `padding: 0` et le `font: inherit` du summary. À purger de `site.css` à sa
   prochaine retouche (le piège « une classe de page ne prend pas le nom d’une
   règle de site.css », site.md). */
.faq .prose-col .qa { margin: 0; padding: 0; border-bottom: 1px solid var(--border); scroll-margin-top: 88px; }
.faq .prose-col .qa:first-of-type { margin-top: 10px; }
.faq .prose-col .qa:last-of-type { border-bottom: none; }
.faq .prose-col .qa summary {
  position: relative;
  isolation: isolate;
  display: block;
  font: inherit;
  padding: 14px 34px 14px 4px;
  cursor: pointer;
  list-style: none;
}
.faq .prose-col .qa summary::-webkit-details-marker { display: none; }
.faq .prose-col .qa summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; border-radius: 4px; }
.faq .prose-col .qa summary::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 4px -10px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--text-2) 8%, transparent);
  opacity: 0;
  transition: opacity 120ms ease;
}
@media (hover: hover) {
  .faq .prose-col .qa summary:hover::before { opacity: 1; }
}
.faq .prose-col .qa h3 {
  display: block; margin: 0;
  font-family: inherit; font-size: inherit; font-weight: 600;
  line-height: 1.45; color: var(--text);
  text-align: left; text-wrap: pretty;
}
.faq .qa-chev {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 2px;
  width: 14px;
  height: 14px;
  margin: auto 0;
  color: var(--text-2);
  transform: rotate(90deg);
  transition: transform 0.15s ease, color 0.15s ease;
}
.faq .prose-col .qa summary:hover .qa-chev { color: var(--text); }
.faq .prose-col .qa[open] > summary .qa-chev { transform: rotate(270deg); }
@media (prefers-reduced-motion: reduce) { .faq .qa-chev, .faq .prose-col .qa summary::before { transition: none; } }
.faq .qa-a { padding: 0 4px 18px; }
.faq .prose-col .qa-a p { margin: 0; color: var(--text-prose); }
.faq .prose-col .qa-a p + p { margin-top: 12px; }
.faq .prose-col .qa-a ul { margin: 10px 0 0; padding-left: 1.25em; color: var(--text-prose); }
.faq .prose-col .qa-a li + li { margin-top: 8px; }
.faq .prose-col .qa-a ul + p { margin-top: 12px; }

/* ── L'INTITULÉ DE FAMILLE, À CHEVAL SUR LA COUTURE (20/08/2026, idée de
   Jonathan : « les superposer à la ligne de transition entre les deux couleurs, et
   ancrer leur flottage de cette manière »). C'est la bonne réponse, et pour une
   raison qu'il faut nommer : **la frontière EXISTE déjà** — c'est le bord de la
   section, là où le fond change. Un filet dessiné par-dessus la doublait ;
   l'intitulé posé à cheval s'en sert. Il ne flotte plus au-dessus d'une famille,
   il COUD les deux.
   LE FOND DE L'ÉTIQUETTE EST UNE TROISIÈME COULEUR, et il le faut : à cheval, elle
   a du fond de page sous sa moitié haute et du fond de famille sous sa moitié
   basse. Lui donner l'un des deux la ferait disparaître d'un côté — et en clair,
   `--bg-card` VAUT le fond de page (#fff) ; elle aurait été invisible une fois sur
   deux. Mesuré : `--fam-badge` se détache des DEUX (1,51 et 1,29 en sombre ; 1,43
   et 1,21 en clair).
   PLUS DE FILETS `::before`/`::after` : la couture les remplace, et deux traits pour
   une seule frontière, c'est le doublon qu'on vient de retirer.
   Le NOM reste le lien, sans chevron (décisions du 20/08 au soir). */
:root { --fam-badge: #d7d7e4; }
@media (prefers-color-scheme: dark) { :root { --fam-badge: #32323f; } }
/* ELLE RESTE FLOTTANTE TANT QU'ON EST DANS LA FAMILLE (20/08/2026, seconde
   demande de Jonathan : « pas moyen de les garder flottants pendant qu'on survole
   le contenu de la famille ? »). C'est le collant du début de soirée, mais sous la
   forme qu'il a retenue — et la différence n'est pas cosmétique : **ce qui avait
   été rejeté, c'était un BANDEAU pleine largeur** (1280 px, qui recouvrait le rail
   et empilait 116 px de chrome). Une étiquette de 162 px, centrée, laisse le rail
   tranquille à gauche et ne coûte que sa propre hauteur.
   `sticky` et non `absolute` : un élément ne peut pas être les deux, et c'est
   `sticky` qui donne les DEUX états — à cheval sur la couture au repos (par la
   marge négative, qui vaut la moitié de sa hauteur), collé sous la nav dès qu'on
   défile, relâché quand la famille se termine. Le conteneur borne le collant tout
   seul : rien à surveiller. */
/* `flow-root` n'est pas décoratif : sans lui, la marge négative de l'étiquette
   FUSIONNE avec celle de la section (margin collapsing du premier enfant) et
   remonte la SECTION ENTIÈRE — l'étiquette reste alors pile sur la couture au
   lieu de la chevaucher (mesuré : 0 px au-dessus, 29 en dessous). Il crée un
   contexte de formatage qui garde la marge à l'intérieur. */
.fam { position: relative; display: flow-root; background: var(--fam-zone); }
.fam-head {
  position: sticky; top: 69px; z-index: 4;
  display: flex; justify-content: center;
  margin: -15px 0 61px; padding: 0 24px;
}
.fam .fam-head h2 {
  margin: 0; padding: 7px 20px;
  background: var(--fam-badge);
  border-radius: 999px;
  font-family: var(--font-head); font-weight: 600; font-size: 0.78rem;
  letter-spacing: 0.05em; text-transform: uppercase; line-height: 1.2;
}
.fam .fam-head h2 .h-anchor {
  color: var(--text-2); text-decoration: none; transition: color 120ms ease;
}
.fam .fam-head h2 .h-anchor:hover,
.fam .fam-head h2 .h-anchor:focus-visible { color: var(--text); }

/* ── LA FERMETURE DE LA ZONE (20/08/2026) — la teinte seule ne suffisait pas, et
   la mesure dit pourquoi : les alternances internes valent 1,187 et la sortie de
   zone 1,247. **Cinq pour cent d'écart.** Après quatre transitions identiques,
   la cinquième passe pour la même chose — on ne peut pas dire « nouvelle famille »
   et « fin de l'argumentaire » dans le MÊME langage, en comptant sur l'intensité.
   D'où un signal d'une AUTRE NATURE, et deux à la fois : un filet ACCENT (la seule
   couleur qui n'a servi nulle part dans l'alternance) et de l'AIR (la clôture
   respire deux fois plus que l'écart entre deux familles). L'ouverture, elle,
   n'a besoin de rien : la bande indigo de #features la marque déjà. */
.fam.fam-last { border-bottom: 2px solid var(--accent-line); }
/*  ne matchait pas — #qol n’est pas la première <section> de
   son parent. L’adjacence, elle, dit exactement ce qu’on veut : la clôture qui
   SUIT la dernière famille. */
.fam.fam-last + .arg.solo { padding-top: 128px; }

/* ── LA NOTIFICATION D’OCCASION MANQUÉE (21/08/2026) — le visuel de
   #opportunities, repris de l’onboarding (`MissedOpportunityDemo`) sur
   proposition de Jonathan. La bannière glisse depuis le bord droit, se pose,
   repart par où elle est venue, et change d’exemple HORS CHAMP.

   FIDÈLE À L’APP, aux chaînes et aux durées près : mêmes textes que la vraie
   notification (« Vous avez un circut pour ça ! », « qmm → quand même »),
   entrée puis pose de 2,4 s, sortie, et l’exemple suivant pendant l’absence.
   Les SIX exemples de chaque langue sont ceux de l’onboarding, et tous sont
   vérifiés dans les packs (`fr-mots-courants`, `en-mots-courants`) — le site
   ne montre pas un déclencheur qui n’existe pas.

   CSS PUR, ET C’EST UN CHOIX : le visuel n’a besoin d’aucun script, donc il ne
   peut pas tomber en panne d’un ReferenceError comme le carrousel l’a fait. Le
   défilé des six exemples tient en deux animations synchronisées : la carte
   boucle sur 4 s, les exemples sur 24 (six fois quatre), chacun décalé d’un
   `animation-delay` NÉGATIF calculé pour que sa fenêtre de visibilité tombe
   pendant que la carte est dehors — c’est ce qui fait qu’on ne voit jamais un
   exemple changer sous les yeux.

   LES DÉCLENCHEURS EN `<code>` NU, sans `.trigger-badge` : dans un VISUEL qui
   reproduit une interface, le badge appartiendrait à l’interface reproduite
   (l’idiome de `.compare`, dans la vitrine) ; le badge reste à la prose. ── */
/* UNE ANIMATION QUI SORT DE SA BOÎTE ÉLARGIT LA PAGE (piège payé le
   21/08/2026, dans la minute qui a suivi la pose du visuel) : le
   `translateX(72px)` de la sortie poussait le `scrollWidth` du document à 423
   pour 375 de large — 48 px de défilement latéral sur TOUTE la page, pour une
   carte qui n’est visible que dans un bloc. Un `transform` ne se contente pas
   de déplacer ; hors d’un conteneur qui clippe, il agrandit la surface
   défilable comme le ferait un élément posé là. `overflow-x: clip` et non
   `hidden` : `clip` ne crée pas de contexte de défilement et laisse
   `overflow-y` visible.
   LE CLIP VIT SUR LA SECTION, PAS SUR LA BOÎTE DU VISUEL (02/10/2026, relevé
   par gnovel : « petit problème sur les ombres des exemples »). Posé sur
   `.missednotif`, il tranchait l’ombre de la carte à 14 px de ses bords
   quand `--shadow-lg` en porte 70 : un rectangle sombre aux arêtes nettes
   sous la bannière, flagrant en thème sombre. Le padding qui devait « lui
   rendre la place » n’en rendait qu’un cinquième. Or empêcher le défilement
   latéral ne demande pas un clip SERRÉ : n’importe quel ancêtre qui ne
   dépasse pas l’écran suffit. La section, large comme l’écran, fait le même
   travail (mesuré à 375 px, carte forcée en sortie : `scrollWidth` de 375
   avec elle, 409 sans aucun clip) et ne coupe l’ombre qu’au bord de l’écran,
   là où elle s’arrête de toute façon. `:has()` fait suivre le clip si le
   visuel change de section. Le padding reste : il ne sert plus l’ombre, il
   garde la carte à sa largeur. */
.missednotif { max-width: 368px; padding: 0 14px; }
.arg:has(.missednotif) { overflow-x: clip; }
.mn-card {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 10px 13px; border-radius: 13px;
  background: var(--bg-card); box-shadow: var(--shadow-lg);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  animation: mn-visite 4s ease-in-out infinite;
}
/* L’icône est l’ASSET OFFICIEL (`/assets/img/icon.svg`), pas un dessin
   refait : dégradé #4945D1 → #282483, liseré interne blanc 16 %, chevrons
   anguleux en butt/miter — les specs du kit de marque, qu’un SVG recopié à
   la main rate forcément (le premier jet les ratait toutes). Le squircle
   porte son propre rayon, rien à arrondir ici. */
.mn-glyph { flex: none; display: block; }
.mn-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mn-title { font-size: 0.94rem; font-weight: 600; color: var(--text); }
/* Les six exemples occupent LA MÊME cellule de grille : la carte garde une
   hauteur constante, quel que soit celui qui est visible. */
.mn-pairs { display: grid; }
.mn-pair {
  grid-area: 1 / 1; font-size: 0.94rem; color: var(--text-2);
  opacity: 0; animation: mn-releve 24s step-end infinite;
}
.mn-pair code { font-family: var(--font-mono); font-size: 0.92em; }
.mn-pair:nth-child(1) { animation-delay: 0s; }
.mn-pair:nth-child(2) { animation-delay: -20s; }
.mn-pair:nth-child(3) { animation-delay: -16s; }
.mn-pair:nth-child(4) { animation-delay: -12s; }
.mn-pair:nth-child(5) { animation-delay: -8s; }
.mn-pair:nth-child(6) { animation-delay: -4s; }
/* Entrée à 0-10 %, pose jusqu’à 70, sortie jusqu’à 78, absente ensuite —
   les proportions de l’app ramenées à un cycle de 4 s. */
@keyframes mn-visite {
  0%   { opacity: 0; transform: translateX(72px); }
  10%  { opacity: 1; transform: translateX(0); }
  70%  { opacity: 1; transform: translateX(0); }
  78%  { opacity: 0; transform: translateX(72px); }
  100% { opacity: 0; transform: translateX(72px); }
}
/* 16,6 % de 24 s = 4 s : chaque exemple tient exactement une visite. */
@keyframes mn-releve {
  0%     { opacity: 1; }
  16.66% { opacity: 0; }
  100%   { opacity: 0; }
}
/* Mouvement réduit : la bannière reste POSÉE avec son premier exemple, comme
   dans l’onboarding — le visuel garde son sens sans bouger d’un pixel. */
@media (prefers-reduced-motion: reduce) {
  .mn-card { animation: none; opacity: 1; transform: none; }
  .mn-pair { animation: none; }
  .mn-pair:nth-child(1) { opacity: 1; }
}

/* ── LA TABLE DE CORRESPONDANCE DES ÉMOJIS (21/08/2026) — le visuel de
   #emojis. Le CARROUSEL du hero montre déjà la MÉCANIQUE (on tape un code,
   l'émoji apparaît) ; le refaire ici n'apprendrait rien de neuf (« ce serait un
   peu con de refaire la même chose », Jonathan). Ce bloc montre donc le
   RÉPERTOIRE, c'est-à-dire l'argument que le carrousel ne peut pas porter :
   PLUSIEURS codes mènent au même émoji — le français, l'anglais, et ceux que
   GitHub ou Slack ont appris à tout le monde. Les trois affirmations du
   paragraphe, montrées au lieu d'être seulement écrites.
   STATIQUE, à dessein : après la notification d'occasion manquée, une seconde
   animation dans la même page coûterait plus qu'elle ne rapporte, et une table
   se lit d'un coup d'œil là où une animation se regarde.
   TOUS LES CODES SONT VÉRIFIÉS dans `emoji.json` — le pack les porte vraiment,
   et l'ordre suit la LANGUE de la page (le code français d'abord côté FR).
   `.trigger-badge` et non `<code>` nu : ce sont des DÉCLENCHEURS, ce qu'on
   tape, et c'est le style que la doctrine leur réserve (l'idiome de `.compare`
   vaut pour du texte reproduit dans un éditeur, pas pour un déclencheur). ── */
/* 512 et non 420 depuis le 28/08 : les mots-clefs partagent la ligne des codes
   (voir .em-kws). La largeur est calée sur l'ANGLAIS, pas sur le français —
   « :thumbsup: :+1: :pouce: (y) approve ok » ne laissait que 12 px de marge à
   480 px quand la rangée française en gardait 35. La règle « on dimensionne en
   français » est un défaut de prudence, jamais une dispense de mesurer les deux.
   La carte reste plus étroite que la colonne de prose (660) : c'est une table,
   pas une figure pleine largeur. */
.emojimap { max-width: 512px; display: grid; gap: 8px; }
.em-row {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 12px;
  padding: 9px 13px; border-radius: var(--radius-sm);
  background: var(--bg-2); border: 1px solid var(--border);
}
/* `align-items: center` : sans lui les deux registres s'alignent sur leur
   ligne de base et les capsules, plus courtes que les badges, remontent —
   « moches et mal centrées verticalement » (Jonathan, capture à l'appui). */
.em-codes { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.em-glyph { font-size: 1.45rem; line-height: 1; }
/* LE CODE MAISON, DIT PAR LA FORME (21/08/2026, demande de Jonathan — son
   `(y)` pour 👍, rejoint le 29/08 par `:lol:` pour 😂). Les codes du pack sont
   pleins ; celui qu'on ajoute soi-même est CREUX, liseré pointillé — le langage
   courant de ce qui n'est pas fourni mais rapporté.
   UN `outline`, PAS UN `border` (29/08) : en hauteur AUTO, `box-sizing` ne joue
   pas et une bordure AJOUTE ses 2 px à la boîte. La compensation qui traînait
   ici (`calc(0.08em - 1px)`) visait un padding qui n'est pas celui qui
   s'applique dans ce contexte, et rendait le badge 3,4 px plus COURT que ses
   voisins ; sans elle il devenait 2 px plus HAUT. L'outline, lui, se dessine
   hors flux : la boîte garde exactement la taille de ses voisines, sans aucune
   valeur à retoucher le jour où leur padding change. `outline-offset: -1px` le
   pose à l'intérieur, à la place qu'occuperait la bordure.
   Défaut resté invisible tant que la rangée s'alignait sur la ligne de base ;
   il a sauté aux yeux dès le passage en `align-items: center`.
   La légende dit le reste en cinq mots : sans elle, le pointillé se lit comme
   une variante graphique, pas comme un ajout personnel. */
.em-mine {
  background: transparent; box-shadow: none;
  outline: 1px dashed var(--accent-line); outline-offset: -1px;
}
/* LES MOTS-CLEFS (28/08, demande de Jonathan) — le SECOND registre de la
   recherche, et le point que les seuls codes ne pouvaient pas faire : on
   trouve l'émoji SANS connaître son code, en tapant ce qu'on a en tête.
   D'où deux vocabulaires dans la même rangée, ceux de l'app : `.trigger-badge`
   pour ce qu'on TAPE (mono, plein), `.em-kw` pour ce qu'on RANGE (capsule
   accent-soft, l'idiome de `.se-kw`). Ils sont sur une SECONDE ligne, pas
   mêlés aux codes : deux registres côte à côte se liraient comme une seule
   liste hétérogène.
   Tous les mots-clefs viennent d'emoji.json — « lol » est vraiment celui de
   `:mdr:`, avec « rire ». */
/* LES MOTS-CLEFS PARTAGENT LA LIGNE DES CODES (28/08, arbitrage de Jonathan).
   Un premier jet les mettait sur une seconde ligne, par crainte que deux
   registres côte à côte se lisent comme une liste hétérogène ; à l'écran, la
   différence de STYLE suffit (mono plein contre capsule accent-soft), et la
   rangée retombe à une seule ligne — 52 px au lieu de 89. Ce qui sépare les
   deux groupes est l'ÉCART : 14 px contre 6 entre deux badges voisins.
   En étroit, `flex-wrap` les renvoie naturellement à la ligne. */
.em-kws { display: flex; flex-wrap: wrap; gap: 5px; margin-left: 8px; }
/* La capsule est calée sur la HAUTEUR du badge de code (31 px mesurés) : deux
   objets de tailles voisines mais inégales sur une même ligne se lisent comme un
   défaut, pas comme deux registres. Le radius plein (pilule) reste l'idiome des
   mots-clefs de l'app ; c'est la FORME qui distingue, pas la taille. */
.em-kw { display: inline-flex; align-items: center; padding: 6px 12px;
  border-radius: 999px; background: var(--accent-soft); color: var(--accent-text);
  font-size: 0.88rem; line-height: 1.35; }
.em-note { margin: 2px 0 0; font-size: 0.86rem; color: var(--text-2); text-align: center; }

/* ── LE SÉLECTEUR DE VARIABLES (21/08/2026) — le visuel de #dynamic-variables.
   IDENTITÉ SOMBRE CONSTANTE, comme .palette-demo : dans l'app, ce popover vit
   dans l'éditeur et suit le thème, mais sur la page il doit avoir la même encre
   que l'autre visuel d'interface de la landing. Deux visuels sombres qui
   diffèrent de teinte se voient tout de suite ; la fidélité au pixel (le fond
   #282b2d des captures) perdrait plus qu'elle ne gagnerait.
   POURQUOI IL EST MAISON et non une capture : doctrine en tête de varmenu.js.
   L'EN-TÊTE N'A PAS SON CHEVRON, à dessein. Dans l'app, il dit « cliquez pour
   replier » ; ici rien ne se replie, et le garder aurait promis un geste qui
   n'existe pas. Sans lui, l'encadré se lit comme une étiquette — ce qu'il est.
   Le rail d'onglets, LUI, est un vrai contrôle : c'est la seule chose de ce
   visuel qui a l'air cliquable, et elle l'est.

   DEUX COLONNES, ET UNE HAUTEUR FIGÉE (22/08/2026, deux retours de Jonathan).
   La carte faisait 424 px de haut et CHANGEAIT de hauteur à chaque onglet, ce
   qui décalait tout le bas de la page — « très désagréable à la lecture », le
   défaut déjà arbitré pour le carrousel du hero. Les deux se soignent ensemble,
   et dans cet ordre :
   · les panneaux de NEUF variables passent en deux colonnes au-dessus de
     700 px (`:has(> li:nth-child(9))`, donc aucune classe à tenir à jour), en
     remplissage VERTICAL pour garder l'ordre de l'app quand on lit une colonne.
     Les colonnes ne sont pas égalisées : chacune fait la largeur de son contenu,
     sinon les pastilles, qui sont en `fit-content`, dessineraient un damier ;
   · les panneaux de quatre restent sur une colonne. C'est ce qui RÉDUIT l'écart
     entre le plus grand et le plus petit à une seule ligne (5 contre 4) : les
     égaliser en colonnes aurait laissé 3 lignes de vide à figer ;
   · varmenu.js pose alors un `min-height` égal au plus grand panneau. La
     hauteur ne bouge plus, donc la transition qui l'accompagnait a disparu.
   Sous 700 px, tout revient à une colonne : à 375 px, deux colonnes rendraient
   les aperçus illisibles (ellipse partout). Le vide y est donc plus grand,
   c'est le prix assumé de la page qui ne bouge plus. ── */
/* 620 px ET NON 560 (22/08/2026) : à 560, les deux colonnes du panneau
   presse-papiers réclamaient 591 px pour 536 disponibles, et c'est l'aperçu de
   « Majuscule en début de phrase » — le libellé le plus long — qui payait
   l'écart, réduit à deux caractères. Or c'est justement la ligne qui doit se
   lire, puisqu'elle est la seule à se distinguer de « Initiales en majuscules ».
   L'aplomb avec .palette-demo (560) est perdu, sans dommage : les visuels de
   cette page ont déjà des largeurs propres à leur contenu (.emojimap 420,
   .showcase 660) — c'est le CENTRE qui est commun, pas la largeur. */
.varmenu { max-width: 620px; text-align: left; }
/* UN <div> ET NON UN <p>, ET CE N'EST PAS UN DÉTAIL (mordu le 21/08/2026) :
   `.prose-col p` (0,1,1) porte une couleur ET une marge, donc il bat toute
   règle en une seule classe — la même collision que celle qui a fait rendre
   l'écho de #collections en encre de prose depuis sa naissance. En <p>,
   l'étiquette prenait le gris du texte courant et perdait ses 10 px de marge,
   collée à la carte. Le <div> est de toute façon plus juste : ce n'est pas un
   paragraphe, c'est le bouton de l'app rendu en étiquette. */
/* CENTRÉE, comme le titre et la carte (22/08/2026, relevé par Jonathan). Dans
   l'app le bouton ⌘J est ferré à gauche au-dessus du popover, mais la page a sa
   propre grammaire — « ce qui s'affiche se centre, ce qui se lit se ferre à
   gauche » — et ici le titre du bloc est centré, la carte aussi : l'étiquette
   seule à gauche faisait décrocher l'ensemble. `flex` + `width: fit-content` et
   non `inline-flex` : un inline-flex ne se centre pas par ses marges. */
.vm-toggle {
  display: flex; width: fit-content; align-items: center; gap: 7px; margin: 0 auto 9px;
  font-size: 0.88rem; color: #ececf1;
  background: #23232c; border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 9px; padding: 5px 11px;
  /* SURFACE SOMBRE FIXE, COMPOSANT À JETONS DE THÈME (02/10/2026, relevé par
     Grégoire : « contraste assez faible sur ⌘J »). L’étiquette est sombre
     dans les deux thèmes, mais la keycap prend ses couleurs aux jetons : en
     thème clair, l’indigo fait pour le blanc sortait sur ce fond, à 2,1 de
     contraste. La surface redéclare donc les jetons d’accent du thème
     SOMBRE (valeurs de site.css), et tout composant posé dedans les suit. */
  --accent-text: #a8a3ff;
  --accent-soft: rgba(107, 102, 242, 0.16);
  --accent-line: rgba(107, 102, 242, 0.30);
}
.vm-toggle .keycap { flex: none; }
.vm-braces { font-family: var(--font-mono); color: #b9b9c2; }
.vm-card {
  background: #23232c; border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 14px; box-shadow: var(--shadow-lg); padding: 11px;
}
.vm-tabs { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-bottom: 9px; }
.vm-tab {
  font: inherit; font-size: 0.82rem; line-height: 1.2; color: #b9b9c2;
  background: transparent; border: 0; border-radius: 999px;
  padding: 5px 11px; cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.vm-tab:hover { color: #ececf1; background: rgba(255, 255, 255, 0.06); }
.vm-tab:focus-visible { outline: 2px solid #8b86ff; outline-offset: 2px; }
.vm-tab.on { color: #a8a3ff; background: rgba(107, 102, 242, 0.22); font-weight: 600; }
/* `minmax(0, 1fr)` ET NON la piste `auto` par défaut : une piste auto se
   dimensionne au MAX-CONTENT, donc le panneau presse-papiers débordait de la
   carte de 28 px en étroit dès que les libellés ont cessé de se couper
   (mesuré à 375 px). Bornée, la piste rend l'ellipse à l'aperçu. La règle des
   deux colonnes s'y superpose sans conflit : la première colonne reste
   explicite, la seconde suit `grid-auto-columns`, bornée elle aussi. */
/* LE GROUPE EST CENTRÉ, LES PASTILLES RESTENT ALIGNÉES (22/08/2026, retour de
   Jonathan). Une piste étirée (`1fr`) occupe toute la carte, et comme les
   pastilles sont en `fit-content`, tout le vide s'accumulait DU MÊME CÔTÉ —
   spectaculaire sur le panneau Aléatoire, quatre pastilles courtes contre une
   moitié de carte vide. Les pistes prennent donc leur largeur naturelle
   (`max-content`) et c'est la GRILLE qui se centre (`justify-content`), pas
   chaque pastille : `justify-items: center` les aurait centrées une à une, donc
   désalignées entre elles, ce que Jonathan ne veut précisément pas.
   `minmax(0, …)` garde le droit de se rétracter quand la place manque — sans
   ce plancher à zéro, une piste max-content déborde de la carte. */
.vm-panel { list-style: none; display: grid; grid-template-columns: minmax(0, max-content);
  justify-content: center; gap: 4px; margin: 0; padding: 0; align-content: start; }
.vm-panel[hidden] { display: none; }
.vm-panel li {
  display: flex; align-items: baseline; gap: 6px; width: fit-content; max-width: 100%;
  padding: 4px 9px; border-radius: 7px;
  background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.08);
}
/* LE LIBELLÉ NE SE COUPE JAMAIS, C'EST L'APERÇU QUI CÈDE — comme dans l'app,
   et c'est ce qui tient la hauteur (22/08/2026). Sans `nowrap`, cinq pastilles
   du panneau presse-papiers passaient sur deux lignes en colonne étroite
   (54 px au lieu de 32, mesuré) : le panneau réclamait 244 px là où les autres
   en demandent 177, et c'est ce maximum-là qu'on aurait figé pour tout le
   monde. Un aperçu tronqué est fidèle (les captures de l'app en portent) ; un
   libellé sur deux lignes ne l'est pas. */
.vm-panel b { font-weight: 600; font-size: 0.84rem; color: #ececf1; white-space: nowrap; }
/* L'aperçu est en MONO et en gris, comme dans l'app : ce n'est pas du texte
   qu'on lit, c'est une valeur qu'on reconnaît. `min-width: 0` + ellipse pour
   que la pastille cède en étroit au lieu de déborder de la carte. */
.vm-panel code {
  font-family: var(--font-mono); font-size: 0.75rem; color: #9a9aa5;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vm-panel code:empty { display: none; }
@media (min-width: 700px) {
  /* `grid-auto-columns` BORNÉ, sinon la grille ne contraint rien : des colonnes
     `auto` prennent la largeur de leur contenu, et le panneau presse-papiers
     débordait de la carte de 114 px (mesuré) depuis que les libellés ne se
     coupent plus. Deux colonnes égales bornent la casse et rendent l'ellipse
     à l'aperçu, à qui elle revient. Les pastilles, elles, restent en
     `fit-content` : c'est la COLONNE qui est bornée, pas elles, donc aucun
     damier. */
  .vm-panel:has(> li:nth-child(9)) {
    grid-auto-flow: column; grid-template-rows: repeat(5, auto);
    grid-template-columns: repeat(2, minmax(0, max-content)); column-gap: 10px;
  }
}
@media (max-width: 560px) {
  .vm-panel b { font-size: 0.8rem; }
  .vm-panel code { font-size: 0.72rem; }
}

/* ── LES VIDÉOS DE DÉMONSTRATION (22/08/2026) — le premier est celui de
   #palette. Doctrine du pilotage (mouvement réduit, hors écran, chargement
   différé) en tête de demovideo.js.
   ELLE VIT DANS LE CONTENEUR, PAS DANS LA COLONNE DE PROSE, et c'est une
   contrainte de LISIBILITÉ, pas un choix esthétique : le panneau ⌘CC occupe
   57 % de la largeur du cadre filmé, donc à 620 px il serait rendu à 355 px
   pour 640 pt réels — soit du texte de résultat à 7 px, illisible, alors que
   c'est précisément ce que la démo demande de lire. À 900 px, le panneau
   retrouve 80 % de sa taille d'écran. L'idiome existe déjà : le ruban de
   #collections sort lui aussi de la colonne (952 px du .container).
   LE FOND EST DÉJÀ NOIR DANS LA VIDÉO, donc pas de fond ici — seulement le
   rayon et l'ombre, pour que le cadre appartienne à la même famille que
   .palette-demo et .varmenu. `aspect-ratio` évite le saut de mise en page
   pendant que la vidéo se charge (elle est en `preload="none"`). ── */
.demo-video {
  position: relative; max-width: 900px; margin: 30px auto 34px;
  border-radius: 14px; overflow: hidden; box-shadow: var(--shadow-lg);
  line-height: 0;
}
/* AUCUN `aspect-ratio` ICI, ET C’EST VOULU (23/08/2026). Le navigateur le déduit
   des attributs `width`/`height` de la balise — vérifié à la mesure : il calcule
   `auto 1800 / 1444` et réserve la hauteur au pixel près. La règle CSS était
   donc inutile depuis le début, et elle est devenue NUISIBLE le jour où une page
   a porté DEUX vidéos de formats différents (#palette et #create) : une règle
   unique ne peut pas servir les deux, et la version par langue ne réglait que la
   moitié du problème. Les dimensions vivent désormais à UN SEUL endroit, la
   balise elle-même — le mode d’échec « CSS et HTML désaccordés » n’existe plus.
   NE PAS remettre de rapport d’aspect ici sans relire ça. */
.demo-video video { display: block; width: 100%; height: auto; background: #0a0a0c; }
/* Le bouton se pose SUR la vidéo, en bas à droite, et ne se voit qu'au survol
   ou au focus — sauf quand la vidéo est à l'arrêt, où il est la seule porte
   vers la démo (mouvement réduit, ou lecture arrêtée à la main). */
.dv-toggle {
  position: absolute; right: 12px; bottom: 12px; width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border-radius: 50%; cursor: pointer; line-height: 1;
  background: rgba(20, 20, 26, 0.72); color: #ececf1;
  border: 1px solid rgba(255, 255, 255, 0.18);
  opacity: 1; transition: opacity 140ms ease, background-color 140ms ease;
}
.demo-video.dv-playing .dv-toggle { opacity: 0; }
.demo-video:hover .dv-toggle, .dv-toggle:focus-visible { opacity: 1; }
.dv-toggle:hover { background: rgba(20, 20, 26, 0.92); }
.dv-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dv-toggle svg { width: 13px; height: 13px; fill: currentColor; }
.dv-toggle .dv-pause, .demo-video.dv-playing .dv-toggle .dv-play { display: none; }
.demo-video.dv-playing .dv-toggle .dv-pause { display: block; }
@media (prefers-reduced-motion: reduce) { .demo-video .dv-toggle { opacity: 1; } }
@media (max-width: 700px) { .demo-video { margin: 24px auto 26px; border-radius: 10px; } }

/* ══════ LE FICHIER DE PROFIL (#export) ══════════════════════════════════
   Un EXTRAIT du fichier, pas la maquette d'un écran : le canon n'est pas une
   vue SwiftUI mais le format lui-même, vérifié À L'OCTET sur un vrai
   `.circutprofile` (BackupCodec : prettyPrinted + sortedKeys +
   withoutEscapingSlashes, dates ISO 8601). D'où les tics de Foundation que
   personne n'invente : l'espace AVANT les deux-points, les clés en ordre
   ALPHABÉTIQUE, l'indentation à deux espaces.
   Ce que la figure ajoute à la prose, ce n'est pas qu'un fichier existe —
   la prose le dit — c'est ce que « lisible » veut dire : des noms en clair,
   et VOTRE texte au milieu, en clair lui aussi.
   COLORATION PAR TYPE, comme un éditeur, jamais par « à qui appartient la
   donnée » : une date et un mot-clef sont deux chaînes, les colorer
   différemment trahirait la capture. Les chaînes portent donc le ton plein
   (c'est là qu'est le contenu), le squelette s'efface.
   PAS de numéros de ligne : la barre de titre dit déjà « fichier ouvert »,
   et trente numéros ajouteraient du bruit sans rien prouver de plus. */
.shot-file { justify-self: center; width: 100%; }
.pf-panel { padding: 0; overflow: hidden; }
.pf-bar { display: flex; align-items: center; gap: 7px; padding: 8px 13px;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--text) 4%, transparent); }
.pf-ico { width: 13px; height: 13px; flex: none; opacity: 0.5; }
.pf-name { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-2); }
/* `overflow-wrap: anywhere` plutôt qu'un défilement horizontal : la ligne du
   texte étendu dépasse la colonne de 660 px, et un éditeur en retour à la
   ligne automatique fait exactement ça. Le défilement, lui, cacherait la
   moitié de la seule ligne qui porte la preuve. */
.pf-code { margin: 0; padding: 13px 15px; font-family: var(--font-mono);
  font-size: 0.76rem; line-height: 1.45; white-space: pre-wrap;
  overflow-wrap: anywhere; tab-size: 2;
  color: color-mix(in srgb, var(--text-2) 72%, transparent); }
/* COLORATION : la palette est celle des tuiles de #stats (.st-teal/.st-violet/
   .st-green), tirée des couleurs système d'Apple — la page n'en gagne pas une
   nouvelle. Attribution par TYPE, comme un éditeur : clés en indigo (notre
   identité, et c'est la structure), chaînes en vert, littéraux en violet,
   ponctuation en gris. Les valeurs claires sont FONCÉES par rapport aux icônes
   de #stats : là-bas elles vivent sur un fond teinté à 14 % en 19 px, ici
   c'est du texte de 12 px sur fond de carte.
   RATIOS MESURÉS (sonde canvas : on peint l'échantillon sur le fond réel et on
   laisse le navigateur résoudre le composite — une sonde qui lit color-mix à la
   main se trompe, ses composantes sortent en 0-1 et non en 0-255).
                clair   sombre
     clés        7,01    7,85
     chaînes     5,39    9,59
     littéraux   5,75    7,11
     ponctuation 3,51    3,93   (structurelle, volontairement en retrait) */
.pf-k { color: var(--accent-text); }
.pf-s { color: #137a43; }
.pf-b { color: #8b41c4; }
@media (prefers-color-scheme: dark) {
  .pf-s { color: #4cd964; }
  .pf-b { color: #c88bf5; }
}
@media (max-width: 700px) { .pf-code { font-size: 0.7rem; padding: 11px 12px; } }

/* ══════ L'APERÇU D'IMPORT (#import) ═══════════════════════════════════════
   Le pendant de #share : là-bas on montre celui qui REÇOIT un partage, ici
   celui qui MIGRE. C'est ce que la prose annonce sans le montrer — « rien ne
   s'écrit avant que vous ayez vu l'aperçu ».
   POURQUOI PAS UN MUR DE LOGOS (proposition de Jonathan, écartée sur pièces) :
   la section porte DÉJÀ les neuf noms en toutes lettres, un mur les redirait
   en deux langages ; deux des neuf entrées n'ont structurellement pas de logo
   (« Remplacements macOS » est un panneau des Réglages, « CSV » n'est pas un
   produit) et ce sont les portes de qui ne vient pas d'un concurrent ; et un
   seul des neuf outils est installé ici, donc le pipeline d'extraction de
   #works-everywhere ne couvrirait qu'un neuvième du mur. NB : /switch/, la
   page écrite pour les switchers, ne montre aucun logo non plus.
   Le nom du concurrent est dans le TITRE de la feuille (import.preview.title
   = « Importer depuis %@ ») : la figure le nomme sans l'afficher.
   FIDÈLE AU COMPOSANT : les groupes sont REPLIÉS d'office (doctrine 1 de
   ImportPreviewSheet — « à 600 snippets, une liste à cocher n'est pas un
   contrôle »), la case a trois états (square / minus.square.fill /
   checkmark.square.fill), le compte est « sélectionnés/total » en chiffres
   tabulaires, et les boutons du pied portent leurs indices esc et ↩. */
.shot-import { justify-self: center; width: 100%; }
.ip-panel { padding: 0; }
.ip-head { display: flex; align-items: baseline; gap: 12px; padding: 14px 16px 12px; }
.ip-title { font-family: var(--font-head); font-weight: 600; font-size: 1.02rem; color: var(--text); }
.ip-sub { font-size: 0.82rem; color: var(--text-2); margin-top: 3px; }
.ip-htxt { flex: 1; min-width: 0; }
.ip-link { flex: none; font-size: 0.86rem; color: var(--accent-text); }
/* LA LISTE DÉFILE, ET ÇA SE VOIT (28/08, relevé par Jonathan). Sans ce
   bornage, on lisait quatre groupes totalisant 119 sous un pied annonçant 575 —
   l'écart était juste (14 dossiers, 10 hors champ) mais RIEN ne le disait, et
   un lecteur qui additionne tombait sur un mensonge apparent. La feuille de
   l'app est à taille FIXE (560×560) avec ses groupes dans un ScrollView : à 14
   dossiers elle défile pour de bon. La rangée coupée par le bord et le fondu
   sont donc FIDÈLES, pas un artifice — c'est ce que montrerait une capture.
   RÈGLE : un total qui dépasse ce que la figure montre doit être accompagné du
   signe qu'elle ne montre pas tout. Sinon, la cohérence arithmétique se
   retourne contre elle. */
.ip-groups { display: grid; gap: 2px; padding: 0 12px 6px; }
/* TOUS LES DOSSIERS SONT MONTRÉS, et c'est une contrainte d'HONNÊTETÉ, pas de
   place (28/08, Jonathan, au bout de trois passes). Une figure qui annonce un
   total doit laisser le lecteur le RETROUVER en additionnant ce qu'il voit :
   47 + 218 + 96 + 142 = 503, et le pied dit 503. Tout hors-champ rouvre l'écart,
   qu'on le signale ou non — un lecteur n'admet pas des données invisibles.
   Les deux tentatives précédentes (rangée coupée + fondu, puis coupe franche)
   réduisaient l'écart de 456 à 395 puis à 72 : toutes fausses au même titre.
   Le nombre de dossiers suit donc ce qu'on peut montrer, pas l'inverse. */
.ip-group { display: flex; align-items: center; gap: 8px; padding: 7px 12px;
  border-radius: 7px; background: color-mix(in srgb, var(--text) 5%, transparent); }
.ip-box { flex: none; width: 15px; height: 15px; }
.ip-box.ip-on { color: var(--accent); }
.ip-box.ip-off { color: var(--text-2); opacity: 0.7; }
.ip-chev { flex: none; width: 9px; height: 9px; color: var(--text-2); opacity: 0.6; }
.ip-name { flex: 1; min-width: 0; font-size: 0.92rem; font-weight: 500; color: var(--text); }
.ip-n { flex: none; font-size: 0.8rem; color: var(--text-2); opacity: 0.8;
  font-variant-numeric: tabular-nums; }
.ip-warn { display: flex; align-items: center; gap: 7px; padding: 9px 24px 11px;
  font-size: 0.84rem; color: var(--text-2); }
.ip-foot { display: flex; align-items: center; gap: 12px; padding: 12px 16px 14px;
  border-top: 1px solid var(--border); }
.ip-ftxt { flex: 1; min-width: 0; }
.ip-add { font-size: 0.92rem; color: var(--text); }
.ip-cons { font-size: 0.8rem; color: var(--text-2); margin-top: 2px; line-height: 1.4; }
.ip-btns { flex: none; display: flex; gap: 8px; }
.ip-btn { display: inline-flex; align-items: center; gap: 6px; font-size: 0.86rem;
  padding: 5px 12px; border-radius: 7px; border: 1px solid var(--border);
  background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text); }
.ip-btn.ip-go { background: var(--accent); border-color: transparent; color: #fff; }
.ip-btn .keycap { font-size: 0.82em; }
.ip-btn.ip-go .keycap { background: rgba(255, 255, 255, 0.22); color: #fff;
  border-color: rgba(255, 255, 255, 0.3); }
@media (max-width: 620px) {
  .ip-foot { flex-wrap: wrap; }
  .ip-btns { margin-left: auto; }
}

/* ══════ L'ICÔNE DE #privacy (28/08) ══════════════════════════════════════
   LES QUATRE CARTES SONT RETIRÉES (« Pas de compte · Pas de cloud · Pas de
   télémétrie · Pas d'IA ») : trois d'entre elles étaient MOT POUR MOT dans le
   paragraphe qui suit. Leur ajouter des icônes aurait décoré une redite.
   Deux remplacements ont été proposés et écartés — la maquette du réglage des
   mises à jour (Jonathan : « ça met l'accent sur la seule exception au 100 %
   local », et il a raison : la figure se regarde AVANT la prose, on aurait
   rendu saillante une exception que le texte avoue déjà à sa juste taille),
   puis quatre cartes AFFIRMATIVES tirées de preuves observables. Décision de
   Jonathan : « cette section n'est qu'une porte d'entrée, il y a une page
   complète sur le sujet » — même raisonnement que #keyboard-first, tranché
   sans visuel. UNE PORTE N'A PAS À FAIRE LA DÉMONSTRATION.
   L'ICÔNE N'EST PAS UN CADENAS, et c'est délibéré : le cliché de la banque
   d'images n'aurait rien dit de plus que le titre. C'est LE LOGO DE L'APP
   (`/assets/img/icon.svg`, 787 o, vectoriel, déjà déployé), et non un dessin
   maison : un premier jet représentait un MacBook au trait avec le double
   chevron à l'écran — Jonathan : « ça fait un peu cheap ». Il avait raison,
   et la leçon est générale : UN DESSIN MAISON ENTRE EN CONCURRENCE AVEC UNE
   IDENTITÉ QUI EXISTE DÉJÀ, et perd. Le logo ne PROUVE rien sur la vie
   privée — il n'a pas à le faire : la section est une porte, la preuve vit
   sur /privacy/. Il sert de repère et de respiration.
   C'est la SEULE icône de section de la landing : les autres sections n'en ont
   pas, et n'en auront pas. Elle tient lieu de figure, elle ne s'ajoute pas. */
/* L'ORDINATEUR EST EN CSS, PAS EN SVG DESSINÉ. Le premier jet traçait le
   MacBook ET le double chevron à la main : Jonathan a trouvé le glyphe « moche »
   et l'ensemble « un peu cheap ». La correction tient en deux gestes — le
   contenu de l'écran est le VRAI logo (`icon.svg`, vectoriel, celui du Dock),
   et le châssis se réduit à deux rectangles arrondis, sans trait décoratif.
   Un contour sobre autour d'un objet riche : c'est le contraste qui porte,
   pas le dessin. RÈGLE : ne jamais REDESSINER ce qui existe déjà en asset. */
.pz-mac { width: 128px; margin: 6px auto 26px; }
/* L'écran est en 16:10 (116×72) et la base DÉBORDE de part et d'autre, comme
   un portable ouvert. Sans ce débordement, les deux rectangles se lisaient
   comme un cadre posé sur une barre. */
.pz-screen { width: 116px; height: 72px; margin: 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid color-mix(in srgb, var(--text-2) 45%, transparent);
  border-radius: 9px; }
/* La base déborde de l'écran des deux côtés, comme un vrai capot ouvert. */
.pz-base { width: 128px; height: 6px; margin-top: 3px; border-radius: 0 0 5px 5px;
  background: color-mix(in srgb, var(--text-2) 45%, transparent); }
.pz-screen img { display: block; width: 44px; height: 44px; }

/* ══════ LE CLAVIER DE #keyboard-first (29/08) ═══════════════════════════════
   La section était la SEULE, avec #lifetime-pricing, à n'avoir aucun visuel —
   #works-everywhere a son mur d'apps, #opportunities sa notification. Le prix
   n'a rien à montrer et reste nu ; celle-ci reçoit une icône, comme #privacy.
   UNE KEYCAP ⌘ AGRANDIE A ÉTÉ ESSAYÉE PUIS ÉCARTÉE (Jonathan) : la prose de la
   section porte DÉJÀ quatre keycaps, dont deux ⌘, dans le même style. L'échelle
   les distinguait (3,3×) mais le VOCABULAIRE était identique — un emblème qui
   répète l'objet que la prose montre déjà n'ajoute rien. Le test 1 des figures
   vaut aussi pour les icônes.
   LA GÉOMÉTRIE EST CALCULÉE, PAS TÂTONNÉE (scratchpad, clavier.py) : rangées
   égales, gouttières constantes, et la rangée du bas définie en PARTS (1,15 /
   1,15 / 1,6 / 4,6 / 1,6 / 1,15) plutôt qu'en pixels — elle se recalcule seule
   si la grille change. C'est ce qui évite le « un peu cheap » du MacBook
   dessiné à la main : les proportions ne sont pas devinées.
   TROIS ÉPAISSEURS, et c'est ce qui donne la profondeur : le châssis, les
   touches (plus fines, plus pâles), la touche ⌘ (accent). L'œil lit un clavier,
   puis LA touche — l'ordre exact de la phrase de la section. */
.kb-svg { display: block; width: 192px; height: auto; margin: 6px auto 26px; }
.kb-case { fill: none; stroke: color-mix(in srgb, var(--text-2) 50%, transparent);
  stroke-width: 1.8; }
.kb-keys rect { fill: none; stroke: color-mix(in srgb, var(--text-2) 34%, transparent);
  stroke-width: 1.1; }
.kb-cmd { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 1.4; }
.kb-glyph { fill: var(--accent-text); font-size: 10px; font-weight: 500;
  text-anchor: middle; dominant-baseline: central; }

/* ── LES SUGGESTIONS OBSERVÉES (#suggestions, 29/08/2026) — la LISTE de la
   modale, SANS son chrome : un en-tête et des lignes, donc un panneau et pas
   une troisième feuille après #share et #import. Doctrine complète dans le
   commentaire HTML de fr/index.html.
   La ligne suit `ObservedSuggestionsSheet.row` : chevron MINEUR de la marque en
   accent, texte et compte empilés, bouton BORDÉ neutre, croix en tertiaire.
   L’accent va à l’IDENTITÉ, jamais au contrôle (§7 du briefing) — teinter le
   bouton a été essayé dans l’app le 27/08 et rejeté à l’écran (« ça fait
   bizarre ») : ce qui manquait à la liste était une colonne d’ancrage, pas un
   bouton plus voyant. */
.sg-panel { padding: 16px 18px; display: grid; gap: 4px; }
.sg-title { font-family: var(--font-head); font-weight: 600; font-size: 1.02rem;
  color: var(--text); }
.sg-sub { font-size: 0.88rem; color: var(--text-2); margin-bottom: 8px; }
.sg-list { display: grid; gap: 10px; padding-top: 12px;
  border-top: 1px solid var(--border); }
.sg-item { display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: 11px; }
/* Le glyphe garde la boîte de 13 px du composant SwiftUI ; sa viewBox est
   calculée sur l’emprise PEINTE, la pointe miter comprise, donc rien ne
   déborde et il n’y a aucun `overflow` à corriger. */
.sg-chev { width: 13px; height: 13px; color: var(--accent-text); }
.sg-body { display: grid; gap: 2px; min-width: 0; }
.sg-txt { font-size: 0.92rem; line-height: 1.35; color: var(--text); }
.sg-n { font-size: 0.8rem; color: var(--text-3, var(--text-2));
  font-variant-numeric: tabular-nums; }
.sg-btn { font-size: 0.86rem; padding: 4px 12px; border-radius: 7px;
  border: 1px solid var(--border); color: var(--text-2); white-space: nowrap; }
.sg-x { font-size: 0.86rem; color: var(--text-3, var(--text-2)); opacity: 0.75; }
/* En étroit, le bouton passe SOUS le texte : à 375 px, une phrase de huit mots
   et un bouton de trois ne tiennent pas sur une ligne, et rétrécir le texte le
   rendrait illisible. Placements EXPLICITES, sans quoi la croix tomberait à la
   ligne avant le bouton (ordre du DOM). */
@media (max-width: 560px) {
  .sg-item { grid-template-columns: auto 1fr auto; align-items: start; }
  .sg-chev { grid-area: 1 / 1; margin-top: 3px; }
  .sg-body { grid-area: 1 / 2; }
  .sg-x { grid-area: 1 / 3; }
  .sg-btn { grid-area: 2 / 2 / 3 / 4; justify-self: start; margin-top: 7px; }
}

/* ══════ L’ICÔNE DE #qol (29/08) ════════════════════════════════════════════
   La section avait une VITRINE de quatre cartes ; elle est retirée (décision de
   Jonathan : ne citer aucun exemple), et trois de ses quatre redisaient une
   section de la page. Sans figure, la règle des icônes s’applique : elle en
   reçoit une, la troisième après #privacy et #keyboard-first.
   MÊME GRAMMAIRE QUE LES DEUX AUTRES : géométrie GÉNÉRÉE depuis l’unité
   répétée (carré de 13, gouttière de 4, d’où 149 × 81 — la hauteur du Mac et
   du clavier), un tracé fin et pâle pour l’ensemble, l’accent sur UN élément.
   Les teintes sont EXACTEMENT celles du clavier : pas une couleur de plus. */
.qol-grid { display: block; width: 149px; height: auto; margin: 6px auto 26px; }
.qol-grid .qg-cell { fill: none;
  stroke: color-mix(in srgb, var(--text-2) 34%, transparent); stroke-width: 1.3; }
.qol-grid .qg-one { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 1.4; }

/* ══════ LA FRISE DE #lifetime-pricing (29/08) ══════════════════════════════
   La section était la SEULE sans figure ; elle en reçoit une à la demande de
   Jonathan. Ce qu’elle montre est ce que la prose ne peut pas : le TEMPS.
   ON NE COMPTE PLUS LES PAIEMENTS (v2) : « 5 paiements » supposait un
   abonnement ANNUEL, et au mois ce serait 60 — une figure ne peut pas annoncer
   un total qui dépend d’une modalité qu’elle n’affiche pas. Une série qui se
   RÉPÈTE et s’estompe contre une pastille unique dit la même chose, et reste
   vraie des deux côtés.
   LE FONDU DIT LA SUITE, IL N’EFFACE PAS : décroissance jusqu’à 0,18, jamais
   zéro (leçon de #import, où un dégradé avait effacé la rangée qu’il annonçait).
   NI PRIX NI CONCURRENT NOMMÉ. Le prix est à un clic, sur « prix abordable ».
   BUDGET D’ACCENT : la pastille de Circut, et elle seule. */
.years { margin: 26px 0; }
.yr-grid { display: grid; align-items: center; gap: 11px 0;
  grid-template-columns: minmax(96px, auto) 1fr minmax(74px, auto); }
.yr-lbl { font-size: 0.9rem; color: var(--text-2); padding-right: 14px; }
/* L’échelle de temps : cinq repères réguliers, l’unité que les pastilles ne
   suivent PAS — c’est tout le sujet, un paiement ne tombe pas une fois l’an. */
.yr-scale { display: grid; grid-template-columns: repeat(5, 1fr); }
.yr-y { font-size: 0.76rem; color: var(--text-2); opacity: 0.7;
  font-variant-numeric: tabular-nums; }
.yr-line { display: flex; align-items: center; justify-content: space-between; }
.yr-p { width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: color-mix(in srgb, var(--text-2) 46%, transparent);
  opacity: var(--o, 1); }
/* La pastille de Circut est SEULE sur sa rangée : `justify-content` la
   collerait au bord, or elle doit tomber sur la première année. */
.yr-line .yr-on { width: 11px; height: 11px; background: var(--accent);
  margin-right: auto; margin-left: 0; }
.yr-sum { font-size: 0.86rem; color: var(--text-2); text-align: right;
  white-space: nowrap; padding-left: 14px; }
.yr-me { color: var(--text); font-weight: 600; }
@media (max-width: 520px) {
  .yr-grid { grid-template-columns: minmax(78px, auto) 1fr; gap: 9px 0; }
  .yr-y { font-size: 0.68rem; }
  .yr-sum { grid-column: 2 / -1; text-align: left; padding: 2px 0 0; }
}

/* ══════ LE FILET DE DÉBUT DE SECTION (29/08) ═══════════════════════════════
   MESURÉ avant d’être corrigé : entre deux FAMILLES, 155 px et un fond qui
   change ; entre deux SECTIONS d’une même famille, 0 px et rien du tout —
   leur `padding: 30px 0` fait tout le travail, soit 60 px de blanc nu. Le
   surtitre était censé marquer le début, mais il flotte au milieu de ce blanc.
   POURQUOI PAS « DE PART ET D’AUTRE DU SURTITRE » (l’idée de Jonathan) :
   le surtitre est ferré à GAUCHE dans une colonne centrée, et l’encadrer des
   deux côtés supposerait de le centrer — or « ce qui se lit se ferre à
   gauche » (doctrine du 11/08). Le filet prend donc toute la largeur de la
   COLONNE et se place AU-DESSUS : il ouvre la section au lieu de l’encadrer.
   LARGEUR DE COLONNE, PAS D’ÉCRAN : un trait pleine page découperait la
   landing en tranches. Celui-ci se lit comme un début de chapitre.
   PAS SUR LA PREMIÈRE SECTION D’UNE FAMILLE (`.arg + .arg`) : elle suit déjà
   le `fam-head`, qui marque bien plus fort. Deux signaux au même endroit se
   contrarient. Les sections SOLO hors famille (#share, #qol, #lifetime-pricing)
   n’en reçoivent pas non plus — elles sont déjà isolées par leur fond.
   NEUTRE, JAMAIS L’ACCENT : une frontière est un constat, pas un état en
   attente (budget d’accent).
   `:first-child` N’EST PAS UNE PRÉCAUTION : QUATRE sections poursuivent
   leur prose SOUS la figure (#palette, #create, #collections,
   #works-everywhere) et portent donc DEUX `prose-col` dans un seul
   `container`. Sans lui, le filet se posait deux fois — le second sous la
   vidéo ou sous le mur d’apps, là où il ne sépare rien (relevé par
   Jonathan, 29/08). Un sélecteur de position s’écrit pour la structure la
   plus COMPLEXE de la page, jamais pour la plus fréquente : 18 sections sur
   22 n’ont qu’une colonne, et ce sont les quatre autres qui décident. */
.fam .arg + .arg > .container > .prose-col:first-child::before {
  content: ""; display: block; height: 1px; margin: 0 0 34px;
  background: color-mix(in srgb, var(--text-2) 22%, transparent);
}

/* LE BANDEAU DES TÉMOIGNAGES (.voices) vit dans site.css depuis le 16/09/2026,
   avec le ruban (.packs-*) : /buy/ le partage. Reste ici ce qui tient à la
   landing, le lien du dessous. */
/* .voices .arg-cta vit dans site.css avec le bouton, depuis le 16/09/2026. */
