/* © 2026 BY ROBOTS — tous droits réservés. Voir LICENSE. */
/* ═════════════════════════════════════════════════════════════════════════
   LE LANGAGE VISUEL, EN UN SEUL EXEMPLAIRE — servi à L'ACCUEIL ET À LA PORTE
   ═════════════════════════════════════════════════════════════════════════
   Il a vécu dans le `<style>` d'index.html, quand un seul document portait
   les huit écrans. La page de CONNEXION (login.html) en est un neuvième, et
   c'est un AUTRE document : recopier la palette et les composants chez elle,
   c'était s'assurer qu'au premier réglage les deux dériveraient — la même
   faute qu'une liste de plans recopiée chez un lecteur. Le langage sort donc
   ici, et les deux pages le LIENT.

   CE QUI EST ICI ET CE QUI RESTE CHEZ LA PAGE : ici, ce qu'un composant EST
   (une enseigne peinte, une option choisie, une plaque émaillée, un rail) ;
   chez la page, où il se pose et quelle taille il fait. C'est pourquoi
   l'accueil garde ses `#startOverlay …` et la porte ses `#loginPage …` : ce
   sont des mises en page, pas du langage.

   ELLE EST PUBLIQUE, ET C'EST UNE DÉCISION. La page de connexion s'affiche
   AVANT toute session : tout ce qu'elle demande doit passer la porte du gate
   (`SANS_SESSION` dans gate/server.mjs), sinon elle répond 401 et l'écran
   casse en production seulement. Cette feuille y est nommée, et le test de
   fumée le vérifie des deux côtés. Elle ne contient donc RIEN qui parle du
   jeu — des couleurs, des cadres et des boutons.

   LES CHEMINS D'IMAGES SONT RELATIFS À CETTE FEUILLE (`../textures/ui/…`) :
   un `url()` se résout depuis le fichier CSS, jamais depuis le document. */


  /* la base d'un titre d'écran — l'ENSEIGNE peinte, plus bas, s'y ajoute */

  /* ═══════════════════════════════════════════════════════════════════════
     LA PALETTE ET LES CARACTÈRES — la racine du langage commun
     ═══════════════════════════════════════════════════════════════════════
     Huit écrans, un seul jeu de règles : tout ce que l'interface peint sort
     d'ici, et les écrans n'écrivent ensuite que ce qui leur est propre. Un
     écran repeint à la main pendant que les sept autres restent en l'état,
     c'est sept incohérences.

     LA PALETTE EST CELLE DE LA VILLE, PAS UNE PALETTE D'INTERFACE. Pine Creek
     est désaturée : un os crème, un charbon chaud, une poussière. Le ROUGE est
     le seul accent, et il ne sert qu'à deux choses — ce qui est CHOISI et ce
     qui est GRAVE. L'ambre ne sert qu'aux enseignes peintes. Trois couleurs
     d'état viennent du HUD de jeu et n'y sont pas réinventées : l'orangé du
     blessé, le rouge sourd de l'à-terre, le vert de la pastille « prêt ».

     TROIS FAMILLES, ET PAS UNE DE PLUS. Le TITRE est BANGERS — les enseignes
     peintes, les bandeaux de section, les grands boutons ; l'UI est une
     sans-serif système — les libellés, les valeurs, de la signalétique qui se
     lit d'un coup ; le TEXTE reste le Georgia d'origine — les paragraphes se
     lisent, ils ne se contemplent pas.

     AUCUNE POLICE N'EST CHARGÉE DEPUIS UN TIERS, ET C'EST PLUS QU'UNE QUESTION
     DE PANNE. Un tiers peut tomber ou être bloqué, soit ; mais surtout **la
     page de connexion s'affiche à des visiteurs qui n'ont encore rien
     accepté** — demander un fichier à `fonts.gstatic.com` depuis cet écran-là
     enverrait leur adresse IP à un tiers avant même qu'ils aient lu nos
     mentions légales, qui promettent « aucun cookie tiers ». Bangers est donc
     DANS le dépôt (`prototype/fonts/`, licence et crédits dans
     `fonts/CREDITS.md`), servie par le gate, et `/fonts/` passe sa porte sans
     session — sans quoi la porte perdrait sa police en production seulement.

     ET LA PILE DE REPLIS RESTE DERRIÈRE. `font-display: swap` plus les quatre
     condensées système à la suite de `--police-titre` : fichier absent ou lent,
     le texte s'affiche tout de suite dans une condensée du système, jamais dans
     le vide. Même discipline que les images, qui ont toutes un aplat dessous.
     Les accents français sont dans le sous-ensemble `latin` (U+0000–00FF) : un
     mot comme PRÊT ne se coupe pas en deux polices. */

  /* Bangers — Vernon Adams / The Bangers Project Authors, SIL Open Font
     License 1.1. Texte intégral : `fonts/OFL-Bangers.txt`. Deux sous-ensembles,
     et `unicode-range` fait que `latin-ext` ne se télécharge que s'il sert. */
  @font-face {
    font-family: 'Bangers';
    font-style: normal; font-weight: 400; font-display: swap;
    src: url(../fonts/bangers-latin.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
      U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
      U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face {
    font-family: 'Bangers';
    font-style: normal; font-weight: 400; font-display: swap;
    src: url(../fonts/bangers-latin-ext.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
      U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
      U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }

  /* LES ÉCRITURES QUE BANGERS N'A PAS. Il couvre le latin et le latin étendu —
     de l'anglais au tchèque, au roumain, au hongrois et aux langues
     nordiques —, et rien d'autre. Sans une pile à elles, le cyrillique, le
     grec, le chinois, le japonais et le coréen tomberaient dans le premier
     repli venu du système, chacun dans un autre (Impact porte le cyrillique ET
     le grec). `html:lang()` l'emporte sur `:root` par
     sa spécificité (la langue est posée sur <html> par i18n.js et landing.js),
     et Bangers reste EN TÊTE pour les chiffres et le latin mêlés au texte
     (F$ 220). Des polices du SYSTÈME, jamais un fichier : une police chinoise
     pèse des mégaoctets, et chaque système en a une bonne. */
  html:lang(ru), html:lang(uk), html:lang(el) {
    --police-titre: 'Bangers', 'Impact', 'Arial Black', 'Arial Narrow', sans-serif;
  }
  html:lang(zh-CN) {
    --police-titre: 'Bangers', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'Source Han Sans SC', sans-serif;
    --police-ui: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
    --police-texte: Georgia, 'Songti SC', 'SimSun', 'Noto Serif SC', serif;
  }
  html:lang(zh-TW) {
    --police-titre: 'Bangers', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans TC', 'Source Han Sans TC', sans-serif;
    --police-ui: 'Segoe UI', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans TC', sans-serif;
    --police-texte: Georgia, 'Songti TC', 'PMingLiU', 'Noto Serif TC', serif;
  }
  html:lang(ja) {
    --police-titre: 'Bangers', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic', 'Meiryo', 'Noto Sans JP', sans-serif;
    --police-ui: 'Segoe UI', 'Hiragino Sans', 'Yu Gothic UI', 'Meiryo', 'Noto Sans JP', sans-serif;
    --police-texte: Georgia, 'Hiragino Mincho ProN', 'Yu Mincho', 'Noto Serif JP', serif;
  }
  html:lang(ko) {
    --police-titre: 'Bangers', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif;
    --police-ui: 'Segoe UI', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif;
    --police-texte: Georgia, 'AppleMyungjo', 'Batang', 'Noto Serif KR', serif;
  }

  /* (suite du commentaire de tête)

     LES IMAGES SONT UNE MATIÈRE, JAMAIS UNE STRUCTURE. Chaque
     `url(../textures/ui/…)` est doublée d'un aplat ou d'un dégradé DESSOUS : le
     fichier absent, la page garde sa forme, ses couleurs et sa lisibilité —
     elle perd seulement le grain peint. C'est `node tools/images-ui.mjs` qui
     les produit, et `textures/CREDITS.md` qui dit d'où elles viennent. */
  :root {
    --nuit: #0b0a08;          /* le fond des sous-pages */
    --ardoise: #16130f;       /* un panneau */
    --panneau: #1d1913;       /* un panneau plus clair, posé sur l'ardoise */
    --creux: #100e0b;         /* le creux d'un champ, d'une case */
    --os: #efe3cd;            /* l'encre principale */
    --creme: #ddd0b6;         /* l'encre courante */
    --poussiere: #8f8577;     /* ce qui est secondaire */
    --sang: #a81f18;          /* CHOISI, ou GRAVE — et rien d'autre */
    --sang-vif: #d0392a;
    --sang-sourd: #5e1512;
    --braise: #e0a33c;        /* l'ambre des enseignes */
    --filet: rgba(239, 227, 205, .16);
    --filet-fort: rgba(239, 227, 205, .34);
    /* les trois états, repris du HUD de jeu — on ne réinvente pas une palette */
    --blesse: #d8a48a;
    --aterre: #c96a5a;
    --vert: #7ec97e;
    /* BANGERS EN TÊTE, les condensées système derrière : c'est la pile de
       replis, et elle ne se retire pas (cf. le commentaire de tête). */
    --police-titre: 'Bangers', 'Anton', 'Haettenschweiler', 'Impact', 'Arial Narrow', sans-serif;
    --police-ui: 'Segoe UI', ui-sans-serif, Roboto, Helvetica, Arial, sans-serif;
    --police-texte: Georgia, 'Times New Roman', serif;
    /* la matière : deux fichiers, deux rôles. Le GRAIN se répète sur les
       panneaux et le papier ; le BANDEAU est le coup de pinceau des titres. */
    --grain: url(../textures/ui/grain.webp);
    --bandeau: url(../textures/ui/bandeau.webp);
    --plaque: url(../textures/ui/plaque.webp);
    /* LES QUATRE CADRES PEINTS, découpés d'une planche fournie par le
       propriétaire (`tools/images-ui.mjs`, table `IMAGES`). Ce sont des
       CADRES et non des aplats : ils se posent en `border-image`, jamais en
       `background-size: 100% 100%` — cf. LE CADRE PEINT, plus bas. */
    --cadre-champ: url(../textures/ui/input-fond.webp);
    --cadre-geste: url(../textures/ui/bouton-fond.webp);
    --cadre-option: url(../textures/ui/langue-fond.webp);
    --cadre-option-choisie: url(../textures/ui/langue-fond-actif.webp);
    /* LES DEUX CADRES DE L'ACCUEIL, fournis à part : le rouge AUX MAINS de PRÊT,
       et le sombre à liseré crème des cinq rails. Mêmes règles, même mécanisme. */
    --cadre-pret: url(../textures/ui/pret-fond.webp);
    --cadre-rail: url(../textures/ui/rail-fond.webp);
    /* la PLANCHE émaillée à bouts rouges du bandeau de pied de l'accueil : une
       matière de plus, même mécanisme — un 9-slice, jamais un étirement */
    --cadre-pied: url(../textures/ui/pied-fond.webp);
    --icone-chevron: url(../textures/ui/icone-chevron.webp);
    /* LES DEUX PLAQUES À GLYPHE. Elles ne sont pas des cadres : chacune sert
       UN bouton, et son dessin est peint dedans (cf. `.plaque-glyphe`). */
    --plaque-boutique: url(../textures/ui/boutique-fond.webp);
    --plaque-refaire: url(../textures/ui/reglages-refaire.webp);
    /* LES DEUX PLANCHES DE L'ARGENT, à l'accueil. Même famille et pour la même
       raison : leur ORNEMENT est peint HORS du cadre — la liasse du magot, le
       sac et les balles de la boutique — et c'est ce qui interdit le 9-slice.
       Le nom dit l'écran : `--plaque-boutique` est déjà la plaque du RETOUR de
       la boutique, celle-ci est le bouton qui y mène. */
    --plaque-magot: url(../textures/ui/magot-fond.webp);
    --plaque-boutique-accueil: url(../textures/ui/boutique-bouton.webp);
  }

  /* ═══════════════════════════════════════════════════════════════════════
     LE CADRE PEINT — quatre sprites, quatre rôles, et un seul mécanisme
     ═══════════════════════════════════════════════════════════════════════
     Le propriétaire a peint quatre fonds : un CHAMP, un GESTE PRINCIPAL, une
     OPTION au repos, une OPTION CHOISIE. Chacun a un liseré irrégulier, des
     coins usés et du grain.

     ON NE LES ÉTIRE PAS, ON LES DÉCOUPE EN NEUF. `background-size: 100% 100%`
     déforme la bordure avec la boîte : sur un bouton large et bas, le liseré
     devient épais en haut et fin sur les côtés, et les coins s'écrasent. Le
     9-slice (`border-image-slice`) garde les quatre COINS à leur taille, étire
     les quatre CÔTÉS le long de l'arête, et `fill` remplit le centre. C'est
     exactement ce à quoi sert cette propriété, et c'est du CSS pur.

     LES DÉCOUPES SONT MESURÉES SUR LE FICHIER FINAL, pas sur la planche : ce
     que `border-image-slice` compte, ce sont les pixels de l'image SERVIE, et
     l'import la redimensionne. Liseré relevé sur sept médianes (un liseré
     peint est irrégulier — la médiane, jamais le maximum, qui tombe sur une
     bavure) : champ 6–9 px, geste 9–11, option 8–10. Les valeurs ci-dessous
     ajoutent deux pixels pour que le coin garde son usure.

     `stretch` ET PAS `repeat` : une arête peinte à la main n'a pas de motif
     qui reboucle, un carrelage y ferait des coutures visibles tous les N
     pixels. Étirée, elle ne montre rien — c'est de la matière, pas un dessin.

     ET LE REPLI EST LA BORDURE CSS D'ORIGINE. `border-image` n'a pas d'aplat
     de secours : source absente, c'est le `border-style`/`border-color` de
     l'élément qui reprend, et le `background` qui remplit. On ne les efface
     donc JAMAIS en posant un cadre — on ne fait qu'épaissir le trait.

     LE RACCOURCI `border:` REMET `border-image` À ZÉRO, ET C'EST SILENCIEUX.
     C'est la règle à ne pas redécouvrir : `border: 3px solid #2a0f0b` n'écrit
     pas trois propriétés mais QUATRE — la quatrième est `border-image: initial`,
     et elle efface le cadre posé par un sélecteur moins spécifique sans qu'un
     seul message n'apparaisse nulle part. Les quatre composants qui portent un
     cadre écrivent donc `border-width` / `border-style` / `border-color` en
     TROIS déclarations. Symptôme, si l'on oublie : le bouton garde exactement
     son ancien aspect, comme si l'image n'existait pas. */
  .peint {
    border-style: solid;
    border-image-slice: var(--cadre-decoupe) fill;
    border-image-width: var(--cadre-largeur, 1);
    border-image-repeat: stretch;
    border-image-source: var(--cadre);
  }

  /* la base d’un titre d’écran — l’ENSEIGNE peinte, plus bas, s’y ajoute */
  .overlay h1 { font-size: 46px; letter-spacing: 10px; margin: 0 0 6px; font-weight: normal; font-style: italic; }

  /* ═══════════════════════════════════════════════════════════════════════
     LE LANGAGE COMMUN — sept composants, et tous les écrans les partagent
     ═══════════════════════════════════════════════════════════════════════
     Ils sont posés sur les classes GÉNÉRIQUES du document (`h1`, `h2`,
     `.times`, `.lbl`, `.note`, `.actions`) plutôt que sur des classes neuves :
     c'est ce qui habille les huit écrans d'un coup, et ce qui fait qu'un écran
     ajouté demain est habillé sans qu'on y pense.

       .panneau     le cadre sombre, grain et filet — le fond de tout
       h1           l'ENSEIGNE peinte : condensée crème sur coup de pinceau
       h2           le BANDEAU de section : même pinceau, plus bas, avec icône
       .rail        le grand bouton à icône et chevron
       .times button l'OPTION : sombre, et ROUGE quand elle est choisie
       .lbl         l'ÉTIQUETTE de scotch, collée devant une rangée d'options
       .rail        le grand bouton à icône et chevron
       .enseigne    la plaque émaillée du geste principal (PRÊT, GARDER)

     LES ICÔNES SONT DES MASQUES, PAS DES IMAGES. Un SVG de deux cents octets
     en `data:`, posé en `mask`, peint par `currentColor` : l'icône prend donc
     la couleur de son bouton — crème au repos, rouge quand il est choisi,
     sombre sur un panneau clair — sans qu'on en dessine trois versions. Aucune
     requête, aucun appel de dessin, rien à charger. */
  /* le glyphe et le mot font UN bouton : `inline-flex` + `gap`, pas une marge
     — une marge se dédouble dans un conteneur qui a déjà son propre `gap` */
  .ico { display: inline-flex; align-items: center; gap: .72em; }
  .ico::before {
    content: ''; display: inline-block; flex: none;
    width: 1.15em; height: 1.15em; vertical-align: -.22em;
    background: currentColor;
    -webkit-mask: var(--ico) center / contain no-repeat;
    mask: var(--ico) center / contain no-repeat;
  }
  /* la réserve d'icônes : un trait de 1,6, jamais de remplissage — c'est le
     même dessin que les glyphes du HUD (cf. `#bodyHud svg`) */
  .i-personne { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='7.5' r='3.6'/%3E%3Cpath d='M4.8 20.5c0-4 3.2-6.6 7.2-6.6s7.2 2.6 7.2 6.6'/%3E%3C/svg%3E"); }
  .i-groupe { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='9' cy='8' r='3'/%3E%3Ccircle cx='17' cy='9' r='2.3'/%3E%3Cpath d='M3 19c0-3.2 2.7-5.2 6-5.2s6 2 6 5.2'/%3E%3Cpath d='M16 14.2c2.9 0 5 1.8 5 4.8'/%3E%3C/svg%3E"); }
  .i-epingle { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6'%3E%3Cpath d='M12 21.5s6.5-7 6.5-11a6.5 6.5 0 0 0-13 0c0 4 6.5 11 6.5 11z'/%3E%3Ccircle cx='12' cy='10.2' r='2.4'/%3E%3C/svg%3E"); }
  /* LE HASARD : deux chemins qui se croisent, l'idiome universel du « au
     hasard ». Il est dans la réserve et non chez LA CARTE parce qu'une icône
     est du LANGAGE, et que la réserve est un endroit, pas deux. */
  .i-hasard { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.8 6.6h3c1.2 0 2.3.6 3 1.6l4.4 6.6c.7 1 1.8 1.6 3 1.6h2.6'/%3E%3Cpath d='M2.8 17.4h3c1.2 0 2.3-.6 3-1.6l1.1-1.7'/%3E%3Cpath d='m13.3 9.6 1.1-1.7c.7-1 1.8-1.6 3-1.6h2.6'/%3E%3Cpath d='m18.1 4.2 2.6 2.4-2.6 2.4M18.1 14.2l2.6 2.4-2.6 2.4'/%3E%3C/svg%3E"); }
  /* LES HUIT GLYPHES DE RANGÉE, dessinés d'après la maquette de LA CARTE. Ils
     sont ici et non chez elle pour la raison écrite juste au-dessus — une icône
     est du LANGAGE. Et ce sont des MASQUES et non des images, parce que la
     maquette n'existe qu'en 1 122 px de large : le dessin d'une rangée y fait
     45 px, quand une icône peinte servie à 2× en demande 88 (cf. les six de
     l'accueil dans `tools/images-ui.mjs`). Une silhouette au trait se dessine,
     elle prend la couleur de son libellé, et elle ne pèse rien. */
  .i-chrono { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='13.8' r='7.6'/%3E%3Cpath d='M12 13.8V9.8M9.6 2.8h4.8M12 2.8v3.4M18.7 7.3l1.7-1.7'/%3E%3C/svg%3E"); }
  .i-nuage { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M7 18.5h10.2a3.8 3.8 0 0 0 .3-7.6 5.6 5.6 0 0 0-10.8-1A3.8 3.8 0 0 0 7 18.5z'/%3E%3C/svg%3E"); }
  .i-pluie { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 14.5h10.2a3.8 3.8 0 0 0 .3-7.6 5.6 5.6 0 0 0-10.8-1A3.8 3.8 0 0 0 7 14.5z'/%3E%3Cpath d='M8.4 17.4 7.4 20.2M12 17.4l-1 2.8M15.6 17.4l-1 2.8'/%3E%3C/svg%3E"); }
  .i-eclair { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M13.6 2.4 5.4 13.6h5.2l-1.2 8 8.2-11.4h-5.2z'/%3E%3C/svg%3E"); }
  .i-tornade { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M3.6 5.4h16.8M5.6 9h12.8M7.8 12.6h8.4M10 16.2h4.6M11.6 19.8h1.4'/%3E%3C/svg%3E"); }
  .i-crane { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M12 2.8c-4.4 0-7.4 3.1-7.4 7.3 0 2.5 1.1 4 2.5 5v2.3c0 .9.7 1.6 1.6 1.6h6.6c.9 0 1.6-.7 1.6-1.6v-2.3c1.4-1 2.5-2.5 2.5-5 0-4.2-3-7.3-7.4-7.3z'/%3E%3Ccircle cx='9.2' cy='11' r='1.7'/%3E%3Ccircle cx='14.8' cy='11' r='1.7'/%3E%3Cpath d='M10.6 15.4h2.8'/%3E%3C/svg%3E"); }
  .i-cartouche { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M9.4 21.2h-4V10.8l2-3.2 2 3.2zM18.6 21.2h-4V10.8l2-3.2 2 3.2z'/%3E%3Cpath d='M5.4 16.6h4M14.6 16.6h4'/%3E%3C/svg%3E"); }
  .i-neige { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M12 2.8v18.4M4 7.4l16 9.2M20 7.4 4 16.6'/%3E%3Cpath d='M9.8 5 12 6.9 14.2 5M9.8 19 12 17.1l2.2 1.9'/%3E%3C/svg%3E"); }
  /* le billet de l'ARGENT PARTAGÉ : une rangée de LA CARTE venue après la
     maquette, dessinée du même trait */
  .i-billet { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Crect x='2.8' y='6.4' width='18.4' height='11.2' rx='1.4'/%3E%3Ccircle cx='12' cy='12' r='2.6'/%3E%3Cpath d='M6.2 9.6v4.8M17.8 9.6v4.8'/%3E%3C/svg%3E"); }
  .i-globe { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M3.5 12h17M12 3.5c2.4 2.4 3.6 5.3 3.6 8.5S14.4 18.1 12 20.5c-2.4-2.4-3.6-5.3-3.6-8.5S9.6 5.9 12 3.5z'/%3E%3C/svg%3E"); }
  .i-son { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M4.5 9.5h3.2L12 5.8v12.4l-4.3-3.7H4.5z'/%3E%3Cpath d='M15.4 9.4a3.6 3.6 0 0 1 0 5.2M17.9 7a7 7 0 0 1 0 10'/%3E%3C/svg%3E"); }
  .i-image { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='14' rx='1.6'/%3E%3Ccircle cx='9' cy='10' r='1.6'/%3E%3Cpath d='M4 17l4.6-4.4 3.2 3 3-2.6L20 17'/%3E%3C/svg%3E"); }
  .i-souris { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6'%3E%3Crect x='7' y='2.8' width='10' height='18.4' rx='5'/%3E%3Cpath d='M12 6.5v3.4'/%3E%3C/svg%3E"); }
  .i-clavier { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Crect x='2.5' y='6' width='19' height='12' rx='1.6'/%3E%3Cpath d='M6 9.5h.01M9.5 9.5h.01M13 9.5h.01M16.5 9.5h.01M6 12.8h.01M9.5 12.8h.01M13 12.8h.01M16.5 12.8h.01M8 15.8h8'/%3E%3C/svg%3E"); }
  /* LES DEUX AUTRES FAÇONS DE JOUER. Elles sont dans la réserve à côté du
     clavier et de la souris parce qu'elles disent la même chose qu'eux : par
     quoi je joue. La MANETTE est sa silhouette (deux poignées, deux manches,
     la croix) ; l'ÉCRAN TACTILE est un DOIGT sur une dalle — pas un téléphone,
     qui se serait confondu avec l'écran de `.i-ecran`. */
  .i-manette { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M8.4 7.5h7.2a4.4 4.4 0 0 1 4.3 3.4l1.2 5.2c.4 1.7-1.7 2.8-2.9 1.5l-2.3-2.5H8.1l-2.3 2.5c-1.2 1.3-3.3.2-2.9-1.5l1.2-5.2a4.4 4.4 0 0 1 4.3-3.4z'/%3E%3Cpath d='M7.6 10.6v2.6M6.3 11.9h2.6' stroke-linecap='round'/%3E%3Ccircle cx='16.2' cy='11.4' r='.9' fill='%23fff' stroke='none'/%3E%3Ccircle cx='18' cy='13.2' r='.9' fill='%23fff' stroke='none'/%3E%3C/svg%3E"); }
  /* LES AVIS : la bulle d'un retour, l'insecte d'un bug, et les QUATRE VISAGES
     de la note de fin de partie — du pire au meilleur, au même trait que le
     reste de la réserve. Des masques et pas des emojis : un emoji porte la
     couleur et le dessin du système, un masque prend l'encre de sa plaque
     (crème au repos, rouge choisi). */
  .i-bulle { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M4 4.8h16c.8 0 1.5.7 1.5 1.5v9c0 .8-.7 1.5-1.5 1.5h-8.6l-4.6 3.6v-3.6H4c-.8 0-1.5-.7-1.5-1.5v-9c0-.8.7-1.5 1.5-1.5z'/%3E%3Cpath d='M7 9.4h10M7 12.6h6.4'/%3E%3C/svg%3E"); }
  .i-insecte { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cellipse cx='12' cy='14' rx='4.4' ry='5.8'/%3E%3Cpath d='M9.4 8.6a2.6 2.6 0 0 1 5.2 0M12 9.2v10.4M7.6 11.8H4M7.6 15.4H4M16.4 11.8H20M16.4 15.4H20M8.6 18.6l-2.4 2M15.4 18.6l2.4 2M10.4 6.4 8.8 4.2M13.6 6.4l1.6-2.2'/%3E%3C/svg%3E"); }
  .i-note-1 { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.2 17c1-1.5 2.3-2.2 3.8-2.2s2.8.7 3.8 2.2M7.8 8.6l2.6 1.2M16.2 8.6l-2.6 1.2'/%3E%3C/svg%3E"); }
  .i-note-2 { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.4 15.6h7.2M9.2 9.6v.8M14.8 9.6v.8'/%3E%3C/svg%3E"); }
  .i-note-3 { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.2 13.8c1 1.6 2.3 2.4 3.8 2.4s2.8-.8 3.8-2.4M9.2 9.6v.8M14.8 9.6v.8'/%3E%3C/svg%3E"); }
  .i-note-4 { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M7.4 12.8h9.2c-.4 2.9-2.2 4.6-4.6 4.6s-4.2-1.7-4.6-4.6zM7.9 9.8c.6-1 2-1 2.6 0M13.5 9.8c.6-1 2-1 2.6 0'/%3E%3C/svg%3E"); }
  .i-doigt { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='4.5' y='2.5' width='15' height='19' rx='2.6'/%3E%3Cpath d='M9.6 12.2V7.4a1.6 1.6 0 0 1 3.2 0v6.1'/%3E%3Cpath d='M12.8 11.6a1.5 1.5 0 0 1 3 0v3.4a4.3 4.3 0 0 1-4.3 4.3h-.6a3.6 3.6 0 0 1-2.9-1.5l-1.6-2.3 1.1-1 1.1.9'/%3E%3C/svg%3E"); }
  .i-panier { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.8 4h2.6l2.4 10.4h9.1l2.1-7.4H6.6'/%3E%3Ccircle cx='9.4' cy='19' r='1.5'/%3E%3Ccircle cx='16.6' cy='19' r='1.5'/%3E%3C/svg%3E"); }
  .i-alerte { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.4 21.4 20H2.6z'/%3E%3Cpath d='M12 9.6v4.6M12 17.2h.01'/%3E%3C/svg%3E"); }
  .i-visage { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Crect x='5.5' y='3.5' width='13' height='17' rx='4.5'/%3E%3Cpath d='M9.5 10.5h.01M14.5 10.5h.01M9.8 15.5c1.4 1 3 1 4.4 0'/%3E%3C/svg%3E"); }
  .i-tenue { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M9 3.5 4 6.2l1.6 4L8 9.4v11h8v-11l2.4.8 1.6-4L15 3.5a3 3 0 0 1-6 0z'/%3E%3C/svg%3E"); }
  .i-sac, .i-dos { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M5.5 20.5v-8.5a4.5 4.5 0 0 1 4.5-4.5h4a4.5 4.5 0 0 1 4.5 4.5v8.5z'/%3E%3Cpath d='M9.5 7.5V6.2a2.5 2.5 0 0 1 5 0v1.3'/%3E%3Cpath d='M8.5 20.5V16h7v4.5'/%3E%3C/svg%3E"); }
  .i-tete { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M7.5 14.5V8a4.5 4.5 0 0 1 9 0v6.5'/%3E%3Cpath d='M7.5 12.2C4.3 12.9 2 14.2 2 15.7 2 17.5 6.5 19 12 19s10-1.5 10-3.3c0-1.5-2.3-2.8-5.5-3.5'/%3E%3C/svg%3E"); }
  .i-ceinture { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M2 9.5h20v5H2z'/%3E%3Crect x='9' y='7.6' width='6' height='8.8' rx='1.2'/%3E%3Cpath d='M12 9.9v4.2'/%3E%3C/svg%3E"); }
  /* l'ÉPAULE : un buste, et ce qu'on y pose (le chat, le corbeau, la main) */
  .i-epaule { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='10' cy='7' r='3.1'/%3E%3Cpath d='M3.6 20.5c0-3.6 2.9-5.9 6.4-5.9 1.3 0 2.5.3 3.5.9'/%3E%3Ccircle cx='17.6' cy='14.4' r='2.6'/%3E%3C/svg%3E"); }
  /* les quatre mesures de la barre de pied de l'accueil */
  .i-pin { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Cpath d='M12 2.5 6.5 11h3.2L5.4 18h13.2l-4.3-7h3.2z'/%3E%3Cpath d='M12 18v3.5'/%3E%3C/svg%3E"); }
  .i-soleil { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='4.1'/%3E%3Cpath d='M12 2.4v2.6M12 19v2.6M2.4 12h2.6M19 12h2.6M5.2 5.2l1.9 1.9M16.9 16.9l1.9 1.9M18.8 5.2l-1.9 1.9M7.1 16.9l-1.9 1.9'/%3E%3C/svg%3E"); }
  .i-horloge { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 6.6V12l3.6 2.2'/%3E%3C/svg%3E"); }
  .i-ecran { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Crect x='2.8' y='4' width='18.4' height='12.4' rx='1.6'/%3E%3Cpath d='M8.6 20h6.8M12 16.4V20'/%3E%3C/svg%3E"); }

  /* --- LE PANNEAU. Le fond de tout : une ardoise, un filet chaud, et le grain
         de papier posé par-dessus en très faible opacité. Les coins sont à
         peine cassés (3 px) — une interface de survie n'a pas de coins ronds,
         mais un angle parfaitement vif se lit comme une fenêtre système. */
  .panneau {
    position: relative; box-sizing: border-box;
    background: var(--ardoise);
    border: 1px solid var(--filet-fort); border-radius: 3px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(239, 227, 205, .06);
  }
  /* le grain : il ne prend aucun clic et ne s'imprime que par-dessus le fond,
     jamais par-dessus le contenu (`z-index: 0` et le contenu au-dessus) */
  .panneau::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    border-radius: inherit; opacity: .09; mix-blend-mode: overlay;
    background: var(--grain) repeat; background-size: 256px 256px;
  }
  .panneau > * { position: relative; }

  /* --- L'ENSEIGNE PEINTE. Le titre d'un écran est PEINT : une condensée
         crème, légèrement penchée, sur un coup de pinceau rouge. Le pinceau
         est une image ; le `linear-gradient` dessous est ce qui reste si elle
         manque — la forme et la couleur tiennent sans elle. */
  .overlay h1 {
    font-family: var(--police-titre); font-style: normal; font-weight: 400;
    text-transform: uppercase; color: var(--os);
    text-shadow: 0 2px 0 rgba(0, 0, 0, .45), 0 0 18px rgba(0, 0, 0, .5);
  }
  .overlay.subpage h1 {
    position: relative;
    display: inline-block; font-size: 42px; letter-spacing: .045em;
    margin: 4px 0 22px; padding: .16em .62em .2em;
    transform: skewX(-5deg);
  }
  /* LE PINCEAU SE POSE EN MASQUE, PAS EN FOND — et c'est un correctif, pas un
     goût. Il était peint en FOND, par-dessus un dégradé rouge de repli ; or
     l'encre de `bandeau.webp` n'occupe que **50 % de la hauteur** du fichier et
     84 % de sa largeur (mesuré sur le canal alpha). Le dégradé se voyait donc
     tout autour, en bandes franches au-dessus et en dessous du coup de brosse :
     « le bouton THE MAP a un problème de fond rouge » — c'était un RECTANGLE
     rouge, et le repli qui devait sauver la forme était ce qui la détruisait.
     Le fondu à 5 % des bouts ne pouvait rien y faire, il ne fond que
     horizontalement.
     On ne garde donc du fichier que sa FORME, et la couleur vient du dégradé —
     l'idiome est déjà celui du trait rouge des catégories de MON PERSONNAGE
     (`#customOverlay .cat.selected::after`, index.html).
     CONTREPARTIE CONNUE ET ASSUMÉE : un masque n'a pas d'aplat de repli. Image
     absente, il ne reste que le texte crème sur le panneau — lisible, sans
     enseigne. C'est le bon sens de l'échange : un repli qui abîme le cas normal
     pour protéger un cas qui n'arrive pas coûte plus qu'il ne rapporte.
     LE PSEUDO-ÉLÉMENT ET NON L'ÉLÉMENT : un masque posé sur le `h1` découperait
     aussi ses LETTRES là où la brosse s'effile. */
  .overlay.subpage h1::before {
    content: ''; position: absolute; z-index: -1; inset: 0;
    background: linear-gradient(92deg, var(--sang) 0, #8d1712 100%);
    -webkit-mask: var(--bandeau) center / 116% 205% no-repeat;
    mask: var(--bandeau) center / 116% 205% no-repeat;
  }
  .overlay.subpage h1 > * { display: inline-block; transform: skewX(5deg); }

  /* --- LE BANDEAU DE SECTION. Le même pinceau, plus bas, et une icône
         devant : c'est ce qui distingue COMPTE de LANGUE d'un coup d'œil quand
         cinq blocs se suivent. Il court sur toute la largeur de son bloc — un
         titre centré au milieu d'un panneau ne dit pas où le bloc commence. */
  .overlay h2 {
    display: flex; align-items: center; gap: 11px;
    font-family: var(--police-titre); font-weight: 400; text-transform: uppercase;
    font-size: 21px; letter-spacing: .07em; color: var(--os);
    margin: 0; padding: 9px 16px;
    background:
      var(--bandeau) center / 104% 215% no-repeat,
      linear-gradient(90deg, var(--sang) 0, #8d1712 100%);
    text-shadow: 0 1px 0 rgba(0, 0, 0, .4);
  }
  .overlay h2 .ico::before { width: 1.05em; height: 1.05em; }

  /* (LA PUCE — le cartouche « PINE CREEK | #ABC123 | HOUR = 12 » — a vécu ici.
     L'accueil ne la porte plus : ces valeurs sont passées dans la BARRE DE
     PIED, chacune sous sa légende, où elles se lisent au lieu de se subir. Ce
     qui ne sert plus à rien ne reste pas en réserve « au cas où ».) */

  /* --- LA RANGÉE. Ce qui ALIGNE les options, et pas seulement ce à quoi elles
         ressemblent : un composant se partage ENTIER. Elle a vécu chez
         l'accueil pendant que ses boutons vivaient ici, et la PORTE — l'autre
         document — recevait donc des options bien peintes empilées les unes
         sous les autres. Le symptôme se lit toujours pareil : un élément qui a
         la bonne apparence à la mauvaise place.
         `flex-wrap: wrap` est le défaut parce qu'une rangée des RÉGLAGES peut
         aligner douze options ; un écran qui n'en veut aucun le dit chez lui
         (cf. `#loginPage .langues`). */
  .overlay .times { margin-top: 26px; display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; align-items: center; }
  .overlay .times + .times { margin-top: 10px; }

  /* --- L'OPTION. Une rangée de choix (l'heure, la pluie, la taille du
         groupe) : sombre au repos, ROUGE quand elle est choisie — cerclée et
         éclairée de l'intérieur, comme un bouton d'appareil qui s'allume.
         C'est le seul endroit de l'interface où le rouge veut dire « ceci »
         et non « attention ». */
  /* `:not(.enseigne)` : la plaque émaillée est un bouton d'option comme les
     autres pour le document, et ce sélecteur-ci l'emporterait sur elle (deux
     classes plus un élément contre une classe). On l'écarte ici plutôt que de
     surenchérir là-bas — sinon chaque nouvel écran devrait deviner de quelle
     spécificité s'armer pour poser une plaque. */
  /* IL EST POSÉ, PAS DÉCOUPÉ. Le liseré était à `--filet` (crème à 16 %) et le
     coin à 3 px : sur le noir de la ville, un bouton d'option se lisait comme
     un trou dans l'écran. Le liseré passe à `--filet-fort`, le coin à 5 px, et
     une ombre COURTE ET BASSE (2 px de descente, 4 de flou) le pose sur le
     panneau. Ce n'est pas un halo : une ombre centrée ferait briller le bouton,
     alors qu'on veut seulement qu'il ait une épaisseur. */
  .overlay .times button:not(.enseigne) {
    background: var(--creux);
    border-width: 1px; border-style: solid; border-color: var(--filet-fort);
    border-radius: 5px; color: var(--creme);
    font-family: var(--police-ui); font-size: 11px; letter-spacing: .16em;
    text-transform: uppercase; padding: 9px 15px; min-height: 34px;
    cursor: pointer; opacity: .82;
    box-shadow: 0 2px 4px rgba(0, 0, 0, .55);
    transition: opacity .18s, border-color .18s, color .18s, background-color .18s;
  }
  .overlay .times button:hover { opacity: 1; border-color: var(--os); }
  /* LE CHOISI RESTE PLUS FORT QUE LE REPOS : le liseré rouge vif l'emporte sur
     le crème, et l'ombre portée basse reste sous l'éclairage intérieur — les
     deux se cumulent, ils ne se remplacent pas. */
  .overlay .times button.selected {
    opacity: 1; color: #ffd9d2; border-color: var(--sang-vif);
    background: linear-gradient(180deg, rgba(168, 31, 24, .30), rgba(168, 31, 24, .12));
    box-shadow: inset 0 0 12px rgba(208, 57, 42, .35), 0 0 0 1px rgba(208, 57, 42, .18),
      0 2px 4px rgba(0, 0, 0, .55);
  }

  /* --- L'ÉTIQUETTE DE SCOTCH. Le libellé d'une rangée d'options est un bout
         de ruban beige collé de travers : il n'a pas la même NATURE que les
         options qu'il annonce, et une couleur de plus l'aurait dit moins
         bien. La largeur est fixe — sinon la colonne d'options danse d'une
         langue à l'autre (HEURE / TIME). */
  .overlay .times .lbl {
    display: inline-block; box-sizing: border-box;
    width: 116px; flex: none; margin-right: 6px; padding: 7px 10px;
    background: linear-gradient(184deg, #d9cba8, #c3b492);
    color: #241d13; font-family: var(--police-ui); font-weight: 600;
    font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
    text-align: center; opacity: .92; transform: rotate(-.7deg);
    box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
  }

  /* --- LE GRAND BOUTON À CHEVRON. Une ligne pleine largeur : l'icône dit de
         quoi il s'agit, le mot le dit, et le chevron dit qu'on s'en va
         ailleurs. Le chevron est un `::after` — le texte du bouton reste
         intact, donc l'attribut de traduction et le mode édition ne voient
         rien passer. (Ce commentaire n'écrit pas le nom de cet attribut :
         `tools/langues.mjs` le cherche à la regex dans TOUT le document,
         feuille de style comprise, et prendrait la suite du CSS pour une
         phrase à traduire.) */
  .rail {
    display: flex; align-items: center; gap: 14px;
    /* LA MÊME LARGEUR QUE LA PLAQUE, et c'est ce qui fait la colonne : les
       trois rangées de l'accueil (personnage, lien, PRÊT) s'alignent, et le
       carton d'erreur avec elles. À `100%` le bouton prenait la largeur de son
       contenu — `.times` est une rangée flex qui n'impose rien. */
    width: min(74vw, 470px); box-sizing: border-box; padding: 13px 18px;
    background: linear-gradient(180deg, rgba(29, 25, 19, .96), rgba(16, 14, 11, .96));
    /* TROIS DÉCLARATIONS ET PAS LE RACCOURCI : `border:` écrirait aussi
       `border-image: initial`, et comme cette règle vient APRÈS `.peint` à
       spécificité égale, elle effaçait le cadre du rail peint en silence —
       il restait une boîte grise de 13 px (cf. LE CADRE PEINT, plus haut) */
    border-width: 1px; border-style: solid; border-color: var(--filet);
    border-radius: 3px; color: var(--creme);
    font-family: var(--police-ui); font-size: 12.5px; letter-spacing: .2em;
    text-transform: uppercase; text-align: left; cursor: pointer; opacity: .92;
    transition: border-color .18s, color .18s, opacity .18s;
  }
  .rail::after {
    content: '\203A'; margin-left: auto; font-size: 21px; line-height: 1;
    opacity: .55; transition: opacity .18s, transform .18s;
  }
  .rail:hover { opacity: 1; color: var(--os); border-color: var(--filet-fort); }
  .rail:hover::after { opacity: 1; transform: translateX(3px); }
  /* LE RAIL PEINT : le cadre sombre à liseré crème fourni par le propriétaire
     (`rail-fond.webp`, 1024 px de large). Le liseré fait 10 px sur le fichier
     servi, mesuré, et les coins sont ÉCHANCRÉS sur une vingtaine de pixels :
     la tranche prend les deux, et elle est UNIFORME (44 partout) pour que
     l'échancrure garde son angle — un coin étiré dans un sens se lit comme un
     défaut de rendu. Le bord de 13 px la réduit d'un tiers : liseré à 3 px,
     échancrure à 6, ce que la maquette montre. Ce bord n'est pas un trait
     épais : la tranche contient le fond sombre derrière le liseré, donc le
     bord SE LIT comme l'intérieur du bouton — et c'est pour ça que la marge
     intérieure descend d'autant, la boîte ne grossit pas.
     L'icône et le chevron restent des MASQUES peints par `currentColor` : le
     fond passe derrière eux, il ne les remplace pas. Rien ne reste derrière
     le cadre (cf. la plaque de sang) ; le survol, qui n'a plus de bordure à
     éclaircir, éclaircit la matière. */
  .rail.peint {
    --cadre: var(--cadre-rail);
    --cadre-decoupe: 44;
    /* 10 px de bord (liseré ≈ 3 px + échancrure ≈ 6 sur le fichier servi),
       pas 13 : avec la colonne réduite (« toute la colonne devrait être plus
       petite »), le rail doit garder son RAPPORT — 4,65:1 sur la maquette. La
       hauteur sort du composant : 10 + 2 + 34 + 2 + 10 = 58 px pour 265 de
       large, 4,57:1. */
    border-width: 10px; padding: 2px 4px 2px 5px; gap: 8px;
    border-radius: 0; background: none;
    /* EN GRAS, À GAUCHE DE SON ICÔNE. La coupe en deux lignes n'est PAS ici :
       elle est une règle du bouton qui la porte (`.deux`, plus bas). La
       TAILLE du texte est à la page. */
    font-weight: 700;
    transition: filter .18s, color .18s, opacity .18s;
  }
  /* L'ICÔNE EST LA SIENNE — un dessin peint, fourni (`icone-*.webp`), et non
     un masque : elle a sa matière et sa couleur, elle ne se teinte pas avec le
     bouton. C'est un pseudo-élément et pas un `<img>` dans le bouton, pour que
     le texte du bouton reste UN NŒUD TEXTE : c'est lui que le mode édition
     cliquera et que la traduction réécrira. 40 px de case, `contain` — les
     dessins n'ont pas tous le même rapport —, et c'est cette case qui donne
     au rail sa hauteur : 10 + 2 + 40 + 2 + 10 = 64 px. Le rail de la maquette
     fait 349 × 75 ; ici il est plus étroit (74 % d'une colonne de 336) et
     PLEIN — c'est le contenu qui a grossi, pas la boîte. */
  .rail.peint::before {
    content: ''; flex: none; width: 40px; height: 40px;
    background: var(--icone) center / contain no-repeat;
    filter: drop-shadow(0 1px 0 rgba(0, 0, 0, .5));
  }
  /* le chevron aussi est le sien : même geste de survol que le glyphe */
  .rail.peint::after {
    content: ''; width: 13px; height: 21px; opacity: .9;
    background: var(--icone-chevron) center / contain no-repeat;
  }
  /* DEUX LIGNES, PARCE QUE LE DESIGN LE DIT — PAS PARCE QUE LE TEXTE NE
     TIENT PAS. Sur la maquette, PERSONNALISER / LE PERSONNAGE, PERSONNALISER /
     LA MAP et COPIER LE LIEN / D'INVITATION sont sur deux lignes ; RÉGLAGES et
     CONTRÔLER LA HORDE sur une. Un équilibrage « naturel » laissait LA MAP sur
     une ligne (il tenait), et le propriétaire a tranché : « respecte le
     design ». La coupe est donc FORCÉE par la boîte du texte : `--deux` est
     plus étroit que le libellé entier et plus large que son mot le plus long,
     en FRANÇAIS comme en ANGLAIS (mesuré), et `balance` choisit alors la
     coupe la plus égale — PERSONNALISER / LA MAP, CUSTOMISE / THE MAP. Le
     texte est dans un <span data-i18n> : le bouton reste un <button>, le mode
     édition clique un texte, la traduction réécrit le span et rien d'autre. */
  .rail.peint.deux > span {
    display: block; max-width: var(--deux, 10em); text-wrap: balance;
  }
  .rail.peint:hover { filter: brightness(1.14); }
  .rail.peint:disabled:hover { filter: none; }
  .ic-personnage { --icone: url(../textures/ui/icone-personnage.webp); }
  .ic-carte { --icone: url(../textures/ui/icone-carte.webp); }
  .ic-lien { --icone: url(../textures/ui/icone-lien.webp); }
  .ic-reglages { --icone: url(../textures/ui/icone-reglages.webp); }
  .ic-horde { --icone: url(../textures/ui/icone-horde.webp); }
  /* LE CARNET DE SURVIE n'a pas encore d'icône PEINTE : en attendant la
     planche du propriétaire (qui prendra la place de cette ligne, en
     `icone-carnet.webp` comme ses voisines), un dessin du même langage — os
     crème cerné d'encre brune, légèrement penché, les anneaux de la spirale
     sur la tranche gauche comme le glyphe du tiroir tactile (`carnet`,
     tactile.js). Un dessin et non un fichier : rien à importer pour une
     icône provisoire. */
  .ic-carnet { --icone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='rotate(-6 20 20)' stroke='%23231a12' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='10' y='5' width='23' height='30' rx='2' fill='%23e8dcbf' stroke-width='3'/%3E%3Cpath d='M16 13h12M16 18h12M16 23h9' fill='none' stroke-width='2.2'/%3E%3Cpath d='M6 10h8M6 16h8M6 22h8M6 28h8' fill='none' stroke='%23231a12' stroke-width='4.4'/%3E%3Cpath d='M6 10h8M6 16h8M6 22h8M6 28h8' fill='none' stroke='%23e8dcbf' stroke-width='1.6'/%3E%3C/g%3E%3C/svg%3E"); }
  /* LES QUATRE CATÉGORIES DE MON PERSONNAGE. Elles sont dans la RÉSERVE et non
     chez leur écran pour la même raison que les cinq de l'accueil : une icône
     est du LANGAGE, et la réserve est un endroit, pas deux. Le propriétaire les
     a peintes CRÈME SUR LA TÔLE de leur bouton, pas détourées comme les
     précédentes — c'est l'import qui leur a fabriqué un alpha depuis leur
     luminance (`glyphe`, tools/images-ui.mjs), et le résultat se pose ici comme
     les autres. */
  .ic-corps { --icone: url(../textures/ui/icone-corps.webp); }
  .ic-visage { --icone: url(../textures/ui/icone-visage.webp); }
  .ic-tenue { --icone: url(../textures/ui/icone-tenue.webp); }
  .ic-accessoires { --icone: url(../textures/ui/icone-accessoires.webp); }

  /* --- LA PLAQUE ÉMAILLÉE : le geste principal d'un écran, et il n'y en a
         jamais qu'un (PRÊT au salon, GARDER CE PERSONNAGE à l'édition). Une
         plaque de tôle émaillée crème, usée, avec son texte peint en rouge —
         l'inverse exact de tout le reste de l'interface, et c'est pour ça
         qu'on ne peut pas la manquer. L'image porte l'usure ; l'aplat crème
         dessous porte la lisibilité. */
  .enseigne {
    position: relative; display: inline-flex; align-items: center;
    justify-content: center; gap: 16px;
    padding: 16px 46px; border-radius: 2px;
    /* LA TÔLE EST CADRÉE, PAS ÉTIRÉE. L'image de plaque est en 3:2 et le bouton
       en 7:1 : à `100% 100%` les coins arrondis et les vis s'écrasaient en
       volutes, et la tôle se lisait comme un parchemin. En `cover` on ne garde
       que la BANDE CENTRALE — l'émail usé, ses piqûres de rouille, sa tache de
       sang — et c'est le CSS qui refait le bord : un liseré rouge peint et une
       arête sombre, exactement ce que la maquette montre. Aucune déformation,
       quelle que soit la largeur du bouton. */
    /* LE REPLI EST UN APLAT, PAS UN DÉGRADÉ. Le détourage laisse deux ou trois
       pixels de transparence au bord de la tôle : un dégradé dessous s'y voyait
       comme deux barres CLAIRES en bout de plaque, alors qu'un aplat de la
       teinte de l'émail s'y confond. La règle vaut pour toute image détourée
       posée sur un repli. */
    background: var(--plaque) center / cover, #ded1b3;
    border-width: 2px; border-style: solid; border-color: #3a2419;
    color: #8f1a13; font-family: var(--police-titre); font-weight: 400;
    font-size: 30px; letter-spacing: .06em; text-transform: uppercase;
    cursor: pointer; opacity: 1;
    box-shadow: 0 10px 26px rgba(0, 0, 0, .55),
      inset 0 0 0 3px rgba(143, 26, 19, .55), inset 0 0 0 5px rgba(0, 0, 0, .12);
    /* LE RETOUR EST EN MOUSSE (demande du propriétaire) : la plaque se soulève
       d'un rien au survol et REBONDIT en place — la courbe dépasse sa cible
       puis revient, c'est ce qui fait « mousse » et non « bouton de site ».
       Le relâchement (`:active`) l'enfonce, plus vite et sans rebond : on
       appuie sur une plaque, elle ne rebondit pas sous le doigt. */
    transition: filter .18s, box-shadow .22s,
      transform .28s cubic-bezier(.34, 1.56, .64, 1);
  }
  .enseigne::before {
    /* le triangle de lecture, peint comme le texte : il dit « ça part » */
    content: ''; flex: none; width: .52em; height: .62em; background: currentColor;
    clip-path: polygon(0 0, 100% 50%, 0 100%);
  }
  .enseigne:hover {
    filter: brightness(1.07);
    transform: translateY(-2px) scale(1.015);
    box-shadow: 0 14px 30px rgba(0, 0, 0, .6),
      inset 0 0 0 3px rgba(143, 26, 19, .55), inset 0 0 0 5px rgba(0, 0, 0, .12);
  }
  .enseigne:active {
    transform: translateY(1px) scale(.99);
    transition-duration: .18s, .1s, .08s; transition-timing-function: ease-out;
  }
  @media (prefers-reduced-motion: reduce) {
    .enseigne, .enseigne:hover, .enseigne:active { transform: none; transition: filter .18s; }
  }
  .enseigne:disabled { filter: grayscale(.6) brightness(.7); cursor: default; }

  /* --- LES ACTIONS DU BAS. Sobres, sombres, et RETOUR porte son chevron à
         gauche : c'est la seule chose qui distingue « je m'en vais » de « je
         fais ». Même mécanique que le rail — un `::after`/`::before`, pas un
         mot de plus dans le document. */
  /* MÊME RELIEF QUE L'OPTION : liseré clair, coin de 5 px, ombre courte et
     basse. Les deux sont la même chose pour la main — un bouton sombre qu'on
     presse —, et leur donner deux épaisseurs différentes se lirait comme deux
     familles. */
  .overlay .actions button:not(.enseigne) {
    background: var(--creux); border: 1px solid var(--filet-fort); border-radius: 5px;
    color: var(--creme); font-family: var(--police-ui); font-size: 11.5px;
    letter-spacing: .18em; text-transform: uppercase; padding: 12px 22px;
    cursor: pointer; opacity: .85; box-shadow: 0 2px 4px rgba(0, 0, 0, .55);
    transition: opacity .18s, border-color .18s;
  }
  .overlay .actions button:hover { opacity: 1; border-color: var(--os); }
  .overlay .actions button[data-back]::before {
    content: '\2039'; margin-right: 11px; font-size: 19px; vertical-align: -2px; opacity: .6;
  }

  /* --- LA PROSE. Tout ce qui se lit en phrases garde le Georgia : c'est du
         texte, pas de la signalétique. Il ne prend jamais l'encre pleine —
         une explication n'est pas une alerte. */
  .overlay .note, .overlay .brief {
    font-family: var(--police-texte); color: var(--creme);
  }
  /* les champs de saisie : un creux dans le panneau, pas une boîte posée
     dessus — d'où l'ombre INTÉRIEURE et le fond plus sombre que le panneau */
  #seedInput, .txtin {
    background: var(--creux); border-color: var(--filet);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, .5);
    border-radius: 3px; color: var(--os);
  }

  /* --- L'ENVELOPPE. L'icône du seul champ de la porte : une adresse e-mail.
         Même trait de 1,6 que le reste de la réserve — elle est ici et non
         dans login.html parce qu'une icône est du LANGAGE, et que la réserve
         est un endroit, pas deux. */
  .i-enveloppe { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'%3E%3Crect x='2.6' y='5' width='18.8' height='14' rx='1.8'/%3E%3Cpath d='m3.4 6.6 8.6 6.2 8.6-6.2'/%3E%3C/svg%3E"); }

  /* --- LA MARQUE. Le logo est FOURNI par le propriétaire (textures/CREDITS.md)
         et il est POSÉ COMME UNE AFFICHE : une image, et dessous deux mots
         peints au pochoir qui reprennent la place si le fichier manque. Le
         `:has()` les efface tant que l'image est là — donc `onerror` n'a qu'à
         retirer l'image, il n'a rien d'autre à rétablir. C'est ici et pas dans
         un écran parce que DEUX écrans la portent maintenant : l'accueil et
         la porte. Chacun n'écrit ensuite que sa TAILLE et sa PLACE. */
  h1.marque { margin: 0; padding: 0; display: block; line-height: 0; }
  h1.marque img {
    display: block; width: 100%; height: auto;
    filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .75));
  }
  h1.marque:has(img) .mot { display: none; }
  h1.marque .mot {
    display: block; line-height: .92; font-size: 58px; letter-spacing: .04em;
    transform: skewX(-5deg);
  }
  h1.marque .mot.deux { color: var(--braise); font-size: 62px; }

  /* --- LA PLAQUE DE SANG : la variante ROUGE du geste principal. Deux écrans
         la portent — PRÊT au salon, RECEVOIR UN CODE à la porte — et c'est
         pour ça qu'elle est ici et non chez l'un des deux. Elle ne remplace
         pas l'émail crème : l'émail dit « valide ce que tu viens de régler »
         (GARDER CE PERSONNAGE), le sang dit « pars ». D'où le triangle qui
         passe à DROITE, dans le sens de lecture, au lieu d'annoncer à gauche.
         La matière peinte viendra de `--tache` (image encore à faire) ;
         l'aplat rouge dessous porte déjà la forme et la lisibilité. */
  /* ELLE EST PEINTE, ET SON CADRE EST FOURNI. Avec `.peint`, le dégradé rouge
     et la bordure sombre passent DERRIÈRE le sprite : ils ne disparaissent pas,
     ils deviennent le repli. L'ombre intérieure claire, elle, est RETIRÉE
     quand le cadre est là — le liseré peint dit déjà le bord, et deux bords
     l'un sur l'autre font un tube. */
  .enseigne.sang {
    border-width: 3px; border-style: solid; border-color: #2a0f0b; border-radius: 4px;
    background:
      var(--tache, none) center / 100% 100% no-repeat,
      linear-gradient(178deg, #c9231a, #9d150f);
    color: #fbeee6; letter-spacing: .02em;
    box-shadow: 0 12px 30px rgba(0, 0, 0, .6), inset 0 0 0 2px rgba(255, 220, 210, .25);
  }
  .enseigne.sang.peint {
    --cadre: var(--cadre-geste);
    --cadre-decoupe: 11 13;
    /* le cadre est plus épais sur les CÔTÉS que sur les arêtes, comme le
       sprite : c'est ce qui empêche le 9-slice de comprimer les coins */
    border-width: 9px 12px;
    /* RIEN NE RESTE DERRIÈRE LE CADRE. Un cadre peint est DÉTOURÉ — son alpha
       s'éteint en biseau sur les deux ou trois pixels du bord —, donc ce qu'on
       laisse dessous ressort par ce biseau et se lit comme une frange sale.
       `border-radius` était le pire des trois : il découpait le fond et
       l'ombre en coins ronds là où le sprite a des coins carrés et usés, ce qui
       posait une écharpe sombre à chaque angle. Les déclarations restent
       écrites plus haut, où elles sont le REPLI — `border-image` n'en a pas
       d'autre —, et l'ombre PORTÉE reste : elle décolle le bouton de la ville,
       elle ne borde pas la peinture. */
    border-radius: 0; background: none;
    box-shadow: 0 12px 30px rgba(0, 0, 0, .6);
  }
  /* LA PLAQUE AUX MAINS : le même geste, avec le cadre rouge fourni pour PRÊT
     (`pret-fond.webp`) — des mains de zombie se dressent dans son tiers gauche.
     Elles ne peuvent pas rester dans le CENTRE du 9-slice : le centre s'étire
     à la boîte, et sur un bouton sept fois plus large que haut elles
     s'aplatissaient en traînée. Elles entrent donc dans la TRANCHE GAUCHE
     (250 px sur le fichier servi, mesuré : les doigts vont à 240), et le bord
     gauche prend la largeur qui garde à cette tranche SES PROPORTIONS — elle
     fait 250 × 230 à la source, et sa hauteur d'arrivée est celle de la boîte
     de remplissage (ligne + marges), d'où `1.2em + 28px` : 76 px pour 40 px de
     corps, 59 pour 26, et les mains ne s'écrasent pas de plus d'un cinquième
     entre les deux. Le mot commence APRÈS les mains, comme sur la maquette.
     Le haut et le bas gardent leur bord de 9 px (liseré 12–14 px servi) ; à
     droite, le liseré du fichier est rongé et fin, 16 de tranche le tiennent.
     COCHÉ, la source de cadre se retire (cf. `#ready.selected` à l'accueil) :
     le vert nu est le seul signal que le salon partage, et les mains partent
     avec le rouge — le bord gauche retombe alors à celui de l'état. */
  .enseigne.sang.peint.mains {
    --cadre: var(--cadre-pret);
    --cadre-decoupe: 16 16 15 250;
    border-width: 9px 12px 9px calc(1.2em + 28px);
  }
  /* LES BOUTONS D'OPTION PEINTS ne se déclarent pas pour tout le monde : le
     sprite fourni est celui d'une LANGUE, deux boutons côte à côte qui sont le
     seul choix d'un écran. Les rangées d'options des RÉGLAGES en alignent
     jusqu'à douze — douze cadres peints y feraient du bruit, pas de la
     signalétique. La classe existe donc ici, et chaque écran décide. */
  /* `:not(.enseigne)` LUI AUSSI, et pas par élégance : sans lui cette règle
     pèse une classe de moins que celle de l'option au repos, qui reprendrait
     alors le coin rond, le liseré clair et l'ombre — un cadre peint porte les
     trois DANS son sprite, et deux bords l'un sur l'autre font un tube. */
  .times button.peint:not(.enseigne) {
    --cadre: var(--cadre-option);
    --cadre-decoupe: 11 12;
    border-width: 7px 8px;
    /* rien derrière le cadre — cf. la plaque de sang, plus haut : le fond
       sombre et les coins ronds ressortaient par le biseau du détourage */
    border-radius: 0; background: none; box-shadow: none;
  }
  .times button.peint.selected {
    --cadre: var(--cadre-option-choisie);
    --cadre-decoupe: 10 11;
    /* le liseré ROUGE du sprite dit déjà « choisi » : l'éclairage intérieur
       du bouton d'option ferait double emploi et mangerait le mot */
    box-shadow: none;
  }
  /* ═══════════════════════════════════════════════════════════════════════
     LA PLAQUE À GLYPHE — un sprite, un bouton, et son dessin est dedans
     ═══════════════════════════════════════════════════════════════════════
     Un CADRE peint se réutilise à toutes les tailles, il n'a rien au milieu,
     et il se découpe en neuf (cf. LE CADRE PEINT, plus haut). Une PLAQUE À
     GLYPHE, non : le propriétaire y a peint le dessin du bouton — le chevron
     du retour, le caddie de la boutique, la flèche de reprise —, dans les
     coins comme au centre. Découpée en neuf, elle se serait délavée sur toute
     la largeur ; gommée, elle aurait perdu ce qui la rend reconnaissable.

     ELLE GARDE DONC SES PROPORTIONS, ET C'EST CE QUI RÉCONCILIE LES DEUX
     RÈGLES. `aspect-ratio` verrouille la boîte sur le rapport du FICHIER, si
     bien que `background-size: 100% 100%` ne déforme plus rien : ni le liseré,
     ni les coins usés, ni le glyphe. C'est le régime d'`eclaboussure.webp`,
     pour la même raison — un sprite qu'un seul élément porte.

     LE MOT SE POSE ENTRE LES GLYPHES. `--pg-g` et `--pg-d` sont les fractions
     de largeur que le dessin occupe à gauche et à droite, MESURÉES sur les
     pixels du fichier servi : le texte ne peut donc pas monter sur le caddie,
     quelle que soit la langue. Et la boîte ne grandit pas avec lui — c'est une
     plaque, elle a la taille qu'elle a ; un mot trop long s'y coupe plutôt que
     de la déformer.

     LE REPLI EST L'APLAT DESSOUS, comme partout : fichier absent, il reste un
     bouton sombre lisible, sans son dessin. */
  .plaque-glyphe {
    box-sizing: border-box; display: inline-flex; flex: none;
    align-items: center; justify-content: center;
    /* elle a la taille qu'elle a — sauf sur une fenêtre plus étroite qu'elle,
       où elle rétrécit en gardant son rapport plutôt que de déborder */
    width: min(var(--pg-l), 100%); aspect-ratio: var(--pg-r);
    padding: 0 calc(var(--pg-l) * var(--pg-d)) 0 calc(var(--pg-l) * var(--pg-g));
    background: var(--pg) center / 100% 100% no-repeat, var(--pg-repli, #241d15);
    border-width: 0; border-style: none; border-radius: 0;
    box-shadow: none; opacity: 1;
    color: var(--os); font-family: var(--police-ui); font-size: 11.5px;
    letter-spacing: .14em; text-transform: uppercase; text-align: center;
    line-height: 1.15; overflow: hidden; cursor: pointer;
    transition: filter .18s, transform .18s;
  }
  .plaque-glyphe:hover { filter: brightness(1.16); }
  .plaque-glyphe:active { transform: translateY(1px); }
  /* LES DEUX RÈGLES DE LA RANGÉE D'ACTIONS L'EMPORTAIENT (une classe de plus
     qu'ici) : on les reprend au même poids plutôt que de surenchérir chez
     chaque écran, et le chevron `‹` du RETOUR s'efface — il est PEINT dans la
     plaque, deux chevrons feraient deux boutons. */
  .overlay .actions button.plaque-glyphe {
    background: var(--pg) center / 100% 100% no-repeat, var(--pg-repli, #241d15);
    border: 0; border-radius: 0; box-shadow: none; opacity: 1;
    padding: 0 calc(var(--pg-l) * var(--pg-d)) 0 calc(var(--pg-l) * var(--pg-g));
  }
  .overlay .actions button.plaque-glyphe::before { content: none; }
  /* (LE RETOUR À L'ÉCLABOUSSURE a vécu ici. Depuis les maquettes du 11
     septembre, toute sous-page sort par le carré au chevron en haut à gauche
     — `.plaque-carree.chevron`, plus bas.) */
  /* LA BOUTIQUE : le caddie tient les 19 premiers pour cent, le chevron les
     onze derniers. Sprite 1825 × 470. */
  .pg-boutique {
    --pg: var(--plaque-boutique); --pg-l: 196px; --pg-r: 3.883;
    --pg-g: .24; --pg-d: .16; --pg-repli: #8d1712;
    color: #ffe9e4;
  }
  /* ═══════════════════════════════════════════════════════════════════════
     LA PLAQUE CARRÉE — un bouton d'icône, et son dessin est du CSS
     ═══════════════════════════════════════════════════════════════════════
     Le MENU et sa CROIX, sur mobile. Ce n'est PAS une plaque à glyphe : là le
     dessin est peint dans le fichier, ici il n'y a pas de fichier du tout. Un
     burger est trois traits et une croix deux diagonales — de la GÉOMÉTRIE,
     pas de la matière —, et une image pour ça serait plus lourde, floue au
     redimensionnement et impossible à teindre (cf. « ce qui doit vraiment être
     une image », tools/images-ui.mjs).

     LE CADRE, LUI, EST PEINT : c'est celui des rails (`--cadre-rail`), découpé
     en neuf comme partout — un cadre se réutilise à toutes les tailles, c'est
     exactement ce pour quoi il existe. La tranche est la même que chez le rail
     (44), le bord plus fin parce que la boîte est petite : à 10 px sur 46, le
     liseré aurait mangé le glyphe. */
  .plaque-carree {
    position: relative; display: grid; place-items: center; flex: none;
    width: 46px; height: 46px; padding: 0; border-radius: 0;
    border-width: 7px; border-style: solid; border-color: var(--filet-fort);
    border-image-source: var(--cadre-rail);
    border-image-slice: 44 fill; border-image-repeat: stretch;
    background: none; box-shadow: none; opacity: .92; cursor: pointer;
    color: var(--creme);
    transition: filter .18s, opacity .18s, transform .28s cubic-bezier(.34, 1.56, .64, 1);
  }
  .plaque-carree:hover { filter: brightness(1.14); opacity: 1; }
  .plaque-carree:active { transform: scale(.94); transition-duration: .18s, .18s, .08s; }
  /* LES TRAITS SONT DES BOÎTES, PAS DES BORDURES : une bordure du bouton est
     déjà prise par le cadre peint. Trois `box-shadow` sur un seul rectangle —
     un nœud de moins, et les trois traits ne peuvent pas se désaligner. */
  .plaque-carree.burger::before {
    content: ''; width: 20px; height: 2.5px; background: currentColor;
    box-shadow: 0 -7px 0 currentColor, 0 7px 0 currentColor;
  }
  /* la croix : le même trait, croisé avec lui-même */
  .plaque-carree.croix::before,
  .plaque-carree.croix::after {
    content: ''; position: absolute; width: 21px; height: 2.5px;
    background: currentColor;
  }
  .plaque-carree.croix::before { transform: rotate(45deg); }
  .plaque-carree.croix::after { transform: rotate(-45deg); }
  /* le RETOUR : deux côtés d'un carré, tourné — le chevron ‹ de la maquette de
     la recharge. Décalé d'un rien vers la droite : un chevron centré sur sa
     boîte a l'œil qui le voit trop à gauche. */
  .plaque-carree.chevron::before {
    content: ''; width: 13px; height: 13px; margin-left: 6px;
    border-left: 3px solid currentColor; border-bottom: 3px solid currentColor;
    transform: rotate(45deg);
  }
  @media (prefers-reduced-motion: reduce) {
    .plaque-carree, .plaque-carree:active { transform: none; transition: filter .18s; }
  }

  /* --- LES DEUX PLAQUES DE L'ARGENT, À L'ACCUEIL ---------------------------
     Elles sont ici et non chez l'accueil parce qu'elles sont des PLAQUES À
     GLYPHE comme les trois précédentes : un sprite, un bouton, le dessin
     dedans. Ce qui leur est propre est mesuré sur le fichier servi, comme
     partout — la fraction de largeur que le dessin prend de chaque côté.

     LE BOUTON DE LA BOUTIQUE (1024 × 342). Le sac, la liasse, la canette et
     les balles tiennent les 33 premiers pour cent ; le chevron et ses boulons
     les 16 derniers. Il remplace le rail sombre : le propriétaire en a fait le
     seul geste ROUGE de la pile, et il est plus haut que ses voisins parce que
     son rapport est celui de la planche — l'écraser à la hauteur d'un rail
     aurait aplati le sac, qui est le sujet. */
  .pg-boutique-accueil {
    --pg: var(--plaque-boutique-accueil); --pg-l: 250px; --pg-r: 2.994;
    /* REPLI TRANSPARENT, comme le RETOUR et le MAGOT. Un aplat de secours n'est
       légitime que si la planche REMPLIT sa boîte : celle-ci a des coins coupés
       et un sac qui déborde, si bien que le rouge se voyait TOUT AUTOUR de la
       silhouette (c'est le défaut qu'a vu le propriétaire). Ce qu'on perd est
       réel et assumé : fichier absent, le bouton n'a plus de fond — mais un
       rectangle rouge autour d'une planche découpée est un défaut PERMANENT,
       là où le fichier absent est un accident. */
    --pg-g: .34; --pg-d: .17; --pg-repli: transparent;
    /* BANGERS, comme le magot et comme les titres du jeu (demande du
       propriétaire) : ces deux planches sont peintes, pas imprimées, et la
       grotesque d'interface y sonnait comme une étiquette collée dessus.
       GRAISSE 400 ET PAS 700 : la fonte n'a qu'une graisse (cf. `@font-face`,
       plus haut) — en demander une autre laisse le navigateur l'ÉPAISSIR
       lui-même, et un Bangers synthétiquement gras se boursoufle. Et
       l'interlettrage RETOMBE : Bangers est déjà large et tout en capitales,
       les .09em de la grotesque y creusaient des trous. */
    color: #ffe9e4; font-family: var(--police-titre); font-weight: 400;
    letter-spacing: .04em; font-size: 25px;
  }
  /* LE RETOUR EN MOUSSE, CELUI DE PRÊT (demande du propriétaire). Les deux
     planches du coin sont les seules de l'accueil qu'on presse en dehors de
     PRÊT, et elles répondaient comme un bouton d'écran — un simple
     éclaircissement. Elles reprennent donc SA courbe : la plaque se soulève
     d'un rien au survol et REBONDIT en place, le ressort dépassant sa cible
     avant de revenir (`cubic-bezier(.34, 1.56, .64, 1)`), et le relâchement
     l'enfonce plus vite et SANS rebond — on appuie sur une plaque, elle ne
     rebondit pas sous le doigt.

     L'OMBRE, ELLE, NE SE RECOPIE PAS. PRÊT creuse une `box-shadow` : c'est une
     tôle rectangulaire, sa boîte EST sa forme. Ces deux-là sont détourées —
     coins coupés, sac et liasse qui débordent —, et une ombre de boîte y
     dessinerait le rectangle qu'on vient justement de retirer du repli. C'est
     `drop-shadow` qui la remplace : le même geste, mais suivi sur l'ALPHA. Il
     entre dans le `filter` déjà là, à la suite de la luminosité — deux
     `filter` sur la même règle, le second gagne en silence. */
  .pg-magot, .pg-boutique-accueil {
    transition: filter .22s, transform .28s cubic-bezier(.34, 1.56, .64, 1);
  }
  .pg-magot:hover, .pg-boutique-accueil:hover {
    filter: brightness(1.07) drop-shadow(0 8px 12px rgba(0, 0, 0, .55));
    transform: translateY(-2px) scale(1.015);
  }
  .pg-magot:active, .pg-boutique-accueil:active {
    transform: translateY(1px) scale(.99);
    transition-duration: .1s, .08s; transition-timing-function: ease-out;
  }
  @media (prefers-reduced-motion: reduce) {
    .pg-magot, .pg-magot:hover, .pg-magot:active,
    .pg-boutique-accueil, .pg-boutique-accueil:hover, .pg-boutique-accueil:active {
      transform: none; transition: filter .18s;
    }
  }

  /* LE MAGOT (1024 × 294). La liasse tient les 32 premiers pour cent, la CASE
     CRÈME les 24 derniers — et cette case est le seul endroit du jeu où un
     dessin fourni attend qu'on écrive dedans : le `+` s'y pose en repère
     absolu, sur la fraction mesurée, et non au fil du texte. Le repli est
     TRANSPARENT comme celui du RETOUR : la silhouette déborde de tous les
     côtés, un aplat de secours dessinerait un rectangle qui n'existe pas. */
  .pg-magot {
    --pg: var(--plaque-magot); --pg-l: 250px; --pg-r: 3.483;
    --pg-g: .33; --pg-d: .26; --pg-repli: transparent;
    position: relative; color: #f3e7cf;
    font-family: var(--police-titre); font-weight: 400;
    letter-spacing: .03em; font-size: 23px;
  }
  /* LA CASE EST MESURÉE, PAS ESTIMÉE. Sur le fichier servi (1024 x 294) le
     carré crème occupe x 801..959 et y 68..216 : son centre est à 85,0 % de
     la largeur et 48,3 % de la hauteur. Le `+` s'y pose PAR SON CENTRE (deux
     nombres, et le décalage de moitié) plutôt que par quatre bords — une boîte
     posée à l'estime l'avait laissé neuf pour cent trop bas, et quatre nombres
     qui doivent s'accorder se remesurent quatre fois. */
  /* SANS RECHARGE (l'achat en argent réel pas encore sorti pour ce compte) :
     la plaque n'est plus que le SOLDE — pas de `+`, pas de soulèvement, pas de
     main au survol. Elle ne promet pas ce qu'elle n'ouvre pas. */
  .pg-magot.sans-recharge { cursor: default; }
  .pg-magot.sans-recharge::after { content: none; }
  .pg-magot.sans-recharge:hover, .pg-magot.sans-recharge:active { filter: none; transform: none; }
  .pg-magot::after {
    content: '+'; position: absolute; left: 85%; top: 48.3%;
    transform: translate(-50%, -50%);
    color: #2f2618; font-family: var(--police-titre); font-weight: 400;
    /* 34 px et non 21 : le `+` de Bangers est un signe TRAPU qui n'occupe
       qu'un tiers de son cadratin, là où celui d'une grotesque en prend la
       moitié. À la taille du texte il flottait dans la case. */
    font-size: 34px; letter-spacing: 0; line-height: 1; opacity: .85;
  }

  /* LA REPRISE : la flèche circulaire tient les 15 premiers pour cent, le
     chevron les six derniers. Sprite 1980 × 309, le plus long des trois —
     c'est le seul qui porte une phrase entière. */
  .pg-refaire {
    --pg: var(--plaque-refaire); --pg-l: 330px; --pg-r: 6.408;
    --pg-g: .20; --pg-d: .13; --pg-repli: #241d15;
    color: #f2e9d4; font-size: 11px;
  }

  /* le triangle de l'émail annonce à gauche ; celui-ci conclut à droite. On
     n'en dessine pas un second : on déplace le même. */
  .enseigne.sang::before { display: none; }
  .enseigne.sang::after {
    content: ''; margin-left: auto; flex: none;
    width: .42em; height: .5em; background: currentColor;
    clip-path: polygon(0 0, 100% 50%, 0 100%);
  }

  /* ═══════════════════════════════════════════════════════════════════════
     LA SOUS-PAGE, ET LA FEUILLE DE PAPIER DES MENTIONS LÉGALES
     ═══════════════════════════════════════════════════════════════════════
     Elles sont ici parce que DEUX documents les portent : le jeu et la PORTE
     (login.html). Les deux ouvrent CONFIDENTIALITÉ et CGU, les deux les
     remplissent depuis `src/legal.js` — le même texte, donc la même feuille.
     Recopier la découpe déchirée chez la porte, c'était garantir qu'un jour
     les deux ne se déchireraient plus pareil. */
  /* --- sous-pages (personnage / carte / contrôles) : même gabarit,
         RETOUR toujours en premier dans la rangée d'actions en bas --- */
  .overlay.subpage {
    cursor: default; z-index: 12; overflow-y: auto;
    justify-content: flex-start; padding: 24px 0 32px; box-sizing: border-box;
    /* LE SALON RESTE DERRIÈRE. Le voile était à 97 % : on ne voyait plus la
       scène du tout, et une sous-page se lisait comme une autre application.
       Il descend à 82 % — la pièce se devine, le panneau est opaque, et rien
       ne perd en lisibilité. Les pages qui s'empilent SUR une sous-page
       (succès, boutique, manette) restent opaques : voir les réglages
       transparaître à travers la boutique n'apprend rien. */
    background: radial-gradient(ellipse at center, rgba(8, 9, 12, .82), rgba(0, 0, 0, .95));
  }
  /* la rangée d'actions : sa MISE EN PAGE est ici, l'aspect de ses boutons est
     dans le langage commun (plus haut) — les deux ne peuvent pas coexister,
     `.overlay .actions button` l'emportait sur la plaque de GARDER CE
     PERSONNAGE, même faute que `.overlay .times button` avec PRÊT */
  .overlay .actions { margin-top: 26px; display: flex; gap: 12px; }
  /* --- LES MENTIONS LÉGALES (cf. src/legal.js). Même gabarit de sous-page,
         un texte suivi en colonne, aligné à gauche. Fond opaque comme les
         succès : rien à laisser transparaître derrière. --- */
  #privacyOverlay, #termsOverlay {
    background: radial-gradient(ellipse at center, #0a0b0e, #000);
  }
  /* CES DEUX PAGES SONT UNE FEUILLE DE PAPIER, et c'est la seconde inversion
     du jeu après les RÉGLAGES — pour la même raison, poussée plus loin : ce
     sont les deux seuls écrans qu'on lit du début à la fin. Le bord est
     DÉCHIRÉ (`clip-path`, une trentaine de points) : une feuille arrachée d'un
     bloc, punaisée au mur du salon comme les affiches de la ville. Le
     découpage se dessine en CSS, jamais en image — il doit suivre la hauteur
     du texte, qui change avec la langue. */
  .overlay.legal .legalMaj {
    font-family: var(--police-ui); font-size: 11px; letter-spacing: .22em;
    color: #6b5a44; margin: -8px 0 20px; text-transform: uppercase;
  }
  .overlay.legal h1 { margin-bottom: 16px; }
  .overlay.legal .legalCorps {
    position: relative;
    width: min(94vw, 760px); padding: 40px 46px 52px;
    box-sizing: border-box; text-align: left;
    background: linear-gradient(178deg, #efe6d2 0%, #e2d6bb 100%);
    color: #2a2118;
    filter: drop-shadow(0 16px 34px rgba(0, 0, 0, .6));
    clip-path: polygon(
      0.6% 0.4%, 12% 0%, 26% 0.6%, 41% 0%, 58% 0.7%, 73% 0%, 88% 0.6%, 99.4% 0%,
      100% 14%, 99.3% 32%, 100% 50%, 99.2% 68%, 100% 86%, 99.5% 100%,
      86% 99.4%, 71% 100%, 55% 99.3%, 38% 100%, 22% 99.4%, 8% 100%, 0.4% 99.5%,
      0.8% 82%, 0% 64%, 0.7% 46%, 0% 28%, 0.6% 14%);
  }
  .overlay.legal .legalCorps::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    opacity: .34; mix-blend-mode: multiply;
    background: var(--grain) repeat; background-size: 256px 256px;
  }
  /* UN TITRE DE SECTION EST UNE PETITE ENSEIGNE. Le même pinceau que partout,
     mais posé sur du papier : il n'a plus besoin d'être large, il lui suffit
     de trancher. `display: table` pour qu'il ne prenne que sa largeur. */
  .overlay.legal .legalCorps h2 {
    display: table; position: relative;
    font-family: var(--police-titre); font-weight: 400; font-size: 20px;
    letter-spacing: .05em; text-transform: uppercase; color: var(--os);
    margin: 34px 0 12px; padding: 5px 14px 6px; transform: skewX(-5deg);
  }
  /* MÊME BOÎTE COURTE, MÊME DÉFAUT, MÊME REMÈDE que l'enseigne des sous-pages
     ci-dessus : la brosse en MASQUE, la couleur dans le dégradé. */
  .overlay.legal .legalCorps h2::before {
    content: ''; position: absolute; z-index: -1; inset: 0;
    background: linear-gradient(92deg, var(--sang) 0, #8d1712 100%);
    -webkit-mask: var(--bandeau) center / 110% 230% no-repeat;
    mask: var(--bandeau) center / 110% 230% no-repeat;
  }
  .overlay.legal .legalCorps h2:first-child { margin-top: 0; }
  .overlay.legal .legalCorps p {
    position: relative; font-size: 13.5px; line-height: 1.85;
    color: #3b3126; margin: 0 0 12px; max-width: 60em;
  }
