/* FamilyTree_GOT — interface claire, fiches façon arbre généalogique. */

/* `hidden` doit cacher. Toujours.
 *
 * **Le défaut qu'on répare (13.B).** Le navigateur pose `display: none` sur un
 * élément `hidden`, mais c'est une règle d'agent utilisateur : **la moindre
 * règle d'auteur qui donne un `display` la bat**. Cinq éléments de cette
 * application recevaient un `display: flex` par leur classe et restaient donc
 * visibles alors que le script les avait cachés — mesuré le 14/08/2026 sur un
 * compte d'intendant :
 *
 * - `#groupe-essai` — « Créer un compte » et « Se connecter » proposés à
 *   quelqu'un de déjà connecté. C'est ce qui a été signalé.
 * - `#bandeau-procuration` — « vous écrivez chez les autres », affiché alors
 *   qu'on écrivait chez soi. Le plus grave : ce bandeau ment sur où l'on écrit.
 * - `#bandeau-essai` — l'invitation à créer un compte, sur un compte existant.
 * - `#lien-document` — « 📜 Document » sans document où aller.
 * - `#scene-message` — un message de scène vide.
 *
 * Une règle, et la classe entière de défauts disparaît. Le `!important` est ici
 * à sa place : il n'y a pas de cas où l'on veuille afficher un `hidden`. */
[hidden] { display: none !important; }

:root {
  --fond: #eaeef3;
  --fond-2: #ffffff;
  --fond-3: #f3f6f9;
  --plan: #f7f9fb;
  --plan-grille: #e3e8ee;
  --bord: #dde3ea;
  --bord-clair: #c9d1db;
  --texte: #1f2937;
  --texte-doux: #56606d;
  --texte-faible: #8b95a1;
  --accent: #2f9e8f;
  --accent-fonce: #24796d;
  --accent-doux: #dff2ee;
  --carte-fond: #ffffff;
  /* Une seule couche, et non deux.
   *
   * L'ombre d'une carte était empilée : un liseré net à 2 px de flou, plus un
   * halo à 10. Or elle est posée sur le bandeau **et** sur le corps de chaque
   * fiche, plus la photo, plus la poignée — mesuré le 15/08/2026 sur Westeros,
   * **665 couches d'ombre floue** dans un monde de 21 mégapixels, à re-tramer
   * à chaque cran de zoom. Une couche unique, un peu plus large et un peu plus
   * douce, rend le même relief pour la moitié du travail.
   *
   * On a d'abord voulu poser une seule ombre sur `.carte` au lieu de deux à
   * l'intérieur. C'était faux tant que `.carte` réservait 34 px transparents
   * pour le portrait : l'ombre aurait cerné cette bande vide. Le lot 20.C a
   * retiré le portrait, donc la fusion redeviendrait possible — on ne l'a pas
   * faite, parce que ces deux ombres portent aussi les états (focus, liaison,
   * attente), qui remplacent l'une sans toucher à l'autre. */
  --carte-ombre: 0 2px 7px rgba(23, 33, 46, .13);
  --carte-ombre-forte: 0 6px 18px rgba(23, 33, 46, .22);
  --ombre-panneau: 0 8px 26px rgba(23, 33, 46, .10);
  /* La hauteur commune d'une fiche du plan (lot 20.C).
   *
   * Toutes les fiches ont désormais exactement la même structure — un nom, une
   * maison, un lieu — donc la hauteur ne dépend plus de leur contenu et se
   * déclare ici, une fois. C'est elle qui se partage en 1/2 + 1/4 + 1/4 ;
   * `views/cartes.js` la relit sur une fiche rendue pour tracer les liens. */
  /* 144 et non 132 depuis le lot 22.B : la typographie a monté d'un cran
     partout, et les deux quarts du bas (un intitulé + une valeur) n'avaient
     plus leurs 33 px. `mesurer()` relisant cette hauteur sur une fiche réelle,
     la géométrie des connecteurs suit toute seule. */
  --carte-hauteur: 144px;
  --rayon: 9px;
  --police: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --police-titre: "Iowan Old Style", "Palatino Linotype", Georgia, serif;
}

body.sombre {
  --fond: #10141a;
  --fond-2: #171c23;
  --fond-3: #1e242d;
  --plan: #0f1319;
  --plan-grille: #1b212a;
  --bord: #2a313b;
  --bord-clair: #39424f;
  --texte: #e7e2d6;
  --texte-doux: #a2abb7;
  --texte-faible: #767f8b;
  --accent: #3fbfa9;
  --accent-fonce: #2f9e8f;
  --accent-doux: #17342f;
  --carte-fond: #1c222b;
  --carte-ombre: 0 2px 9px rgba(0, 0, 0, .55);
  --carte-ombre-forte: 0 8px 22px rgba(0, 0, 0, .62);
  --ombre-panneau: 0 8px 26px rgba(0, 0, 0, .45);
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--fond);
  color: var(--texte);
  font-family: var(--police);
  font-size: 15px;
  overflow: hidden;
}

button, input, select, textarea { font: inherit; color: inherit; }

.application { display: flex; flex-direction: column; height: 100vh; }
.corps { flex: 1; display: flex; min-height: 0; position: relative; }

/* ------------------------------------------------------------- barre haut */

/* La barre du haut passe à la ligne plutôt que de sortir de l'écran (lot 22.B).
   Elle porte une quinzaine de commandes, et la typographie ayant monté d'un
   cran, la dernière tombait hors du cadre sur un écran de 1280 : `html` est en
   `overflow: hidden`, donc ce qui dépasse n'est pas défilable — il est perdu.
   Le compte connecté et le thème valent mieux qu'une seconde ligne évitée. */
.barre-haut {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding: 9px 16px;
  background: var(--fond-2);
  border-bottom: 1px solid var(--bord);
  z-index: 20;
}

.marque { display: flex; align-items: center; gap: 10px; min-width: 190px; }
.marque-icone { font-size: 21px; color: var(--accent); }
.marque-titre { font-family: var(--police-titre); font-size: 17px; letter-spacing: .03em; }
.marque-univers { font-size: 12.5px; color: var(--texte-faible); }

.fil-ariane {
  display: flex; align-items: center; gap: 9px;
  margin: 0 auto;
  font-family: var(--police-titre);
  font-size: 16px;
}
.fil-vue { color: var(--texte-doux); }
.fil-separateur { color: var(--texte-faible); }
.fil-focus { color: var(--texte); font-weight: 600; }

/* `flex-wrap` ici aussi : sur un écran étroit, c'est ce groupe-là qui est trop
   long à lui seul, et il doit pouvoir se replier sans pousser les autres. */
.barre-controles {
  display: flex; align-items: center; gap: 9px;
  margin-left: auto; flex-wrap: wrap; justify-content: flex-end;
}

/* La barre du haut garde la taille d'avant le lot 22.B, à un demi-point près.
   Ce n'est pas un oubli : ce qu'on nous demandait de grossir, c'est ce qu'on
   **lit** — les notes, les fiches, les cartes du plan. Ici ce sont quinze
   commandes qu'on reconnaît à leur place et à leur icône, et les grossir de
   12 % les faisait déborder de l'écran sur 1280 px, où `overflow: hidden`
   ne laisse même pas défiler pour les rattraper. */
.barre-haut .bouton { font-size: 12.5px; padding: 6px 10px; }
.barre-haut .champ { font-size: 12px; }
.barre-haut .champ select { font-size: 12.5px; }

/* Enregistrement : le bouton force l'écriture, l'état dit où on en est. */
.groupe-enregistrement { display: flex; align-items: center; gap: 6px; }
.badge-attente {
  display: inline-block; margin-left: 6px; min-width: 16px; padding: 0 4px;
  border-radius: 8px; background: var(--accent); color: #fff;
  font-size: 11.5px; line-height: 15px; text-align: center;
}
.etat-ecriture { font-size: 12px; color: var(--texte-faible); white-space: nowrap; }
.etat-ecriture.attente { color: #b8842a; }
.etat-ecriture.erreur { color: #c0392b; }

.champ { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--texte-faible); }
.champ select,
.champ-recherche input {
  background: var(--fond-3);
  border: 1px solid var(--bord);
  border-radius: 8px;
  padding: 6px 9px;
  color: var(--texte);
  outline: none;
}
.champ select:focus, .champ-recherche input:focus { border-color: var(--accent); }

.bouton {
  background: var(--fond-3);
  border: 1px solid var(--bord);
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  color: var(--texte-doux);
  transition: background .15s, border-color .15s, color .15s;
}
.bouton:hover { background: var(--fond); border-color: var(--bord-clair); color: var(--texte); }
/* Un lien déguisé en bouton (le document de campagne) : même boîte, pas de
   soulignement, et la ligne de texte alignée sur celle des vrais boutons. */
a.bouton { text-decoration: none; display: inline-flex; align-items: center; line-height: 1.3; }
.bouton-primaire { border-color: var(--accent); color: var(--accent-fonce); background: var(--accent-doux); }
.bouton-primaire:hover { background: var(--accent-doux); border-color: var(--accent-fonce); }
.bouton-icone { padding: 5px 10px; }
.bouton-plat { background: transparent; border-color: transparent; }
.bouton-plat:hover { background: var(--fond-3); border-color: var(--bord); }
.bouton .ico { opacity: .75; margin-right: 4px; }
/* Les libellés de ⚙, 🛡 et ⏻ n'existent qu'au téléphone (13.B) : dans la barre
   du haut, l'infobulle suffit et la place manque.
 *
 * La règle porte sur **les boutons eux-mêmes**, pas sur l'ancêtre qui les
 * contient. Écrite en `.groupe-compte .lib`, elle cessait de s'appliquer dès
 * que `telephone.js` les déménageait — et la contrepartie est vraie aussi :
 * ces trois nœuds voyagent, ce qui les décrit doit voyager avec eux. */
#lien-admin .lib,
#lien-donnees .lib,
#btn-deconnexion .lib { display: none; }
.bouton:disabled { opacity: .45; cursor: default; }
/* Le seul bouton de la barre basse qui *écrit* : il se distingue des commandes
   de cadrage, qui ne font que regarder. */
/* L'année de campagne : lue souvent, changée rarement. Quand elle manque, elle
   se signale — sans elle, aucune fiche n'affiche d'âge. */
/* L'année dans la barre du haut (lot 22.A) : un champ, pas un bouton qui ouvre
   une fenêtre. Les deux flèches l'encadrent — avancer d'un an est le geste le
   plus fréquent d'une campagne, et il ne mérite pas d'être tapé. */
.champ-annee {
  display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--bord); border-radius: 8px;
  background: var(--fond-2); padding: 1px 4px 1px 6px;
}
.annee-icone { font-size: 14.5px; line-height: 1; margin-right: 1px; }
.champ-annee .annee-pas { width: 22px; min-width: 22px; padding: 2px 0; font-size: 14.5px; }
#saisie-annee {
  width: 64px; border: 0; background: transparent; color: inherit;
  font: inherit; font-variant-numeric: tabular-nums; text-align: center;
  outline: none; padding: 3px 0;
}
.champ-annee.sans-annee #saisie-annee::placeholder { color: var(--texte-faible); font-style: italic; }
.annee-reglage { display: flex; align-items: center; gap: 6px; }
.annee-reglage input { flex: 1; min-width: 0; text-align: center; font-variant-numeric: tabular-nums; }

.bouton-creer { color: var(--accent-fonce); font-weight: 600; }
.bouton-creer:hover { background: var(--accent-doux); border-color: var(--accent); }
.bouton-creer .ico { opacity: 1; }

/* ------------------------------------------------------------------ rail */

.rail {
  width: 246px; flex: 0 0 246px;
  background: var(--fond-2);
  border-right: 1px solid var(--bord);
  overflow-y: auto;
  padding: 14px 12px 30px;
  transition: margin-left .25s ease;
}
.rail.replie { margin-left: -246px; }

/* ------------------------------------- replier les volets (lot 28.B)
 *
 * Deux languettes, une par bord, posées sur `.corps` et non dans les volets :
 * un bouton **dans** le volet disparaît avec lui, et il faudrait alors le
 * chercher ailleurs pour rouvrir. Ici la languette ne bouge que de la largeur
 * du volet — collée à son bord quand il est ouvert, collée à l'écran quand il
 * est replié — et c'est toujours le même objet qu'on vise.
 *
 * Le ☰ de la barre du haut continue de faire la même chose pour le rail : la
 * commande est doublée, pas déplacée. On garde les deux parce qu'elles ne
 * servent pas au même moment — le ☰ quand on balaie la barre, la languette
 * quand on regarde le plan. */
.bascule-volet {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 25; width: 15px; height: 56px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--fond-2); color: var(--texte-faible);
  border: 1px solid var(--bord); cursor: pointer;
  font-size: 14px; line-height: 1;
  transition: left .25s ease, right .25s ease, color .15s, background .15s;
}
.bascule-volet:hover { background: var(--fond-3); color: var(--texte); }
/* La flèche dit **où va le volet**, donc elle se retourne quand il est
   replié : fermé, elle montre l'endroit d'où il reviendra. */
.bascule-volet span { transition: transform .25s ease; }
.bascule-gauche { left: 246px; border-left: 0; border-radius: 0 7px 7px 0; }
.bascule-droite { right: var(--fiche-largeur, 400px); border-right: 0; border-radius: 7px 0 0 7px; }
body.rail-replie .bascule-gauche { left: 0; }
body.rail-replie .bascule-gauche span { transform: rotate(180deg); }
body.panneau-replie .bascule-droite { right: 0; }
body.panneau-replie .bascule-droite span { transform: rotate(180deg); }
/* Replier vaut « sortir du champ », et non « rétrécir » : un volet à demi
   fermé ne rend pas la place qu'on lui demande. */
.panneau.replie { margin-right: calc(-1 * var(--fiche-largeur, 400px)); }
.panneau { transition: margin-right .25s ease; }
/* Repliée, la fiche ne se laisse plus étirer : la poignée serait au milieu du
   plan, sur un volet qu'on ne voit pas. */
.panneau.replie .pn-poignee { display: none; }

/* --------------------------------------------- les deux onglets (lot 21.A) */

/* Collants en haut : on replie un bloc au bas du rail, et il faut pouvoir
   changer d'onglet sans remonter tout ce qu'on vient de parcourir. */
.rail-onglets {
  position: sticky; top: -14px; z-index: 3;
  display: flex; gap: 4px;
  margin: -14px -12px 14px; padding: 12px;
  background: var(--fond-2); border-bottom: 1px solid var(--bord);
}
.rail-onglet {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font: inherit; font-size: 13.5px; font-weight: 600;
  padding: 7px 6px; min-height: 34px;
  border: 1px solid var(--bord); border-radius: 8px;
  background: var(--fond-3); color: var(--texte-faible);
  cursor: pointer;
}
.rail-onglet:hover { color: var(--texte); border-color: var(--accent); }
.rail-onglet.actif {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.rail-onglet .ico { font-size: 15px; }

.rail-bloc { margin-bottom: 20px; }
.rail-bloc h2 {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin: 0 0 8px; padding-bottom: 5px;
  border-bottom: 1px solid var(--bord);
}
/* Un titre de bloc qui porte son propre bouton : le trait passe sous les deux. */
.rail-bloc-entete {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 8px; padding-bottom: 5px; border-bottom: 1px solid var(--bord);
}
.rail-bloc-entete h2 { flex: 1; margin: 0; padding: 0; border-bottom: 0; }
.btn-entete { padding: 1px 7px; font-size: 14.5px; line-height: 1.2; }

/* ------------------------------------------- les blocs repliés (lot 21.A) */

/* Tout l'en-tête est la cible, pas le seul chevron — 220 px au lieu de 30. */
.rail-pliable > .rail-bloc-entete { cursor: pointer; user-select: none; }
.rail-pliable > .rail-bloc-entete:hover h2 { color: var(--texte); }
/* Un vrai bouton, pas une flèche de 10 px : c'est un geste qu'on refait sans
   arrêt, au doigt comme à la souris. 30 px est le minimum qu'on vise sans y
   penser. */
.rail-plier {
  flex: 0 0 auto;
  width: 30px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  font: inherit; font-size: 14.5px; line-height: 1;
  border: 1px solid var(--bord); border-radius: 7px;
  background: var(--fond-3); color: var(--texte-faible);
  cursor: pointer;
}
.rail-plier:hover { border-color: var(--accent); color: var(--texte); }
.rail-bloc.plie > .rail-bloc-contenu { display: none; }
/* Replié, le bloc n'est plus qu'un titre : il n'a plus besoin de son écart. */
.rail-bloc.plie { margin-bottom: 10px; }
/* Les axes de filtre, en pastilles : de quoi rappeler un filtre sur mesure
   sans repasser par la liste déroulante du haut. */
.axes-filtre { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 8px; }
.axe-puce {
  font: inherit; font-size: 12px; line-height: 1.5;
  padding: 2px 8px; border-radius: 11px; cursor: pointer;
  border: 1px solid var(--bord); background: var(--fond-3); color: var(--texte-faible);
}
.axe-puce:hover { border-color: var(--accent); color: var(--texte); }
.axe-puce.actif {
  background: var(--accent-doux); border-color: var(--accent); color: var(--texte);
  font-weight: 600;
}
.axe-puce.sur-mesure::before { content: '⚙ '; opacity: .7; }
.rail-aide { font-size: 12px; color: var(--texte-faible); margin: -2px 0 7px; }

.liste-vues { list-style: none; margin: 0; padding: 0; }
.liste-vues li {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: 14.5px;
}
.liste-vues li:hover { background: var(--fond-3); }
.liste-vues li.actif { background: var(--accent); color: #fff; }
.liste-vues li.actif .vue-icone { color: #fff; }
.vue-icone { font-size: 16px; color: var(--accent); width: 18px; text-align: center; }

.legende { list-style: none; margin: 0; padding: 0; }
.legende li {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: 6px; cursor: pointer;
  font-size: 13.5px; user-select: none;
}
.legende li:hover { background: var(--fond-3); }
.legende li.eteint { opacity: .35; }
.legende li .nombre { margin-left: auto; color: var(--texte-faible); font-size: 12px; font-variant-numeric: tabular-nums; }
.legende-trait { width: 20px; height: 0; border-top-width: 3px; border-top-style: solid; border-radius: 2px; flex: 0 0 20px; }
.legende-pastille { width: 11px; height: 11px; border-radius: 3px; flex: 0 0 11px; }
.legende li .fleche { color: var(--texte-faible); font-size: 11.5px; }

.options { display: flex; flex-direction: column; gap: 7px; }
.option { display: flex; align-items: center; gap: 8px; font-size: 13.5px; cursor: pointer; }
.option input { accent-color: var(--accent); }

/* Le compte-rendu du plan, dans le coin bas gauche de la scène (lot 21.A).
   `pointer-events: none` : il flotte au-dessus du plan, et un clic qui tombe
   dessus doit atteindre ce qu'il y a dessous — c'est un écriteau, pas un
   bouton. */
.stats-plan {
  position: absolute; left: 14px; bottom: 22px; z-index: 5;
  max-width: 250px; padding: 4px 9px;
  border: 1px solid var(--bord); border-radius: 9px;
  background: var(--fond-2);
  font-size: 12.5px; line-height: 1.35; color: var(--texte-doux);
  pointer-events: none;
}
.stats-plan b { color: var(--texte); font-variant-numeric: tabular-nums; }
.stats-filtre { color: var(--texte-faible); }

/* ----------------------------------------------------------------- scène */

.scene {
  flex: 1; position: relative; min-width: 0;
  background: var(--plan);
  background-image:
    radial-gradient(var(--plan-grille) 1px, transparent 1px);
  background-size: 26px 26px;
}

/* `touch-action: none` : le doigt déplace le plan, il ne fait pas défiler la
   page. Contrepartie assumée — le menu long-press du navigateur disparaît, et
   c'est `surMenuContextuel` qui le remplace (voir js/dom.js). */
.plan {
  position: absolute; inset: 0; overflow: hidden; cursor: grab;
  touch-action: none; -webkit-tap-highlight-color: transparent;
}
.plan:active { cursor: grabbing; }
/* `will-change` dit au navigateur que ce nœud-là va bouger sans arrêt : il lui
   réserve sa propre couche, et le panoramique n'est plus qu'un déplacement de
   couche — sans re-tramer les 21 mégapixels du monde. Le zoom, lui, change
   l'échelle et reste un vrai retraçage : c'est pourquoi le niveau de détail
   ci-dessous compte plus que cette ligne. */
.monde {
  position: absolute; left: 0; top: 0; transform-origin: 0 0;
  will-change: transform;
}
.liens { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.cartes { position: absolute; left: 0; top: 0; }

/* ------------------------------------------- formes de fond (lot 20.D)
 *
 * Montée en premier dans le monde, donc **sous** les traits et sous les fiches,
 * et transformée avec eux : une forme zoome et se déplace comme le reste.
 *
 * `pointer-events: none` au repos, et c'est le cœur de l'affaire. Une forme
 * couvre de la surface ; si elle attrapait les clics en permanence, un
 * rectangle tracé autour du Nord empêcherait de faire glisser le plan dans tout
 * le Nord. Le mode dessin (`.actives`) les rend saisissables le temps qu'on
 * s'en occupe, puis leur rend leur transparence. */
.formes { position: absolute; left: 0; top: 0; pointer-events: none; }
.forme {
  position: absolute;
  box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  /* `overflow: visible`, et c'est un changement du lot 27.C. La forme portait
     `hidden` pour retenir son texte ; mais ses poignées débordent volontairement
     de quelques pixels (`-5px`), et `hidden` les **rognait de moitié** — celle
     du coin bas-droit, seule à exister alors, se réduisait à un onglet de 6 px
     par 6, à peu près invisible et à peu près invisable. C'est le texte qu'il
     faut retenir, pas les prises : le débordement se règle donc un cran plus
     bas, sur `.forme-texte`. */
  overflow: visible;
}
/* Attention au nom : `forme-texte` désigne **deux choses**. Sur un `.forme`,
   c'est le genre de la forme (`forme forme-texte`, une zone de texte) ; à
   l'intérieur, c'est le bloc de texte de n'importe quelle forme. D'où le
   `>` : sans lui, une règle écrite pour le bloc s'applique aussi à la zone de
   texte elle-même. Le lot 27.C s'y est fait prendre — un `max-width: 100%`
   destiné au bloc a ramené la forme entière à 20 px de large, `100%` d'un
   parent sans dimension. Les règles voisines évitent le piège autrement, en
   passant par `.formes.actives .forme .forme-texte`. */
.forme > .forme-texte {
  padding: 6px 10px; text-align: center; line-height: 1.25;
  white-space: pre-wrap; word-break: break-word;
  outline: none; /* `contenteditable` pose un cadre système par-dessus */
  /* Ce que `.forme` ne retient plus depuis qu'elle laisse dépasser ses
     poignées : un texte plus long que sa boîte s'arrête au bord au lieu de
     déborder sur le plan. */
  max-width: 100%; max-height: 100%; overflow: hidden;
}
/* Le texte ne se saisit que sur la forme **déjà choisie** : sinon le premier
   clic poserait un curseur au lieu de la sélectionner, et on ne pourrait plus
   l'attraper par le milieu pour la déplacer (lot 22.C). */
.formes.actives .forme .forme-texte { pointer-events: none; }
.formes.actives .forme.choisie .forme-texte { pointer-events: auto; cursor: text; }
.formes.actives .forme { pointer-events: auto; cursor: move; }
.formes.actives .forme:hover { outline: 1px dashed var(--accent); outline-offset: 2px; }
.formes.actives .forme.choisie { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Prises pour le copier-coller (lot 26.B). Le liseré ne dépend **pas** de
   `.actives` : on prend des formes hors du mode dessin, c'est tout le propos.
   Pointillé comme celui des fiches, parce que c'est la même main. */
.forme.prise { outline: 2px dashed var(--accent); outline-offset: 3px; }
/* Et une forme prise **redevient saisissable**, elle seule (lot 28.A).
   C'est la seule entorse au principe du lot 20.D, et elle est étroite : une
   forme au repos reste inerte, c'est ce qui garde le plan traversable partout.
   Mais une forme qu'on vient de désigner d'un cadre n'est plus au repos — on
   l'a montrée du doigt, et attendre d'elle qu'elle attrape le glisser est
   exactement ce à quoi on s'attend. Sans cette ligne, une sélection qui ne
   contient que des formes ne pourrait pas se déplacer du tout, faute d'une
   fiche par laquelle l'attraper. */
.forme.prise { pointer-events: auto; cursor: move; }
/* ── Le mode dessin fait taire les prises de clic des liens (lot 27.C) ──────
 *
 * Les couches de `.monde` s'empilent dans cet ordre : les formes au fond, le
 * <svg> des liens au-dessus, les fiches par-dessus tout. Or `.lien-prise` est
 * une prise **invisible** de 13 px de large (voir plus bas) et elle reste
 * active en permanence : là où une relation passe au-dessus d'une forme, c'est
 * le lien qui reçoit le `mousedown`, jamais la forme.
 *
 * Sur un plan peuplé, ce n'est pas une gêne théorique. Mesuré sur le monde de
 * démonstration : d'une zone de texte de 220 × 60, **104 points sur 320
 * étaient pris par des connecteurs** — un tiers de sa surface, aux endroits
 * qui changent quand la mise en page bouge. Un grand rectangle garde presque
 * toujours de quoi s'attraper ailleurs, une zone de texte non : d'où le
 * « on ne peut pas déplacer le texte, ça n'agit pas comme un rectangle ».
 *
 * Pendant qu'on dessine, ce ne sont donc pas les liens qu'on vise. Ils
 * retrouvent leurs prises en sortant du mode. Les fiches, elles, gardent les
 * leurs : elles sont **visibles**, donc on sait qu'on clique dessus — ce qui
 * était traître ici, c'est de perdre un geste contre ce qu'on ne voit pas. */
.plan.formes-en-cours .lien-prise { pointer-events: none; }

/* Les poignées, tout autour de la forme choisie. Invisibles hors mode dessin :
   elles n'ont rien à signaler quand on ne peut pas s'en servir.
   Le lot 20.D n'en posait qu'une, au coin bas-droit, et le geste ne savait
   qu'agrandir vers la droite et vers le bas : impossible de reprendre de la
   place par le haut sans déplacer la forme d'abord. */
.forme-poignee, .forme-deplace { display: none; }
.formes.actives .forme-poignee {
  display: block;
  position: absolute;
  width: 11px; height: 11px; border-radius: 3px;
  background: var(--carte-fond); border: 2px solid var(--accent);
}
.forme-poignee-nw { left: -5px; top: -5px; cursor: nwse-resize; }
.forme-poignee-n { left: 50%; top: -5px; margin-left: -5px; cursor: ns-resize; }
.forme-poignee-ne { right: -5px; top: -5px; cursor: nesw-resize; }
.forme-poignee-e { right: -5px; top: 50%; margin-top: -5px; cursor: ew-resize; }
.forme-poignee-se { right: -5px; bottom: -5px; cursor: nwse-resize; }
.forme-poignee-s { left: 50%; bottom: -5px; margin-left: -5px; cursor: ns-resize; }
.forme-poignee-sw { left: -5px; bottom: -5px; cursor: nesw-resize; }
.forme-poignee-w { left: -5px; top: 50%; margin-top: -5px; cursor: ew-resize; }

/* La poignée de déplacement, posée **au-dessus** de la forme et non dedans.
   Elle ne sert qu'à la forme choisie, et c'est là qu'elle est indispensable :
   une forme choisie rend son texte saisissable pour qu'on puisse écrire dedans
   (lot 22.C), et une zone de texte est presque entièrement couverte par le
   sien. Sans cette prise, on ne pouvait plus l'attraper que par ses marges.
   Au-dessus plutôt que dans un coin : elle ne cache pas le texte, et elle ne
   se dispute pas la place avec les huit poignées de taille. */
.formes.actives .forme.choisie .forme-deplace {
  display: flex; align-items: center; justify-content: center;
  position: absolute; left: 50%; top: -24px; transform: translateX(-50%);
  width: 30px; height: 18px; border-radius: 5px;
  background: var(--accent); color: #fff;
  font-size: 11px; line-height: 1; letter-spacing: .12em;
  cursor: move; user-select: none;
}
/* Loin, une poignée de 11 px en fait deux : elle ne se vise plus, et son
   liseré brouille la forme. Même parti que `.carte-poignee` (lot 16.I) — on
   les retire, le glisser par le corps de la forme reste. */
.plan.loin .forme-poignee, .plan.loin .forme-deplace { display: none; }

/* La surface de tracé : posée sur le plan et non dans le monde, pour couvrir
   l'écran sans subir le zoom. Elle n'existe que pendant qu'un outil est armé. */
.formes-capture { position: absolute; inset: 0; z-index: 7; cursor: crosshair; }
.forme-apercu {
  position: absolute; pointer-events: none;
  border: 2px dashed var(--accent);
  background: rgba(47, 158, 143, .08);
  border-radius: 10px;
}
.forme-apercu.rond { border-radius: 50%; }

.scene-vide {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--texte-faible); font-size: 14.5px; pointer-events: none; z-index: 4;
}

.barre-basse {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 5px;
  background: var(--fond-2);
  border: 1px solid var(--bord);
  border-radius: 12px;
  padding: 5px 8px;
  box-shadow: var(--ombre-panneau);
  /* Au-dessus de `.formes-capture` (7) : sinon, un outil armé pose une surface
     de tracé invisible par-dessus la barre, et cliquer « ◯ » ou ressortir du
     mode dessine un rectangle au lieu d'appuyer sur le bouton (lot 22.C). */
  z-index: 8;
}
.separateur-vertical { width: 1px; height: 20px; background: var(--bord); margin: 0 3px; }
#zoom-curseur { width: 116px; accent-color: var(--accent); }

.infobulle {
  position: absolute; z-index: 15; pointer-events: none;
  background: var(--fond-2);
  border: 1px solid var(--bord-clair);
  border-radius: 8px; padding: 8px 11px;
  box-shadow: var(--ombre-panneau);
  max-width: 280px; font-size: 13.5px; line-height: 1.45;
}
.astuce-scene {
  position: absolute; left: 50%; top: 14px; transform: translateX(-50%);
  z-index: 7; pointer-events: none;
  background: var(--accent-doux); color: var(--accent-fonce);
  border: 1px solid var(--accent); border-radius: 20px;
  padding: 6px 14px; font-size: 13.5px; line-height: 1.3;
  box-shadow: var(--ombre-panneau); max-width: 82%; text-align: center;
}

.infobulle .ib-titre { font-weight: 600; font-size: 14px; }
.infobulle .ib-sous { color: var(--texte-faible); }
.infobulle .ib-ligne { margin-top: 3px; color: var(--texte-doux); }

/* ---------------------------------------------------------------- cartes */

.cartes.sans-transition .carte { transition: none !important; }

.carte {
  position: absolute;
  display: flex; flex-direction: column;
  /* Une hauteur **ferme**, et non un minimum.
   *
   * Toutes les fiches ont la même structure depuis le lot 20.C : rien n'a plus
   * de raison de dépasser. Et il faut qu'elle soit ferme pour que les
   * `flex-basis` en pourcentage ci-dessous aient un tout à quoi se référer —
   * avec un simple `min-height`, la hauteur reste indéfinie pendant le calcul,
   * les blocs se rangent à la taille de leur contenu, et la « moitié » du nom
   * valait 39 %. */
  height: var(--carte-hauteur);
  cursor: pointer;
  /* Un appui long sur une fiche ouvre notre menu : ni sélection de texte, ni
     bulle « copier », ni loupe iOS ne doivent venir s'y superposer. */
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  transition: left .55s cubic-bezier(.4, 0, .2, 1),
              top .55s cubic-bezier(.4, 0, .2, 1),
              opacity .25s ease, filter .2s ease;
}
.carte.masquee { display: none; }
.carte.hors-recherche { opacity: .28; }
.carte.hors-filtre { opacity: .22; filter: saturate(.35); }
.carte.hors-filtre:hover { opacity: .6; }

/* Le liseré choisi dans la fiche (lot 20.B).
 *
 * Un `outline` et non une `border` : une bordure pousserait la fiche vers
 * l'intérieur de trois pixels, et les traits de liaison sont tracés d'après
 * les boîtes calculées en JavaScript, qui, elles, ne bougeraient pas. Le
 * contour est dessiné **hors** du flux : la géométrie du plan ne change pas.
 *
 * `outline-offset: 2px` le décolle du bandeau, sinon il se confondrait avec la
 * couleur de la maison — c'est précisément ce qu'il ne doit pas faire. */
.carte.bordee {
  outline: 3px solid var(--couleur-bordure);
  outline-offset: 2px;
  border-radius: 7px;
}
/* Un mort porte sa carte en sourdine. Par une opacité sur le bandeau, et non
   par `filter` : un filtre force le navigateur à ouvrir une surface de rendu à
   part pour l'élément, et il y en avait **quarante-sept** dans Westeros. */
.carte.morte .carte-entete { opacity: .72; }
.carte.morte .carte-nom::after {
  content: ' †';
  opacity: .8; font-weight: 400;
}

/* La moitié haute de la fiche : le nom, et rien d'autre (lot 20.C).
 *
 * `flex: 0 0 50%` — une part déclarée, ni élastique ni compressible. Le corps
 * en dessous prend l'autre moitié et la partage à son tour en deux : la moitié
 * et les deux quarts demandés sont écrits, pas obtenus par équilibre entre des
 * contenus qui n'ont pas la même taille. */
.carte-entete {
  flex: 0 0 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--couleur-carte);
  color: #fff;
  text-align: center;
  padding: 6px 9px;
  border-radius: 6px 6px 0 0;
  font-size: 18px; font-weight: 700; line-height: 1.15;
  letter-spacing: -.01em;
  box-shadow: var(--carte-ombre);
}
/* Un satellite garde la taille commune : seul son bandeau est plus discret. */
.carte.satellite .carte-entete { opacity: .88; }
/* Personnage joué : liseré à la couleur du joueur et une couronne discrète —
   sur le plan, une fiche avec quelqu'un derrière ne se confond pas avec un PNJ. */
.carte.joueur .carte-corps { border-color: var(--couleur-joueur); }
.carte.joueur .carte-entete {
  box-shadow: var(--carte-ombre), inset 0 0 0 2px var(--couleur-joueur);
}
.carte.joueur .carte-entete::after {
  content: '♛';
  position: absolute; top: 2px; right: 6px;
  font-size: 12.5px; opacity: .85;
}
.carte.joueur .carte-entete { position: relative; }

/* Chef de maison et héritier : cadre plus appuyé et une marque au coin. La
   taille, elle, ne bouge pas — la mise en page ne doit pas dépendre de qui
   règne, sinon nommer un chef ferait sauter tout le plan. La couronne du
   joueur est à droite, la marque de rang à gauche : elles coexistent. */
.carte.rang-chef { --rang-couleur: #c9a227; }
.carte.rang-heritier { --rang-couleur: #8fa9c4; }
.carte.rang .carte-entete {
  box-shadow: var(--carte-ombre), inset 0 0 0 2px var(--rang-couleur);
}
.carte.rang .carte-corps { border-width: 0 2px 2px; border-color: var(--rang-couleur); }
/* Depuis le lot 20.C il n'y a plus de portrait au-dessus du bandeau : la
   marque de rang remonte donc dans le coin haut-gauche du bandeau lui-même,
   en face de la couronne du joueur. */
.carte-rang {
  position: absolute; top: 4px; left: 4px; z-index: 3;
  width: 17px; height: 17px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--carte-fond); color: var(--rang-couleur);
  border: 1px solid var(--rang-couleur);
  font-size: 11.5px; line-height: 1;
  box-shadow: var(--carte-ombre);
}
/* De loin, les corps de fiche s'effacent : les rangs, eux, restent lisibles —
   c'est justement à cette échelle qu'on cherche qui tient quoi. */
.plan.loin .carte-rang { opacity: 1; }
/* Deux lignes de nom au maximum : au-delà, un nom à rallonge repousserait les
   deux faits hors de la fiche au lieu de se couper. */
.carte-nom {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* La moitié basse : deux rangées de deux cases — la maison et le rôle qu'on y
   tient, puis la région et la ville où l'on se trouve (lot 21.D). */
.carte-corps {
  flex: 0 0 50%; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--carte-fond);
  border: 1px solid var(--bord);
  border-top: 0;
  border-radius: 0 0 6px 6px;
  padding: 0;
  box-shadow: var(--carte-ombre);
  transition: opacity .2s ease;
}
/* Une rangée du corps : la moitié de sa hauteur, deux cases côte à côte. Le
   trait ne sépare que la seconde rangée — posé sur les deux, il doublerait la
   bordure du bandeau juste au-dessus. */
.carte-rangee {
  flex: 0 0 50%; min-height: 0;
  display: flex;
}
.carte-rangee + .carte-rangee { border-top: 1px solid var(--bord); }
/* Chacune des quatre cases. `min-width: 0` est indispensable : sans lui, la
   taille minimale automatique d'un élément flex est celle de son contenu, et un
   nom de ville un peu long pousserait sa voisine hors de la fiche au lieu de se
   faire couper. */
.carte-fait {
  flex: 1 1 50%; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 2px 8px;
  overflow: hidden;
}
.carte-fait + .carte-fait { border-left: 1px solid var(--bord); }
.carte-fait-lib {
  font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--texte-faible); line-height: 1.2;
}
.carte-fait-val {
  font-size: 13.5px; color: var(--texte); line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.carte:hover .carte-corps,
.carte:hover .carte-entete { box-shadow: var(--carte-ombre-forte); }
.carte.focus .carte-corps { border-color: var(--accent); }
.carte.focus .carte-entete { box-shadow: 0 0 0 2px var(--accent), var(--carte-ombre-forte); }
.carte.focus .carte-corps { box-shadow: 0 0 0 2px var(--accent), var(--carte-ombre-forte); }

/* poignée de liaison : apparaît au survol, se glisse vers une autre fiche */
.carte-poignee {
  position: absolute; left: 50%; bottom: -11px; transform: translateX(-50%);
  width: 20px; height: 20px; border-radius: 50%; padding: 0;
  border: 1px solid var(--bord-clair); background: var(--carte-fond);
  color: var(--texte-doux); font-size: 15px; line-height: 1; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
  cursor: crosshair; opacity: 0; z-index: 3;
  /* Pas d'ombre : ce bouton est transparent au repos, et il y en avait une par
     fiche — soixante-sept halos flous dessinés sous un bouton qu'on ne voit
     pas. Affiché, c'est un rond de 20 px : son bord suffit à le détacher. */
  transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.carte:hover .carte-poignee,
.carte.focus .carte-poignee,
.carte.source-liaison .carte-poignee { opacity: 1; }
.carte-poignee:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.plan.loin .carte-poignee { display: none; }

.carte.cible-liaison .carte-entete,
.carte.cible-liaison .carte-corps {
  box-shadow: 0 0 0 2px var(--accent), var(--carte-ombre-forte);
}

/* fiche armée par Maj + clic, en attente de sa destination */
.carte.en-attente .carte-entete,
.carte.en-attente .carte-corps {
  box-shadow: 0 0 0 2px var(--accent), var(--carte-ombre-forte);
}
/* La pulsation portait sur le portrait, qui n'existe plus (lot 20.C) : elle
   passe sur le bandeau, qui est la partie colorée de la fiche et se remarque
   à la même distance. */
.carte.en-attente .carte-entete { animation: pulsation 1.4s ease-in-out infinite; }
@keyframes pulsation {
  0%, 100% { box-shadow: 0 0 0 0 rgba(47, 158, 143, .55), var(--carte-ombre); }
  50%      { box-shadow: 0 0 0 7px rgba(47, 158, 143, 0), var(--carte-ombre); }
}

/* déplacement manuel (Ctrl + glisser) */
.plan.en-deport { cursor: grabbing; }
.carte.en-deport { z-index: 5; }
.carte.en-deport .carte-entete,
.carte.en-deport .carte-corps { box-shadow: var(--carte-ombre-forte); }

/* Plusieurs fiches en main (lot 22.D). Un `outline`, comme le liseré du lot
   20.B et pour la même raison : une bordure décalerait la fiche de deux pixels
   alors que les connecteurs sont tracés d'après des boîtes calculées en
   JavaScript, qui, elles, ne bougeraient pas. */
.carte.selectionnee { outline: 2px dashed var(--accent); outline-offset: 3px; }
.bande-selection {
  position: absolute; z-index: 6; pointer-events: none;
  border: 1px solid var(--accent); border-radius: 4px;
  background: rgba(47, 158, 143, .10);
}

/* Le bouton « ⟳ Recharger » pendant qu'il travaille (lot 26.A). Seule l'icône
   tourne : faire tourner le bouton entier emporterait son libellé. */
#btn-recharger .ico { display: inline-block; }
#btn-recharger.tourne .ico { animation: tourne-ico .9s linear infinite; }
#btn-recharger[disabled] { opacity: .7; }
@keyframes tourne-ico { to { transform: rotate(360deg); } }

/* niveau de détail : au loin, seuls les bandeaux restent lisibles */
/* Niveau de détail : de loin, on ne dessine plus ce qu'on ne peut pas lire.
 *
 * `opacity: .18` pâlissait le corps de la fiche **et le peignait quand même**,
 * en ouvrant au passage une couche de composition par carte. Or ces corps
 * portent 41 % des nœuds du plan (629 sur 1545, mesuré sur Westeros), et ce
 * sont ceux qui portent du texte — le plus cher à tramer.
 *
 * On cache les **enfants**, pas la boîte : `display: none` sur le corps ferait
 * rétrécir la carte, alors que les traits de liaison sont tracés d'après les
 * boîtes calculées en JavaScript, qui ne bougeraient pas. Les liens
 * finiraient dans le vide. La boîte reste donc, vide et pâle ; ce qu'elle
 * contenait s'en va. */
.plan.loin .carte-corps { background: var(--carte-fond); opacity: .55; }
.plan.loin .carte-corps > * { display: none; }

/* ----------------------------------------------------------------- liens */

/* Épaisseurs relevées au lot 28.A — « les liens de relations sont trop
   petits », et c'était mesurable plutôt que discutable. Un trait vit dans le
   monde, donc il **subit le zoom** : à 0,3× — un cadrage courant dès qu'on
   regarde plus d'une maison — un `.lien-social` de 1,5 px n'en faisait plus
   que 0,45, sous le pixel, et avec une opacité de 0,55 par-dessus. Il ne
   restait littéralement rien à voir.
   Les quatre familles montent ensemble et gardent leur hiérarchie relative :
   l'union reste le trait le plus gras, la fratrie le plus discret. L'opacité
   du social passe de .55 à .72 — assez pour se lire de loin, pas assez pour
   concurrencer la filiation, qui est la structure du plan. */
.lien-famille { fill: none; stroke-width: 2.6px; stroke-linejoin: round; stroke-linecap: round; }
.lien-union { fill: none; stroke-width: 3.2px; stroke-linecap: round; }
.lien-fratrie { fill: none; stroke-width: 2.4px; stroke-dasharray: 7 5; stroke-linejoin: round; }
.lien-social {
  fill: none; stroke-width: 2.4px; opacity: .72;
  transition: opacity .2s, stroke-width .15s;
}
.lien-social.survol { opacity: 1; stroke-width: 3.4px; }
.lien-social.attenue { opacity: .1; }

/* Filtre de type actif : ce qui reste doit sauter aux yeux. */
/* Révolu : le trait s'efface sans disparaître. On doit pouvoir lire « ils
   ont été alliés » sans confondre avec « ils le sont ». */
.lien-social.revolu { opacity: .45; stroke-width: 2px; }
.lien-social.revolu.survol { opacity: .8; }
.option-revolus { margin: 8px 0 2px; }

/* Pastille d'un lien : l'emoji posé sur le trait, sur une rondelle de fond qui
   coupe le trait pour rester lisible.
   `pointer-events: none` n'est pas cosmétique — la pastille est dessinée
   au-dessus du trait, et sans ça elle percerait un trou dans sa zone de survol. */
.lien-pastille {
  font-size: 14px;
  pointer-events: none;
  user-select: none;
}
.lien-pastille-fond {
  fill: var(--fond);
  pointer-events: none;
}
.lien-pastille.attenue, .lien-pastille-fond.attenue { opacity: .15; }
.lien-pastille.revolu { opacity: .5; }

/* Saisie de la pastille : un petit champ libre suivi des raccourcis. */
.ligne-emoji { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.champ-emoji {
  width: 44px; flex: 0 0 44px; text-align: center;
  font-size: 16px; padding: 4px 2px;
}
.bouton-emoji {
  font-size: 15px; line-height: 1; padding: 4px 5px; min-width: 0;
}
.bouton-emoji.actif { border-color: var(--accent); background: var(--accent-doux); }

.plan.filtre-liens .lien-social { opacity: 1; stroke-width: 3.2px; }
.plan.filtre-liens .lien-social.survol { stroke-width: 4.2px; }
.plan.filtre-liens .lien-social.attenue { opacity: .35; }
.plan.filtre-liens .lien-famille { stroke-width: 3px; }
.plan.filtre-liens .lien-union { stroke-width: 3.4px; }
.plan.filtre-liens .lien-fratrie { stroke-width: 2.8px; }

/* Prises de clic : invisibles, épaisses, empilées au-dessus du dessin.
   Un connecteur de 1,7 px est intraçable à la souris ; celles-ci font 13 px. */
.lien-prise {
  fill: none; stroke: transparent; stroke-width: 13px;
  stroke-linejoin: round; stroke-linecap: round;
  pointer-events: stroke; cursor: pointer;
}

/* Élastique de création de lien */
.couche-guide { opacity: 0; }
.couche-guide.visible { opacity: 1; }
.guide-trait {
  fill: none; stroke: var(--accent); stroke-width: 2.2px;
  stroke-dasharray: 7 5; stroke-linecap: round;
}
.guide-point { fill: var(--accent); }
.plan.en-liaison, .plan.en-liaison .carte { cursor: crosshair; }

/* --------------------------------------------------------------- panneau */

/* Les cases de « Copier-coller » (lot 23.G, sous-cases au lot 25). */
.option-copie {
  display: flex; align-items: center; gap: 7px;
  padding: 4px 2px; font-size: 12.5px; cursor: pointer;
}
.option-copie input { accent-color: var(--accent); }
/* Les sous-cases d'une catégorie : en retrait, sous un filet, pour qu'on voie
   d'un coup d'œil qu'elles dépendent de celle du dessus. Éteintes, elles
   restent lisibles — on doit comprendre ce qu'on rallumerait. */
.option-copie-sous {
  margin: 0 0 2px 9px; padding-left: 10px;
  border-left: 2px solid var(--bord-clair);
}
.option-copie-sous .option-copie { font-size: 12px; padding: 3px 2px; }
.option-copie-sous.eteint { opacity: .5; }
.option-copie-sous.eteint .option-copie { cursor: default; }

/* Les amis (lot 23.C). Trois listes dans un bloc, séparées par des intertitres
   plutôt que par trois cadres : ce sont les mêmes gens à trois moments. */
.liste-amis { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.ami {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 6px; border-radius: 8px;
}
.ami:hover { background: var(--fond-3); }
.ami-corps { flex: 1; min-width: 0; }
.ami-nom { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ami-meta {
  font-size: 11.5px; color: var(--texte-faible);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ami-actions { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.ami-actions .bouton-plat { padding: 3px 8px; font-size: 12px; }
.ami-entete {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--texte-faible); margin: 8px 0 2px;
}
.ami-entete:first-child { margin-top: 0; }
.ami-vide { font-size: 12.5px; color: var(--texte-faible); line-height: 1.45; }
/* Une demande reçue attend un geste : elle se voit avant les autres. */
.ami-recue { background: var(--accent-doux); }

/* La fiche de droite (lot 22.B) et le carnet en volet (lot 27.A) s'étirent
   par le même geste : une poignée sur leur bord gauche, une largeur retenue
   d'une séance à l'autre. `installerEtirement` (js/main.js) porte le
   mécanisme une seule fois ; ce qui reste propre à chacun, c'est sa variable
   CSS, sa clé de `localStorage` et sa borne basse.
   La fiche part plus large qu'avant le lot 22.B — 330 px pour un bloc de
   notes de six lignes, c'était trop peu. La valeur par défaut de chacun vit
   dans sa propre règle (celle du carnet est plus bas, sur `.volet-carnet`),
   pour que la page s'affiche juste avant même que le JavaScript ait parlé. */
.panneau {
  width: var(--fiche-largeur, 400px);
  flex: 0 0 var(--fiche-largeur, 400px);
  background: var(--fond-2);
  border-left: 1px solid var(--bord);
  display: flex; flex-direction: column;
  min-height: 0;
  position: relative;
}
/* Six pixels de large, débordant de trois sur le plan : de quoi l'attraper
   sans viser. Invisible au repos — c'est le bord du panneau (ou du volet du
   carnet, qui porte la même classe et le même bord gauche à saisir).
   Les deux poignées partagent cette classe, donc l'état « en cours
   d'étirement » se scope par volet (`.panneau .pn-poignee`,
   `.volet-carnet .pn-poignee`) : sans ce scope, tirer l'une allumerait aussi
   le liseré de l'autre, restée immobile. */
.pn-poignee {
  position: absolute; left: -3px; top: 0; bottom: 0; width: 6px;
  cursor: col-resize; z-index: 3; background: transparent;
}
.pn-poignee:hover,
body.fiche-en-etirement .panneau .pn-poignee,
body.carnet-en-etirement .volet-carnet .pn-poignee { background: var(--accent); opacity: .55; }
body.fiche-en-etirement,
body.carnet-en-etirement { cursor: col-resize; user-select: none; }

.pn-onglets { display: flex; border-bottom: 1px solid var(--bord); flex: 0 0 auto; }
.onglet {
  flex: 1; background: transparent; border: 0; border-bottom: 2px solid transparent;
  padding: 10px 8px; cursor: pointer; color: var(--texte-faible); font-size: 14px;
}
.onglet:hover:not(:disabled) { color: var(--texte); }
.onglet.actif { color: var(--accent-fonce); border-bottom-color: var(--accent); font-weight: 600; }
.onglet:disabled { opacity: .4; cursor: default; }

.pn-vue { flex: 1; overflow-y: auto; min-height: 0; }

/* liste des personnes */
.liste-entete {
  position: sticky; top: 0; z-index: 3;
  background: var(--fond-2); border-bottom: 1px solid var(--bord);
  padding: 10px 12px; display: flex; flex-direction: column; gap: 8px;
}
.champ-recherche { position: relative; display: flex; align-items: center; }
.champ-recherche .loupe {
  position: absolute; left: 9px; color: var(--texte-faible); font-size: 15px; pointer-events: none;
}
.champ-recherche input { width: 100%; padding-left: 26px; }
.champ-groupe { justify-content: space-between; }
.champ-groupe select { flex: 1; margin-left: 8px; }

.liste-personnes { padding: 6px; }
.groupe-titre {
  font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--texte-faible); padding: 10px 8px 5px; display: flex; align-items: center; gap: 7px;
}
.groupe-titre .barre-couleur { width: 10px; height: 10px; border-radius: 3px; }
.groupe-titre .nombre { margin-left: auto; }
/* Champ à suggestions (le lieu) : la liste se pose par-dessus la fiche. */
.champ-suggere { position: relative; }
.champ-suggere input { width: 100%; }
.suggestions {
  position: absolute; left: 0; right: 0; top: calc(100% + 2px); z-index: 30;
  list-style: none; margin: 0; padding: 4px;
  max-height: 210px; overflow-y: auto;
  background: var(--fond-2); border: 1px solid var(--bord-clair);
  border-radius: 8px; box-shadow: var(--ombre-panneau);
}
.suggestions li {
  padding: 5px 7px; border-radius: 6px; cursor: pointer;
  display: flex; flex-direction: column; gap: 1px;
}
.suggestions li:hover, .suggestions li.survol { background: var(--accent-doux); }
.sug-valeur { font-size: 13.5px; }
.sug-detail { font-size: 11.5px; color: var(--texte-faible); }

/* Le rail des joueurs : encadré à part, ce ne sont pas des personnages. */
.bloc-joueurs {
  border: 1px solid var(--accent);
  border-radius: 10px;
  padding: 10px 11px;
  background: var(--fond-3);
}
.liste-joueurs { list-style: none; margin: 0; padding: 0; }
.joueur-rail {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 6px; border-radius: 7px; cursor: pointer;
}
.joueur-rail:hover { background: var(--fond); }
.joueur-rail.actif { background: var(--accent-doux); box-shadow: inset 0 0 0 1px var(--accent); }
.joueur-rail-corps { flex: 1; min-width: 0; }
.joueur-rail-nom { font-size: 13.5px; }
.joueur-rail-perso { font-size: 11.5px; color: var(--texte-faible); }
/* Raccourci vers le personnage joué : discret, net au survol de la ligne. */
.joueur-rail-fiche { flex: 0 0 auto; opacity: .45; padding: 0 5px; line-height: 1.6; }
.joueur-rail:hover .joueur-rail-fiche, .joueur-rail.actif .joueur-rail-fiche { opacity: 1; }
.liste-joueurs .vide { font-size: 12.5px; color: var(--texte-faible); font-style: italic; padding: 4px 6px; }

/* Mode « regard d'un joueur » : la ligne porte son curseur d'humeur. */
.item-personne.avec-humeur { align-items: flex-start; flex-wrap: wrap; }
.item-personne.avec-humeur .humeur { margin-left: auto; }
.item-personne.avec-humeur .humeur-points { gap: 3px; }
.item-personne.avec-humeur .humeur-point { width: 13px; height: 13px; }
.item-personne.avec-humeur .humeur-mot { font-size: 11.5px; margin-top: 3px; text-align: right; }
.item-personne.avec-humeur .humeur-modificateurs { justify-content: flex-end; }

.item-personne {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 8px; border-radius: 8px; cursor: pointer;
}
.item-personne:hover { background: var(--fond-3); }
.item-personne.actif { background: var(--accent-doux); }
.item-avatar {
  width: 27px; height: 27px; border-radius: 50%; flex: 0 0 27px;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 11.5px; font-weight: 600;
}
.item-corps { min-width: 0; flex: 1; }
.item-nom { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.item-meta { font-size: 12px; color: var(--texte-faible); }
.item-personne.mort .item-avatar { filter: saturate(.5) brightness(.95); }

/* fiche */
.pn-entete {
  position: sticky; top: 0; z-index: 5;
  background: var(--fond-2); border-bottom: 1px solid var(--bord); padding: 13px 14px;
}
.pn-entete-haut { display: flex; align-items: flex-start; gap: 11px; }
.pn-avatar {
  width: 44px; height: 44px; border-radius: 50%; flex: 0 0 44px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; color: #fff; font-size: 16px;
}
.pn-nom { font-family: var(--police-titre); font-size: 19px; line-height: 1.2; }
.pn-surnom { color: var(--texte-faible); font-size: 13px; font-style: italic; }
.pn-fermer { margin-left: auto; }
.pn-badges { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }
.badge {
  font-size: 11.5px; padding: 3px 8px; border-radius: 20px;
  border: 1px solid var(--bord); color: var(--texte-doux); background: var(--fond-3);
}
.badge-vivant { color: #1d7a5f; border-color: #a9dcc9; background: #e6f6f0; }
.badge-mort { color: #9a4b4b; border-color: #e3c2c2; background: #f9eded; }
body.sombre .badge-vivant { color: #7fd6b2; border-color: #2b5a49; background: #16302a; }
body.sombre .badge-mort { color: #d99b9b; border-color: #5b3636; background: #2c1c1c; }
.badge-maison { color: #fff; font-weight: 600; border: 0; }
/* Rangs : or pour qui règne, argent pour qui attend. Mêmes teintes que les
   marques posées sur les fiches du plan, pour qu'on les relie sans effort. */
.badge-rang { font-weight: 600; }
.badge-rang-chef { color: #8a6d0b; border-color: #d9be62; background: #fbf3d9; }
.badge-rang-heritier { color: #4a637d; border-color: #a8bed4; background: #eaf1f8; }
body.sombre .badge-rang-chef { color: #e6c964; border-color: #6a5720; background: #302a15; }
body.sombre .badge-rang-heritier { color: #a9c3dc; border-color: #3d4f63; background: #1a222c; }

/* Détails d'un lien : repliés tant qu'ils sont vides. */
.fl-details { margin-top: 4px; }
.fl-details > summary {
  cursor: pointer; font-size: 12.5px; color: var(--texte-faible);
  padding: 3px 0; user-select: none;
}
.fl-details > summary:hover { color: var(--texte); }
.fl-details .champ-edit { margin-top: 6px; }

.rangs-puces { display: flex; flex-wrap: wrap; gap: 6px; }
.puce-rang { font-size: 12.5px; padding: 5px 9px; }
.puce-rang.actif { font-weight: 600; }
.puce-rang-chef.actif { color: #8a6d0b; border-color: #d9be62; background: #fbf3d9; }
.puce-rang-heritier.actif { color: #4a637d; border-color: #a8bed4; background: #eaf1f8; }
body.sombre .puce-rang-chef.actif { color: #e6c964; border-color: #6a5720; background: #302a15; }
body.sombre .puce-rang-heritier.actif { color: #a9c3dc; border-color: #3d4f63; background: #1a222c; }

.pn-actions { display: flex; gap: 7px; margin-top: 10px; }

.pn-section { padding: 13px 14px; border-bottom: 1px solid var(--bord); }
.pn-section > h3 {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin: 0 0 10px;
}

.grille-champs { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.grille-champs .pleine { grid-column: 1 / -1; }
.champ-edit { display: flex; flex-direction: column; gap: 3px; }
.champ-edit > label { font-size: 11.5px; color: var(--texte-faible); text-transform: uppercase; letter-spacing: .07em; }
.champ-edit input, .champ-edit select, .champ-edit textarea {
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 6px 8px; outline: none; width: 100%; font-size: 13.5px;
}
.champ-edit textarea { resize: vertical; min-height: 86px; line-height: 1.5; }
.champ-edit input:focus, .champ-edit select:focus, .champ-edit textarea:focus { border-color: var(--accent); }

/* Une ligne de joueur dans la fiche. Le suffixe n'est pas décoratif : sur le
   plan, une carte jouée porte l'état `.joueur` (au même titre que `.morte` ou
   `.satellite`). Un `.joueur` nu attraperait donc aussi les cartes, et comme il
   vient plus bas dans la feuille à spécificité égale, son `padding` l'emporte.
   Le dégât visible d'alors a disparu avec le portrait (lot 20.C), mais la
   collision, elle, reste : le nom de classe doit rester qualifié. */
/* La palette du liseré (lot 20.B) : des cases, pas un cercle chromatique.
   Voir `js/palette.js` pour le pourquoi. La case retenue porte une couronne
   claire **et** un cerne sombre : sur un fond clair comme sur un fond sombre,
   l'une des deux se voit toujours. */
.palette { display: flex; flex-wrap: wrap; gap: 5px; }
.palette-case {
  width: 22px; height: 22px; padding: 0; border-radius: 6px;
  border: 1px solid var(--bord); cursor: pointer;
  transition: transform .1s ease, box-shadow .15s ease;
}
.palette-case:hover { transform: scale(1.12); }
.palette-case.actif {
  box-shadow: inset 0 0 0 2px var(--carte-fond), 0 0 0 2px var(--texte-doux);
}
/* « Aucune couleur » : une case barrée, et non une case vide — vide, elle se
   lisait comme une treizième teinte (un gris très pâle). */
.palette-aucune {
  background:
    linear-gradient(to top left,
      transparent calc(50% - 1px), var(--texte-faible) calc(50% - 1px),
      var(--texte-faible) calc(50% + 1px), transparent calc(50% + 1px));
}

/* ---------------------------------------- l'éditeur d'une forme (lot 20.D)
 *
 * Un panneau flottant, monté sur le même socle que les menus et les autres
 * éditeurs : un seul ouvert à la fois, fermeture au clic extérieur ou à Échap.
 * Il n'a pas de bouton « Enregistrer » — comme partout ici, chaque champ part
 * quand on le touche. */
/* `.flottant` porte le `position: fixed` et le `z-index` ; la largeur, elle, se
   redit ici, comme pour tous les autres panneaux (`.flottant.flottant-langue`
   et consorts) — sinon les 322px de `.flottant`, déclarés plus bas, gagnent. */
.flottant.forme-editeur {
  width: 286px; padding: 11px 12px 10px;
  display: flex; flex-direction: column; gap: 9px;
}

/* ------------------------------------------ un mot aux autres (lot 27.D)
 *
 * Deux objets, et ils ne se ressemblent pas exprès. Le panneau d'envoi est un
 * panneau flottant comme les autres — on l'ouvre, on écrit, il se ferme. La
 * pile de mots reçus, elle, est la **seule** chose de l'application qui
 * apparaisse sans qu'on l'ait demandée : elle se tient donc en bas à droite,
 * sans rien recouvrir d'utile, et s'en va toute seule. */
.flottant.msg-editeur {
  width: 300px; padding: 11px 12px 10px;
  display: flex; flex-direction: column; gap: 9px;
}
.msg-ligne { display: flex; align-items: center; gap: 8px; }
.msg-lib { font-size: 12.5px; color: var(--texte-faible); }
.msg-a-qui { flex: 1; min-width: 0; font-size: 13px; }
.msg-champ {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 62px;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: var(--rayon);
  padding: 7px 9px; font: inherit; font-size: 14px; line-height: 1.45; outline: none;
}
.msg-champ:focus { border-color: var(--accent); background: var(--fond-2); }
.msg-pied { display: flex; align-items: center; gap: 8px; }
/* `inline-flex` et un `gap` : le nombre et le mot sont deux nœuds distincts —
   le dictionnaire compare le texte entier d'un nœud, donc « 480 signes » écrit
   d'un bloc ne se traduirait jamais. L'espace entre eux est donc de la mise en
   page, pas un caractère. */
.msg-reste {
  flex: 1; display: inline-flex; align-items: baseline; gap: 4px;
  font-size: 11.5px; color: var(--texte-faible); font-variant-numeric: tabular-nums;
}

/* La pile des mots reçus. `pointer-events: none` sur la pile et `auto` sur
   chaque carte : entre deux cartes, on rend le clic au plan — sinon une
   colonne invisible de 300 px barrerait le coin de l'écran pendant vingt
   secondes, ce qui est exactement le défaut que le lot 20.D a passé du temps à
   éviter pour les formes. */
.msg-pile {
  position: fixed; right: 16px; bottom: 74px; z-index: 40;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
  pointer-events: none;
}
.msg-recu {
  pointer-events: auto; cursor: pointer;
  width: min(300px, calc(100vw - 32px)); box-sizing: border-box;
  background: var(--carte-fond); border: 1px solid var(--bord-clair);
  border-left: 3px solid var(--accent); border-radius: var(--rayon);
  box-shadow: var(--ombre-panneau); padding: 8px 10px;
  animation: msg-arrive .22s ease;
}
.msg-recu.part { opacity: 0; transition: opacity .4s ease; }
.msg-recu-qui { display: flex; align-items: baseline; gap: 6px; margin-bottom: 3px; }
.msg-recu-nom { font-size: 12.5px; font-weight: 600; color: var(--accent-fonce); }
.msg-recu-prive {
  font-size: 10.5px; color: var(--texte-faible);
  border: 1px solid var(--bord); border-radius: 4px; padding: 0 4px;
}
.msg-recu-texte {
  margin: 0; font-size: 14px; line-height: 1.45; color: var(--texte);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
@keyframes msg-arrive {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
.fe-entete { display: flex; align-items: center; justify-content: space-between; }
/* Un en-tête par lequel on traîne son panneau (lot 27.C). La classe est posée
   par `creerFlottant` quand on lui passe `deplacable`, et par personne d'autre :
   les panneaux qu'on ne déplace pas ne doivent pas *avoir l'air* de se
   déplacer. `user-select: none` sur le seul en-tête — le corps du panneau, lui,
   reste un texte qu'on peut sélectionner. */
.fl-deplacable { cursor: move; user-select: none; }
body.flottant-en-deplacement { cursor: move; user-select: none; }
.fe-actions { display: flex; align-items: center; gap: 4px; }

/* La portée : sur le plan général, ou en centrant sur certains profils. */
.fe-bloc-portee { gap: 6px; }
.fe-portee { flex: 1; font-size: 12.5px; padding: 4px 6px; }
.fe-portee.actif { background: var(--accent); border-color: var(--accent); color: #fff; }
.fe-profils { display: flex; flex-wrap: wrap; gap: 4px; }
.fe-jeton {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 4px 2px 7px; border-radius: 999px;
  background: var(--fond-3); border: 1px solid var(--bord);
  font-size: 12.5px;
}
.fe-jeton-retrait {
  border: 0; background: transparent; color: var(--texte-faible);
  cursor: pointer; font: inherit; line-height: 1; padding: 0 2px;
}
.fe-jeton-retrait:hover { color: var(--danger, #c0392b); }
.fe-ajout-profil {
  width: 100%; background: var(--fond-3); color: inherit;
  border: 1px solid var(--bord); border-radius: 7px;
  padding: 4px 6px; font: inherit; font-size: 13px;
}
.fe-ligne { display: flex; flex-direction: column; gap: 4px; }
.fe-lib {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--texte-faible);
}
.forme-editeur textarea {
  width: 100%; resize: vertical; line-height: 1.45; font-family: inherit;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 6px 8px; font-size: 13.5px; outline: none; color: inherit;
}
.forme-editeur textarea:focus { border-color: var(--accent); }
.fe-styles { display: flex; align-items: center; gap: 6px; }
.fe-bascule { width: 30px; padding: 3px 0; font-size: 14.5px; }
.fe-gras { font-weight: 800; }
.fe-italique { font-style: italic; font-family: var(--police-titre); }
.fe-bascule.actif { background: var(--accent); border-color: var(--accent); color: #fff; }
.fe-taille {
  width: 58px; text-align: center;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 4px 6px; font-size: 13.5px; outline: none; color: inherit;
}
.forme-editeur input[type="range"] { width: 100%; accent-color: var(--accent); }
.fe-aide { font-size: 12px; color: var(--texte-faible); line-height: 1.45; margin: 0; }

/* Les trois outils, dans la barre du bas, à côté de l'interrupteur « Formes ».
   Ils n'apparaissent qu'une fois le mode ouvert : hors de lui, ils ne
   pourraient rien tracer. */
.outils-formes { display: flex; align-items: center; gap: 3px; }
.outil-forme { width: 28px; font-size: 15px; }
.outil-forme.actif, #btn-formes.actif {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
/* Pendant un tracé, le plan ne se déplace plus : le curseur doit le dire. */
.plan.en-trace, .plan.en-trace:active { cursor: crosshair; }

.joueur-ligne { padding: 8px 0; border-bottom: 1px dashed var(--bord); }
.joueur-ligne:last-child { border-bottom: 0; }
.joueur-entete { display: flex; align-items: center; gap: 8px; }
.joueur-pastille { width: 9px; height: 9px; border-radius: 50%; }
.joueur-nom { font-size: 13.5px; }
.joueur-perso { color: var(--texte-faible); font-size: 12px; }
/* Le commentaire d'un joueur sur quelqu'un (lot 22.B).
   C'était une ligne unique de 11 px dans un panneau étroit : « il nous a
   vendus aux Lannister à Vivesaigues » y tenait sur un tiers, le reste
   défilait hors du champ et ne se relisait jamais. C'est maintenant une zone
   de texte qui grandit avec ce qu'on écrit — deux lignes au repos pour ne pas
   pousser l'humeur du joueur suivant hors de l'écran. */
.joueur-commentaire { margin-top: 6px; }
.joueur-commentaire textarea {
  width: 100%; background: transparent; border: 0;
  border-bottom: 1px dashed var(--bord-clair);
  padding: 3px 2px; color: var(--texte-doux); outline: none;
  font: inherit; font-size: 14px; line-height: 1.45;
  resize: vertical; min-height: 36px; overflow: hidden;
}
.joueur-commentaire textarea:focus { border-bottom-color: var(--accent); color: var(--texte); }
/* Humeur : 7 crans, un seul plein — c'est un cran, pas une jauge. */
.humeur { margin: 6px 0 2px; }
.humeur-points { display: flex; gap: 5px; }
.humeur-point {
  width: 16px; height: 16px; border-radius: 50%; padding: 0;
  border: 1px solid var(--bord-clair); background: transparent;
  cursor: pointer; transition: transform .1s, background .15s, box-shadow .15s;
}
.humeur-point:hover { transform: scale(1.18); }
.humeur-point.actif { box-shadow: 0 0 0 3px var(--fond-3); }
.humeur-mot { font-size: 12.5px; margin-top: 5px; }
.humeur-mot.vide { color: var(--texte-faible); font-style: italic; }
.humeur-modificateurs { display: flex; gap: 8px; margin-top: 2px; }
.humeur-mod {
  font-size: 11.5px; color: var(--texte-faible);
  font-variant-numeric: tabular-nums; cursor: help;
}
.humeur-mod.vide { font-style: italic; cursor: default; }

.echelle-aide { font-size: 11.5px; color: var(--texte-faible); margin: -4px 0 7px; }

.groupe-liens { margin-bottom: 12px; }
.groupe-liens > h4 {
  font-size: 11.5px; color: var(--texte-faible); margin: 0 0 5px;
  text-transform: uppercase; letter-spacing: .1em;
}
.lien { display: flex; align-items: center; gap: 8px; padding: 6px 7px; border-radius: 7px; cursor: pointer; }
.lien:hover { background: var(--fond-3); }
.lien-trait { width: 13px; height: 0; border-top-width: 3px; border-top-style: solid; flex: 0 0 13px; }
.lien-corps { min-width: 0; flex: 1; }
.lien-nom { font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lien-type { font-size: 11.5px; color: var(--texte-faible); }
.lien-notes { font-size: 11.5px; color: var(--texte-faible); font-style: italic; }
.puce-secret { font-size: 10.5px; color: #a9762a; border: 1px solid #e0c79a; border-radius: 4px; padding: 0 4px; }
.puce-deduit { font-size: 10.5px; color: var(--texte-faible); border: 1px solid var(--bord); border-radius: 4px; padding: 0 4px; }

.pn-pied {
  position: sticky; bottom: 0;
  background: var(--fond-2); border-top: 1px solid var(--bord);
  padding: 10px 14px; display: flex; align-items: center; gap: 9px;
}
.pn-pied-aide { font-size: 11.5px; color: var(--texte-faible); margin-left: auto; text-align: right; }
.etat-sauvegarde { font-size: 12.5px; color: var(--texte-faible); }
.etat-sauvegarde.modifie { color: #b8842a; }
.etat-sauvegarde.ok { color: var(--accent-fonce); }
.etat-sauvegarde.erreur { color: #c0392b; }

.vide { color: var(--texte-faible); font-size: 13.5px; font-style: italic; }

/* ------------------------------------------------- menus & éditeurs flottants */

.menu-contextuel {
  position: fixed; z-index: 60; min-width: 210px; max-width: 280px; padding: 5px;
  background: var(--fond-2); border: 1px solid var(--bord-clair);
  border-radius: 10px; box-shadow: var(--ombre-panneau); font-size: 14px;
}
.menu-titre {
  display: flex; align-items: center; gap: 7px; padding: 6px 9px 8px;
  font-weight: 600; border-bottom: 1px solid var(--bord); margin-bottom: 4px;
}
.menu-titre-texte { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-pastille { width: 9px; height: 9px; border-radius: 3px; flex: 0 0 9px; }
.menu-texte { padding: 1px 9px 8px; color: var(--texte-doux); line-height: 1.45; }
.menu-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 7px 9px; border: 0; border-radius: 6px; background: transparent;
  text-align: left; cursor: pointer; color: var(--texte);
}
.menu-item:hover { background: var(--fond-3); }
.menu-item:disabled { opacity: .45; cursor: default; background: transparent; }
.menu-icone { width: 15px; flex: 0 0 15px; text-align: center; opacity: .8; }
.menu-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-detail { color: var(--texte-faible); font-size: 12.5px; }
.menu-item.danger { color: #c0392b; }
.menu-item.danger:hover { background: #fdecea; }
body.sombre .menu-item.danger { color: #e8776b; }
body.sombre .menu-item.danger:hover { background: #3a1f1c; }
.menu-separateur { height: 1px; background: var(--bord); margin: 4px 2px; }

/* Un panneau ne dépasse jamais de l'écran (lot 23.A).
   `.fl-corps` était seul borné, à 62vh — mais l'entête et le pied s'ajoutaient
   par-dessus, si bien qu'un éditeur chargé (celui des filtres : variable,
   segments, dégradé, tests) montait à 565 px et poussait son bouton
   « Enregistrer » sous le bord de la fenêtre, hors d'atteinte. On borne donc le
   panneau **entier**, et c'est le corps qui se rétrécit et défile. */
.flottant {
  position: fixed; z-index: 60; width: 322px;
  display: flex; flex-direction: column; overflow: hidden;
  max-height: calc(100vh - 20px);
  background: var(--fond-2); border: 1px solid var(--bord-clair);
  border-radius: 12px; box-shadow: var(--ombre-panneau);
}
/* L'entête et le pied gardent leur taille : ce sont le titre et les boutons,
   les deux choses qu'on ne veut jamais voir rognées. */
.fl-entete, .fl-pied { flex: 0 0 auto; }
.fl-corps { flex: 1 1 auto; min-height: 0; }
.fl-entete {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 9px 9px 12px; border-bottom: 1px solid var(--bord);
}
.fl-titre { flex: 1; font-size: 14px; line-height: 1.45; }
.fl-nom { font-weight: 600; }
.fl-fleche { color: var(--texte-faible); }
.fl-fermer { flex: 0 0 auto; }
.fl-corps {
  padding: 11px 12px; display: flex; flex-direction: column; gap: 10px;
  max-height: 62vh; overflow: auto;
}
.fl-corps .champ-edit textarea { min-height: 50px; }
.fl-pied {
  display: flex; align-items: center; gap: 7px;
  padding: 9px 12px; border-top: 1px solid var(--bord); background: var(--fond-3);
}
.fl-etat { flex: 1; font-size: 12.5px; color: var(--texte-faible); text-align: right; }
.fl-etat.erreur { color: #c0392b; }
.fl-aide { margin: 0; font-size: 12.5px; color: var(--texte-faible); line-height: 1.45; }
/* Ce qu'on perd, et non ce qu'on fait : la seule aide qu'on veut lire avant
   d'appuyer, donc la seule qui se distingue du gris (lot 21.C). */
.fl-aide.fl-alerte {
  color: var(--texte); border-left: 3px solid #c0392b;
  padding: 6px 0 6px 9px; background: rgba(192, 57, 43, .08);
  border-radius: 0 4px 4px 0;
}

/* ------------------------------------------------ raccourcis (lot 16.D) */

.flottant.flottant-raccourcis { width: 342px; }
.rc-groupe { display: flex; flex-direction: column; gap: 4px; }
.rc-titre {
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin-bottom: 2px;
}
/* Les touches d'abord, à largeur fixe : l'œil descend une colonne de touches
   et s'arrête sur la bonne, au lieu de relire chaque phrase. */
.rc-ligne { display: flex; align-items: baseline; gap: 9px; font-size: 13px; }
.rc-touches { flex: 0 0 106px; display: flex; flex-wrap: wrap; gap: 3px; }
.rc-quoi { flex: 1; color: var(--texte-doux); line-height: 1.4; }
.rc-touches kbd {
  font: inherit; font-size: 12px; line-height: 1.5;
  padding: 1px 5px; border-radius: 5px;
  background: var(--fond-3); border: 1px solid var(--bord);
  border-bottom-width: 2px; color: var(--texte);
  white-space: nowrap;
}

/* --------------------------------------------------- langue (lot 16.G) */

.flottant.flottant-langue { width: 188px; }
.flottant-langue .fl-corps { padding: 5px; gap: 2px; }
.lg-choix {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border: 0; border-radius: 8px;
  background: transparent; color: var(--texte);
  font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.lg-choix:hover { background: var(--fond-3); }
.lg-drapeau { font-size: 16px; line-height: 1; }
.lg-nom { flex: 1; }
.lg-coche { color: var(--accent); width: 12px; }
.lg-choix.actif .lg-nom { font-weight: 600; }

/* ------------------------------ offrir et recevoir une note (lot 16.E) */

.flottant.flottant-envoi { width: 340px; }
.of-adresses { resize: vertical; min-height: 54px; line-height: 1.5; }

/* La boîte de réception peut porter plusieurs propositions : elle est plus
   large que les autres panneaux, et son corps défile. */
.flottant.flottant-offres { width: 396px; }
.flottant-offres .fl-corps { max-height: 58vh; gap: 12px; }
.of-carte {
  border: 1px solid var(--bord); border-radius: 9px;
  padding: 10px 11px; background: var(--fond-3);
  display: flex; flex-direction: column; gap: 5px;
}
.of-de { font-size: 13px; }
.of-origine { color: var(--texte-faible); }
.of-titre { font-size: 14px; font-weight: 600; }
.of-meta { font-size: 12px; color: var(--texte-faible); }
/* L'aperçu est du Markdown brut, volontairement : ce qui compte ici est de
   reconnaître la note, pas de la lire en beauté avant de l'avoir acceptée. */
.of-apercu {
  margin: 2px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--texte-doux);
  white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 92px; overflow: hidden;
  border-left: 2px solid var(--bord); padding-left: 8px;
}
.of-actions { display: flex; gap: 7px; margin-top: 4px; }

/* --------------------------------------------------- éditeur de filtre */

.flottant.editeur-filtre { width: 384px; }
.editeur-filtre .fl-corps { max-height: 64vh; overflow-y: auto; }

.gradient-reglage { display: flex; align-items: center; gap: 8px; }
.gradient-bande {
  flex: 1; height: 14px; border-radius: 7px; border: 1px solid var(--bord);
}

.bloc-tests { gap: 6px; }
.jointure select { font-size: 12.5px; }
.ligne-test { display: flex; align-items: center; gap: 5px; }
.ligne-test select, .ligne-test input { font-size: 12.5px; padding: 4px 6px; }
.ligne-test select:first-child { flex: 1.2; min-width: 0; }
.ligne-test select:nth-child(2) { flex: 1; min-width: 0; }
.ligne-test input { flex: 1; min-width: 0; }

/* Listes nommées : cocher ce qui existe, ajouter le reste à la main. */
.flottant.editeur-liste { width: 320px; }
.editeur-liste .fl-corps { max-height: 62vh; overflow-y: auto; }
.liste-valeurs {
  max-height: 190px; overflow-y: auto;
  border: 1px solid var(--bord); border-radius: 8px; padding: 4px 6px;
  background: var(--fond-3);
}
.valeur-observee {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; padding: 2px 0; cursor: pointer;
}
.valeur-observee input { width: auto; flex: 0 0 auto; }
.puces-valeurs { display: flex; flex-wrap: wrap; gap: 4px; }
.puce-valeur {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12.5px; padding: 2px 4px 2px 7px; border-radius: 11px;
  background: var(--accent-doux); border: 1px solid var(--bord);
}
.puce-retirer {
  border: 0; background: none; cursor: pointer; color: var(--texte-faible);
  font-size: 12.5px; line-height: 1; padding: 1px 2px;
}
.puce-retirer:hover { color: #c0392b; }

.apercu-filtre {
  border: 1px solid var(--bord); border-radius: 8px; padding: 7px 9px;
  background: var(--fond-3);
}
.apercu-resume {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 12.5px; color: var(--texte-faible); margin-bottom: 5px;
}
.apercu-exclus { color: #c0392b; }
.apercu-segments { list-style: none; margin: 0; padding: 0; }
.apercu-segments li {
  display: flex; align-items: center; gap: 7px; font-size: 13px; padding: 2px 0;
}
.apercu-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.champ-avec-pastille { display: flex; align-items: center; gap: 7px; }
.champ-avec-pastille select { flex: 1; }
.fl-pastille { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 12px; }

.bouton-danger { color: #c0392b; border-color: #e3b4ae; }
.bouton-danger:hover { background: #fdecea; border-color: #c0392b; color: #c0392b; }
.bouton-danger.arme { background: #c0392b; border-color: #c0392b; color: #fff; }
.bouton-danger.arme:hover { background: #a93226; color: #fff; }

/* --------------------------------------------------- bibliothèque (rail) */

.liste-sauvegardes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.sauvegarde {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; user-select: none;
}
.sauvegarde:hover { background: var(--fond-3); }
.sauvegarde.actif { background: var(--accent-doux); border-color: var(--accent); cursor: default; }
/* Un arbre qu'on nous a ouvert (11.B) : bordure en pointillé, pour qu'on ne le
   prenne pas d'un coup d'œil pour l'une des siennes. */
.sauvegarde.partage { border-style: dashed; }
.sauvegarde.partage .sv-pastille { background: #7fa8c9; }
.sv-pastille {
  width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px;
  border: 1.5px solid var(--bord-clair);
}
.sauvegarde.actif .sv-pastille { background: var(--accent); border-color: var(--accent); }
.sv-corps { min-width: 0; }
.sv-nom { font-size: 14px; color: var(--texte); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sauvegarde.actif .sv-nom { font-weight: 600; }
.sv-meta { font-size: 11.5px; color: var(--texte-faible); font-variant-numeric: tabular-nums; }
.rail-actions { display: flex; gap: 4px; margin-top: 6px; }
.rail-actions .bouton { flex: 1; font-size: 12.5px; padding: 5px 4px; }
.rail-actions .bouton:hover { background: var(--fond-3); border-color: var(--bord); }

.groupe-cadrage { display: contents; }
.groupe-cadrage[hidden] { display: none; }

/* ------------------------------------------- référentiels (rail + éditeurs) */

/* Une maison ou un type sans occurrence affichée reste dans la liste : il
   faut bien pouvoir modifier ce qu'on vient de créer. */
.legende li.inutilise { opacity: .5; cursor: default; }
.legende li.inutilise:hover { background: transparent; }
.legende li.inutilise .nombre { opacity: .6; }

.editeur-referentiel { width: 320px; }
.champ-couleur-edit { display: flex; flex-direction: column; gap: 6px; }
.champ-couleur-edit input[type='color'] {
  width: 100%; height: 30px; padding: 2px;
  border: 1px solid var(--bord); border-radius: 6px;
  background: var(--fond-3); cursor: pointer;
}
.nuancier { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.nuance {
  height: 18px; border-radius: 4px; padding: 0; cursor: pointer;
  border: 1px solid rgba(0, 0, 0, .18);
  transition: transform .12s ease, box-shadow .12s ease;
}
.nuance:hover { transform: scale(1.12); }
.nuance.actif { box-shadow: 0 0 0 2px var(--fond-2), 0 0 0 3px var(--texte); }

.segments { display: flex; gap: 4px; }
.segment {
  flex: 1; padding: 5px 4px; font-size: 12.5px; cursor: pointer;
  border: 1px solid var(--bord); border-radius: 6px;
  background: var(--fond-3); color: var(--texte-doux);
}
.segment:hover { border-color: var(--bord-clair); color: var(--texte); }
.segment.actif { background: var(--accent); border-color: var(--accent); color: #fff; }

.fl-apercu {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 11px; border-radius: 7px;
  background: var(--fond-3); border: 1px solid var(--bord);
  font-size: 14px; color: var(--texte);
}
.ap-pastille { width: 13px; height: 13px; border-radius: 4px; flex: 0 0 13px; }
.ap-trait {
  width: 44px; height: 0; flex: 0 0 44px;
  border-top-width: 3px; border-top-style: solid; border-radius: 2px;
}
.ap-fleche { font-size: 11.5px; margin-left: -6px; }
.ap-fleche[hidden] { display: none; }

/* ------------------------------------------------------------- photos */

.pn-photo-boite { position: relative; flex: 0 0 44px; }
.pn-photo {
  position: relative; overflow: hidden;
  width: 44px; height: 44px; border-radius: 50%; flex: 0 0 44px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; color: #fff; font-size: 16px;
  cursor: pointer; user-select: none;
}
.pn-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pn-photo-voile {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .5); color: #fff; font-size: 17px;
  opacity: 0; transition: opacity .15s ease;
}
.pn-photo:hover .pn-photo-voile,
.pn-photo.survol .pn-photo-voile { opacity: 1; }
.pn-photo.survol { box-shadow: 0 0 0 2px var(--accent); }
.pn-photo-retirer {
  position: absolute; top: -5px; right: -7px;
  width: 19px; height: 19px; padding: 0; font-size: 11.5px; line-height: 1;
  border-radius: 50%; background: var(--fond-2); border: 1px solid var(--bord);
  opacity: 0; transition: opacity .15s ease;
}
.pn-photo-boite:hover .pn-photo-retirer { opacity: 1; }

.item-avatar.avec-photo { overflow: hidden; }
.item-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Le portrait a quitté la carte du plan au lot 20.C : il ne se voit plus que
   dans la fiche (`.pn-photo`, ci-dessus) et dans la liste des personnes
   (`.item-avatar`). Les règles de `.carte-photo` sont donc parties avec lui. */

/* ------------------------------------------------------- vue « tableau » */

/* ----------------------------------------------------------- vue maisons
 *
 * Écran partagé : l'histoire à gauche, ce que la maison *est* à droite. Le
 * partage tient jusqu'à 900 px, en dessous les deux moitiés s'empilent — sur
 * un téléphone, deux colonnes de 180 px ne sont plus deux colonnes.
 */
.vue-maisons {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: var(--fond-2);
}
.mz-onglets {
  display: flex; gap: 2px; padding: 8px 12px 0; flex-wrap: wrap;
  border-bottom: 1px solid var(--bord); flex: 0 0 auto;
}
.mz-onglet {
  display: flex; align-items: center; gap: 7px;
  background: none; border: 1px solid transparent; border-bottom: none;
  border-radius: 8px 8px 0 0; padding: 7px 13px; cursor: pointer;
  color: var(--texte-doux); font-size: 13.5px; font-family: inherit;
}
.mz-onglet:hover { color: var(--texte); background: var(--fond-3); }
.mz-onglet.actif {
  color: var(--texte); background: var(--fond); border-color: var(--bord);
  border-bottom: 1px solid var(--fond); margin-bottom: -1px; font-weight: 600;
}
.mz-onglet-pastille {
  width: 9px; height: 9px; border-radius: 50%; background: var(--couleur-maison);
}
.mz-onglet .nombre {
  font-size: 11.5px; color: var(--texte-faible); background: var(--fond-3);
  border-radius: 9px; padding: 0 6px;
}

.mz-colonnes { flex: 1; display: flex; min-height: 0; }
.mz-colonne { flex: 1 1 50%; min-width: 0; overflow-y: auto; padding: 16px 18px 40px; }
.mz-histoire { border-right: 1px solid var(--bord); }
.mz-fiche { background: var(--fond); }

.mz-entete { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
.mz-entete h2 { font-size: 16px; margin: 0; }
.mz-entete-bloc { display: flex; align-items: baseline; gap: 10px; }
.mz-entete-bloc h3 { margin: 0; }
.mz-entete .bouton { margin-left: auto; }
.mz-etat { margin-left: auto; font-size: 12px; color: var(--texte-faible); }
.mz-etat.erreur { color: #c0392b; }
.mz-devise { margin: -6px 0 14px; font-style: italic; color: var(--texte-doux); font-size: 13.5px; }
.mz-sous-titre { font-size: 12.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--texte-faible); margin: 22px 0 4px; }
.mz-aide { font-size: 12.5px; color: var(--texte-faible); line-height: 1.5; margin: 6px 0 0; }
.mz-vide { padding: 10px 0; line-height: 1.6; }

.mz-bloc { padding: 14px 0; border-bottom: 1px solid var(--bord); }
.mz-bloc:last-child { border-bottom: 0; }
.mz-bloc h3 {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin: 0 0 10px;
}
.mz-bloc textarea {
  width: 100%; background: var(--fond-3); border: 1px solid var(--bord);
  border-radius: 7px; padding: 7px 9px; font-size: 13.5px; line-height: 1.5;
  resize: vertical; outline: none; font-family: inherit; color: inherit;
}
.mz-bloc textarea:focus { border-color: var(--accent); }

/* Événements : une pastille de date, un titre, un récit, des visages. */
.mz-evenement {
  border-left: 2px solid var(--bord-clair);
  padding: 8px 0 10px 12px; margin-bottom: 4px;
}
.mz-evenement-lie { border-left-color: var(--couleur-type); }
.mz-evenement-lie.revolu { opacity: .6; }
.mz-evenement-haut { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.mz-annee {
  font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 600;
  color: var(--texte-faible); background: var(--fond-3);
  border-radius: 9px; padding: 1px 7px; white-space: nowrap;
}
.mz-evenement-titre { font-size: 14px; font-weight: 600; }
.mz-lieu { font-size: 12px; color: var(--texte-faible); }
.mz-modifier { margin-left: auto; opacity: 0; padding: 2px 6px; }
.mz-evenement:hover .mz-modifier, .mz-lien:hover .mz-modifier { opacity: 1; }
.mz-evenement-texte {
  margin: 5px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--texte-doux);
  white-space: pre-wrap;
}

/* Une mention : cliquable, à la couleur de la personne ou de la maison. */
.mz-personnes { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.mz-personne {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 20px;
  padding: 3px 9px; font-size: 12.5px; color: var(--texte-doux);
  cursor: pointer; font-family: inherit;
}
.mz-personne:hover { border-color: var(--couleur-personne); color: var(--texte); }
.mz-personne.morte { opacity: .62; }
.mz-personne-pastille {
  width: 8px; height: 8px; border-radius: 50%; background: var(--couleur-personne);
  flex: 0 0 8px;
}
.mz-personne-retirable { cursor: default; }
.mz-retirer {
  background: none; border: 0; cursor: pointer; color: var(--texte-faible);
  padding: 0 0 0 2px; font-size: 12.5px; font-family: inherit;
}
.mz-retirer:hover { color: #c0392b; }

/* Caractéristiques : le nom, une jauge, le chiffre. La jauge sert à comparer
   deux maisons d'un coup d'œil ; le chiffre reste ce qu'on écrit. */
.mz-caracs { display: flex; flex-direction: column; gap: 7px; }
.mz-carac { display: flex; align-items: center; gap: 10px; }
.mz-carac-nom { flex: 0 0 92px; font-size: 13px; }
.mz-jauge {
  flex: 1; height: 6px; background: var(--fond-3); border-radius: 4px;
  overflow: hidden; min-width: 40px;
}
.mz-jauge-barre { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.mz-carac input {
  flex: 0 0 58px; text-align: center; font-variant-numeric: tabular-nums;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 4px 6px; font-size: 13.5px; outline: none; color: inherit;
}
.mz-carac input:focus { border-color: var(--accent); }

/* ------------------------------------------- unités de guerre (lot 20.E)
 *
 * Une unité est une petite fiche dans la fiche : une ligne de tête (nom, type,
 * retrait), une grille de sept champs, et ses notes repliées. La grille est en
 * `auto-fill` plutôt qu'en nombre fixe de colonnes — la moitié droite de
 * l'écran partagé fait 300 px chez l'un et 700 chez l'autre, et une largeur
 * imposée y laisserait soit des trous, soit des champs illisibles. */
.mz-unite {
  border: 1px solid var(--bord); border-radius: 9px;
  padding: 9px 10px; margin-top: 9px;
  background: var(--fond-2);
}
.mz-unite-haut { display: flex; align-items: center; gap: 7px; }
.mz-unite-haut input {
  flex: 1; min-width: 0;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 5px 8px; font-size: 13.5px; outline: none; color: inherit;
}
.mz-unite-haut input.mz-unite-large { flex: 1.6; font-weight: 600; }
.mz-unite-haut input:focus { border-color: var(--accent); }
.mz-retirer-unite { flex: 0 0 auto; opacity: .55; }
.mz-unite:hover .mz-retirer-unite { opacity: 1; }

.mz-unite-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 7px 10px; margin-top: 9px;
}
.mz-unite-champ { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mz-unite-champ.pleine { grid-column: 1 / -1; }
.mz-unite-champ > span {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--texte-faible);
}
.mz-unite-champ input, .mz-unite-champ select {
  width: 100%; min-width: 0;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 4px 7px; font-size: 13.5px; outline: none; color: inherit;
}
.mz-unite-champ input:focus, .mz-unite-champ select:focus { border-color: var(--accent); }
/* Le pourcentage : le signe suit le champ au lieu d'occuper une colonne. */
.mz-unite-pourcent { display: flex; align-items: center; gap: 4px; }
.mz-unite-pourcent > span { font-size: 12.5px; color: var(--texte-faible); }

/* L'état colore sa propre liste : sur douze unités en pleine bataille, c'est
   la seule colonne qu'on relit, et on la relit de loin. */
.mz-etat-active { border-color: #3fa877 !important; }
.mz-etat-desorganisee { border-color: #e0803a !important; }
.mz-etat-en_deroute { border-color: #d64545 !important; }
.mz-etat-detruite {
  border-color: var(--bord) !important;
  color: var(--texte-faible); text-decoration: line-through;
}

.mz-unite-notes { margin-top: 8px; }
.mz-unite-notes summary {
  font-size: 12px; color: var(--texte-faible); cursor: pointer;
  text-transform: uppercase; letter-spacing: .07em;
}
.mz-unite-notes textarea {
  width: 100%; margin-top: 6px; resize: vertical; line-height: 1.5;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 6px 8px; font-size: 13.5px; outline: none; color: inherit;
  font-family: inherit;
}
.mz-unite-notes textarea:focus { border-color: var(--accent); }

/* Les onze compétences d'une unité (lot 25) : onze cases sur une ligne ou
   deux, pas onze lignes. Le code de trois lettres tient au-dessus d'un champ
   de quarante pixels ; le nom complet est dans l'infobulle du `<label>`. */
.mz-unite-competences { margin-top: 8px; }
.mz-unite-competences summary {
  font-size: 12px; color: var(--texte-faible); cursor: pointer;
  text-transform: uppercase; letter-spacing: .07em;
  display: flex; align-items: center; gap: 6px;
}
.mz-unite-competences summary .nombre {
  font-size: 11px; background: var(--fond-3); border-radius: 9px; padding: 0 6px;
  letter-spacing: 0;
}
.mz-comps-armee { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.mz-comp-armee { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.mz-comp-code {
  font-size: 10.5px; letter-spacing: .06em; color: var(--texte-faible);
  font-variant-numeric: tabular-nums;
}
.mz-comp-armee input {
  width: 40px; text-align: center;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 6px;
  padding: 4px 2px; font-size: 13px; outline: none; color: inherit;
  font-family: inherit;
}
.mz-comp-armee input:focus { border-color: var(--accent); }

.mz-rang-ligne { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.mz-rang-titre { flex: 0 0 92px; font-size: 13px; color: var(--texte-doux); }
.mz-rang-vide { font-size: 12.5px; color: var(--texte-faible); font-style: italic; }
.mz-rang-ligne .mz-personnes { margin-top: 0; }

.mz-lien {
  display: flex; align-items: center; gap: 8px; padding: 5px 0;
  border-bottom: 1px dashed var(--bord); font-size: 13.5px;
}
.mz-lien:last-child { border-bottom: 0; }
.mz-lien.revolu { opacity: .58; }
.mz-lien.orphelin .mz-personne { text-decoration: line-through; }
.mz-lien-type { font-size: 12.5px; font-weight: 600; }
.mz-lien-fleche { color: var(--texte-faible); }
.mz-revolu {
  font-size: 11.5px; color: var(--texte-faible); border: 1px solid var(--bord);
  border-radius: 9px; padding: 0 6px;
}

@media (max-width: 900px) {
  .mz-colonnes { flex-direction: column; }
  .mz-colonne { flex: 0 0 auto; overflow-y: visible; }
  .mz-colonnes { overflow-y: auto; }
  .mz-histoire { border-right: 0; border-bottom: 1px solid var(--bord); }
}

.plan-tableau {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: var(--fond-2);
}
.tb-onglets {
  display: flex; gap: 2px; padding: 8px 12px 0; flex-wrap: wrap;
  border-bottom: 1px solid var(--bord);
}
.tb-onglet {
  display: flex; align-items: center; gap: 7px;
  background: none; border: 1px solid transparent; border-bottom: none;
  border-radius: 8px 8px 0 0; padding: 7px 13px; cursor: pointer;
  font-size: 14px; color: var(--texte-doux); margin-bottom: -1px;
}
.tb-onglet:hover { background: var(--fond-3); }
.tb-onglet.actif {
  background: var(--fond-2); border-color: var(--bord); color: var(--texte);
  font-weight: 600;
}
.tb-onglet .nombre {
  font-size: 11.5px; color: var(--texte-faible); background: var(--fond-3);
  border-radius: 20px; padding: 1px 7px; font-variant-numeric: tabular-nums;
}
.tb-barre {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid var(--bord);
}
.tb-espace { flex: 1; }
.tb-filtre {
  width: 240px; padding: 6px 10px; font-size: 13.5px; font-family: inherit;
  background: var(--fond-3); border: 1px solid var(--bord);
  border-radius: 8px; color: var(--texte);
}
.tb-filtre:focus { outline: none; border-color: var(--accent); }
.tb-info { font-size: 12.5px; color: var(--texte-faible); font-variant-numeric: tabular-nums; }
/* La barre flottante du bas passe par-dessus : on garde de quoi faire
   défiler la dernière ligne au-dessus d'elle. */
.tb-zone { flex: 1; overflow: auto; padding: 0 0 72px; }
.tb-table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 13.5px; }
.tb-table th {
  position: sticky; top: 0; z-index: 2;
  background: var(--fond-3); color: var(--texte-doux);
  text-align: left; font-weight: 600; white-space: nowrap;
  padding: 8px 12px; border-bottom: 1px solid var(--bord-clair);
  cursor: pointer; user-select: none;
}
.tb-table th:hover { color: var(--texte); }
.tb-table th.trie { color: var(--accent); }
.tb-fleche { font-size: 10.5px; }
.tb-table td {
  padding: 6px 12px; border-bottom: 1px solid var(--bord);
  max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--texte-doux);
}
.tb-table tbody tr:hover td { background: var(--fond-3); }
.tb-table tr.cliquable { cursor: pointer; }
.tb-table td.nombre, .tb-table th.nombre { text-align: right; font-variant-numeric: tabular-nums; }
.tb-table td:first-child { color: var(--texte-faible); font-size: 12.5px; }
.tb-plus { display: block; margin: 14px auto; }

/* ------------------------------------------------- édition par procuration
 *
 * Un administrateur écrit dans l'arbre de quelqu'un d'autre. Le bandeau ne se
 * referme pas : on ne doit jamais oublier chez qui l'on est. Ce qui porterait
 * sur *son* compte à lui (tout télécharger, instantané) sort de la barre —
 * ces gestes n'auraient pas le sens qu'on leur prêterait.
 */
.bandeau-procuration {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 14px; font-size: 13.5px; line-height: 1.4;
  background: #fdf3d8; color: #7a5c12; border-bottom: 1px solid #e0c169;
}
.bandeau-procuration .bouton { margin-left: auto; }
body.sombre .bandeau-procuration { background: #352c12; color: #e8c96a; border-bottom-color: #6b5620; }
body.en-procuration #btn-telecharger,
body.en-procuration #btn-instantane { display: none; }

/* ------------------------------------------------ arbre partagé (11.B)
 *
 * Le contraire de la procuration : on regarde sans pouvoir écrire. Le serveur
 * refuse déjà tout verbe autre que GET sur `/api/partages/<id>/lecture` — ces
 * règles ne sont donc pas la sécurité, elles sont l'honnêteté. **Une interface
 * qui propose ce que l'API refuse ment**, et laisse croire qu'on a cassé
 * quelque chose alors qu'on a simplement tenté ce qui n'existe pas.
 */
.bandeau-procuration .bouton { margin-left: auto; }
body.en-partage .bandeau-procuration {
  background: #e7f0f7; color: #2d4f6b; border-bottom-color: #a8c4d8;
}
body.sombre.en-partage .bandeau-procuration {
  background: #16242e; color: #93c1de; border-bottom-color: #2c4657;
}
/* Ce qui reste caché dans les deux cas : « ⤓ Tout télécharger », « 📸 Instantané »
   et les gestes du rail portent sur **le compte** et sur la sauvegarde comme
   objet — ils sont au propriétaire, jamais à l'invité, fût-il rédacteur. */
body.en-partage #btn-telecharger,
body.en-partage #btn-instantane,
body.en-partage .rail-actions { display: none; }

/* Ce qui n'est caché qu'en **lecture** (lot 23.D). Le faire par `:not()` plutôt
   que par un `display` rendu plus bas : ces éléments n'ont pas tous le même
   mode d'affichage, et le deviner en rendrait un de travers. Une interface qui
   ne propose pas ce qui marche ment autant qu'une qui propose ce qui échoue. */
body.en-partage:not(.en-partage-ecriture) #btn-nouveau-profil,
body.en-partage:not(.en-partage-ecriture) #champ-annee,
body.en-partage:not(.en-partage-ecriture) #btn-nouveau-filtre,
body.en-partage:not(.en-partage-ecriture) .etat-ecriture { display: none; }

/* Le bandeau vert dit « vous pouvez écrire » ; le bleu disait « vous regardez ».
   Deux couleurs, parce que la différence entre les deux est ce qu'on risque
   d'oublier au bout de dix minutes. */
body.en-partage-ecriture .bandeau-procuration {
  background: #eaf3ec; color: #2f5d3f; border-bottom-color: #a9ccb4;
}
body.sombre.en-partage-ecriture .bandeau-procuration {
  background: #17271c; color: #96c9a6; border-bottom-color: #2d4a37;
}
/* La liste des personnes reste ouvrable — c'est de la lecture — mais la fiche
   n'y est plus modifiable : `pointer-events` couperait aussi le défilement. */
body.en-partage .sauvegarde { cursor: default; }

/* ------------------------------------------------------ essai sans compte
 *
 * Le contraire de la procuration : ici il ne s'agit pas d'avertir mais
 * d'inviter. Le bandeau est neutre tant que le visiteur regarde, et prend de
 * la voix — sans jamais bloquer — dès qu'il a modifié quelque chose.
 */
.bandeau-essai {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 14px; font-size: 13.5px; line-height: 1.4;
  background: var(--fond-3); color: var(--texte-faible);
  border-bottom: 1px solid var(--bord);
}
.bandeau-essai-texte { flex: 1; min-width: 200px; }
.bandeau-essai .bouton { flex: 0 0 auto; }
.bandeau-essai-fermer { opacity: .6; }
.bandeau-essai-fermer:hover { opacity: 1; }
.bandeau-essai.presse {
  background: var(--accent-doux); color: var(--accent-fonce);
  border-bottom-color: var(--accent);
}
body.sombre .bandeau-essai.presse { color: #7fd8c6; }

.groupe-essai { display: flex; align-items: center; gap: 6px; }
/* Un essai n'a pas d'instantané à ranger : la copie datée n'a de sens que
   pour quelqu'un qui reviendra la chercher. */
body.en-essai #btn-instantane { display: none; }

/* ------------------------------------------------------- démonstration (14)
 *
 * Ni un avertissement de procuration (on ne risque pas d'abîmer le travail de
 * quelqu'un) ni une invitation d'essai (il n'y a rien à garder) : le seul
 * endroit du produit où **ce qu'on fait ne sera pas conservé**. D'où une
 * couleur à lui, une teinte d'encre plutôt que d'alarme, et aucun bouton de
 * fermeture — un avertissement qu'on peut faire taire n'en est pas un.
 */
.bandeau-demo {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 14px; font-size: 13.5px; line-height: 1.4;
  background: #eef0f7; color: #4b4f6b; border-bottom: 1px solid #c3c8e0;
}
.bandeau-demo-texte { flex: 1; min-width: 200px; }
.bandeau-demo .bouton { flex: 0 0 auto; }
body.sombre .bandeau-demo { background: #23253a; color: #b9bde0; border-bottom-color: #3d4166; }
/* Après une écriture, le ton change : là il y a quelque chose à perdre —
   exactement comme le bandeau d'essai, et pour la même raison. */
.bandeau-demo.presse { background: #fdf3d8; color: #7a5c12; border-bottom-color: #e0c169; }
body.sombre .bandeau-demo.presse { background: #352c12; color: #e8c96a; border-bottom-color: #6b5620; }

/* La démonstration se reconnaît dans le rail sans avoir à lire son nom. */
.sauvegarde.demo .sv-pastille { background: #8b90c4; }

/* --------------------------------------------------------- tutoriel (14.C)
 *
 * Le trou du projecteur est fait par une seule ombre de 9999 px : l'élément
 * couvre la cible, et son ombre assombrit tout le reste. Un calque en quatre
 * morceaux aurait quatre positions à tenir d'accord avec celle de la cible.
 */
.tuto { position: fixed; inset: 0; z-index: 90; }
.tuto.tuto-sans-cible { background: rgba(12, 18, 28, .55); }
.tuto-halo {
  position: fixed; border-radius: 10px; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(12, 18, 28, .55), 0 0 0 2px var(--accent) inset;
  transition: all .18s ease;
}
.tuto-carte {
  position: fixed; width: min(340px, calc(100vw - 24px));
  background: var(--carte-fond); color: var(--texte);
  border: 1px solid var(--bord); border-radius: var(--rayon);
  box-shadow: var(--carte-ombre-forte); padding: 14px 16px 12px;
}
.tuto-compteur { font-size: 12.5px; color: var(--texte-faible); letter-spacing: .04em; }
.tuto-titre { margin: 2px 0 6px; font-family: var(--police-titre); font-size: 17px; }
.tuto-texte { margin: 0 0 12px; font-size: 14px; line-height: 1.5; color: var(--texte-doux); }
.tuto-texte b { color: var(--texte); }
.tuto-actions { display: flex; gap: 6px; align-items: center; }
/* « Plus tard » à gauche, seul : une sortie ne se cherche pas parmi les
   boutons qui font avancer. */
.tuto-passer { margin-right: auto; color: var(--texte-faible); }

/* ---------------------------------------------------------------- carnet
 *
 * Le carnet a **deux places** et un seul jeu de règles : volet à côté du plan
 * (étroit, sommaire replié) ou pleine scène (`.carnet-large`, sommaire en
 * colonne permanente). Ce qui change tient en trois déclarations — le reste
 * est commun, sinon les deux places se mettraient à diverger. */

/* Étirable depuis le lot 27.A — même poignée et même mécanisme que la fiche
   de droite (`installerEtirement`, dans js/main.js). `position: relative` est
   pour la poignée : sans lui, son `left: -3px` se positionnerait par rapport
   à `.corps`, le premier ancêtre positionné plus haut, pas par rapport au
   bord du volet. La largeur par défaut suit la fenêtre plutôt qu'un 400 px
   unique pour tout le monde ; `clamp` lui garde un plancher — en dessous de
   320 px, voir le commentaire sur `.cn-sommaire-ouvert` plus bas. */
.volet-carnet {
  position: relative;
  width: var(--carnet-largeur, clamp(320px, 34vw, 520px));
  flex: 0 0 var(--carnet-largeur, clamp(320px, 34vw, 520px));
  background: var(--fond-2);
  border-left: 1px solid var(--bord);
  min-height: 0; min-width: 0;
  display: flex;
}

.carnet {
  position: relative; /* la liste de complétion se pose dedans */
  display: flex; flex-direction: column;
  flex: 1; min-width: 0; min-height: 0;
  background: var(--fond-2);
}
/* Pleine scène : monté à même `#scene` (`main.js`, `enregistrerRendu('carnet',
   …)`), exactement comme `.vue-perso` et `.vue-maisons`. `#scene` est un
   `<main>`, `display: block` par défaut — `.scene { flex: 1 }` le dimensionne
   bien lui-même dans `.corps`, mais ne fait pas de ses propres enfants des
   items flexibles pour autant. Sans ce `position: absolute; inset: 0`,
   `.carnet` restait un `flex: 1` sans effet et grandissait à la hauteur de
   son contenu — mesuré le 22/08/2026, 1727 px pour 536 disponibles, dont
   1191 px introuvables au défilement (lot 27.A). Une fois borné à la boîte de
   la scène, `.cn-sommaire` et `.cn-rendu` (déjà `overflow-y: auto`) et
   `.cn-note` (déjà `min-height: 0`) défilent tout seuls. */
.carnet-large {
  position: absolute; inset: 0;
  background: var(--fond-2);
}

.cn-barre {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 8px; flex: 0 0 auto;
  border-bottom: 1px solid var(--bord);
}
.cn-fil {
  flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 5px;
  font-size: 14px; white-space: nowrap; overflow: hidden;
}
.cn-fil-chapitre { color: var(--texte-faible); }
.cn-fil-sep { color: var(--texte-faible); }
.cn-fil-note { color: var(--texte); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.cn-etat { font-size: 12.5px; color: var(--texte-faible); white-space: nowrap; }
.cn-etat.modifie { color: var(--texte-doux); }
.cn-etat.ok { color: var(--accent-fonce); }
.cn-etat.erreur { color: #b4483a; }
.cn-btn-mode { white-space: nowrap; }

/* L'avertissement de la démonstration, à l'endroit où l'on perdrait le plus.
   Une ligne, pas un bandeau : il ne doit pas manger la place du texte. */
.cn-avertissement {
  flex: 0 0 auto; margin: 0; padding: 5px 10px;
  background: var(--accent-doux); border-bottom: 1px solid var(--bord);
  font-size: 12.5px; line-height: 1.4; color: var(--accent-fonce);
}

.cn-corps { flex: 1; display: flex; min-height: 0; min-width: 0; position: relative; }

/* sommaire */
.cn-sommaire {
  display: none;
  flex-direction: column; gap: 2px;
  padding: 8px; overflow-y: auto;
  background: var(--fond-3);
  border-right: 1px solid var(--bord);
}
/* Étroit : le sommaire se pose **par-dessus** la note, plutôt qu'à côté.
   Depuis le lot 27.A le volet s'étire, mais sa borne basse reste 320 px
   (`CARNET_MIN`, js/main.js) : lui prendre 230 px pour une liste laisserait
   90 px de texte au pire des cas, ce qui n'est plus une note mais une colonne
   de mots. Une vraie colonne resterait à l'étroit même une fois le volet
   tiré — d'où l'overlay, quelle que soit la largeur retenue. */
.cn-sommaire-ouvert .cn-sommaire {
  display: flex;
  position: absolute; left: 0; top: 0; bottom: 0; width: 78%;
  z-index: 5; box-shadow: var(--ombre-panneau);
}
.carnet-large .cn-sommaire {
  display: flex; position: static; width: 250px; flex: 0 0 250px; box-shadow: none;
}
.carnet-large .cn-btn-sommaire { display: none; }

.cn-sommaire-entete { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.cn-sommaire-entete h3 {
  flex: 1; margin: 0; font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--texte-faible);
}
.cn-groupe { margin-bottom: 10px; }
.cn-groupe-titre { display: flex; align-items: center; gap: 4px; margin-bottom: 3px; }
.cn-groupe-titre .nombre {
  font-size: 12px; color: var(--texte-faible); font-variant-numeric: tabular-nums;
}
.cn-hors-chapitre {
  font-size: 12.5px; color: var(--texte-faible); text-transform: uppercase; letter-spacing: .08em;
}
.cn-chap-titre {
  flex: 1; min-width: 0; background: transparent; border: 1px solid transparent;
  border-radius: 5px; padding: 3px 5px; font-size: 13.5px; font-weight: 600;
}
.cn-chap-titre:hover:not([readonly]) { border-color: var(--bord); }
.cn-chap-titre:focus { background: var(--fond-2); border-color: var(--accent); outline: none; }

.cn-ligne {
  display: flex; align-items: center; gap: 6px; width: 100%;
  background: transparent; border: 0; border-radius: 6px;
  padding: 6px 8px; text-align: left; cursor: pointer; color: var(--texte-doux);
  font-size: 14px;
}
.cn-ligne:hover { background: var(--fond-2); color: var(--texte); }
.cn-ligne.actif { background: var(--accent-doux); color: var(--accent-fonce); font-weight: 600; }
.cn-ligne-titre { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cn-ligne-cites { font-size: 12px; color: var(--texte-faible); white-space: nowrap; }
.cn-nouvelle-note { width: 100%; margin-top: 4px; }
.cn-danger:hover { color: #b4483a; border-color: #e0b4ac; }

/* la note */
.cn-note { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.cn-titre {
  flex: 0 0 auto; margin: 10px 12px 6px; padding: 4px 6px;
  background: transparent; border: 1px solid transparent; border-radius: 6px;
  font-family: var(--police-titre); font-size: 18px; font-weight: 600;
}
.cn-titre:hover:not([readonly]) { border-color: var(--bord); }
.cn-titre:focus { background: var(--fond-3); border-color: var(--accent); outline: none; }

.cn-outils {
  flex: 0 0 auto; display: flex; align-items: center; gap: 2px;
  padding: 0 10px 6px; overflow-x: auto; scrollbar-width: thin;
}
.cn-outil { min-width: 26px; height: 26px; font-size: 13px; padding: 0 5px; flex: 0 0 auto; }
.cn-outil-gras { font-weight: 800; }
.cn-outil-italique { font-style: italic; font-family: var(--police-titre); }
.cn-outil-barre { text-decoration: line-through; }
.cn-outil-balise { color: var(--accent-fonce); font-weight: 700; }
.cn-outils-sep { width: 1px; height: 16px; background: var(--bord); margin: 0 3px; flex: 0 0 auto; }

.cn-source {
  flex: 1; min-height: 0; resize: none;
  margin: 0 10px; padding: 10px 12px;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: var(--rayon);
  font-size: 15px; line-height: 1.65; tab-size: 2;
}
.cn-source:focus { outline: none; border-color: var(--accent); background: var(--fond-2); }

.cn-rendu {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 2px 14px 10px; font-size: 15px; line-height: 1.65; color: var(--texte);
}

.cn-pied {
  flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
  padding: 7px 10px; border-top: 1px solid var(--bord);
}
.cn-pied-aide { flex: 1; min-width: 0; font-size: 12px; color: var(--texte-faible); }
.cn-chapitre-choix { font-size: 13px; max-width: 45%; }

.cn-vide { padding: 26px 20px; color: var(--texte-doux); font-size: 14px; line-height: 1.6; }
.cn-vide-titre { font-family: var(--police-titre); font-size: 17px; color: var(--texte); margin: 0 0 8px; }
.cn-vide .bouton { margin-top: 12px; }

/* complétion « / » */
.cn-suggestions {
  position: absolute; z-index: 20; margin: 0; padding: 4px; list-style: none;
  min-width: 230px; max-width: 320px;
  background: var(--fond-2); border: 1px solid var(--bord-clair);
  border-radius: var(--rayon); box-shadow: var(--ombre-panneau);
}
.cn-suggestions li {
  display: flex; align-items: baseline; gap: 6px;
  padding: 5px 7px; border-radius: 5px; cursor: pointer; font-size: 14px;
}
.cn-suggestions li.survol { background: var(--accent-doux); }
.cn-sug-ico { flex: 0 0 auto; font-size: 12.5px; }
.cn-sug-nom { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cn-sug-detail { font-size: 12px; color: var(--texte-faible); white-space: nowrap; }

/* --------------------------------------------------------------- markdown */

.cn-rendu h1, .cn-rendu h2, .cn-rendu h3,
.cn-rendu h4, .cn-rendu h5, .cn-rendu h6 {
  font-family: var(--police-titre); margin: 16px 0 6px; line-height: 1.3;
}
.cn-rendu h1 { font-size: 20px; }
.cn-rendu h2 { font-size: 17.5px; }
.cn-rendu h3 { font-size: 15.5px; }
.cn-rendu h4, .cn-rendu h5, .cn-rendu h6 { font-size: 15px; color: var(--texte-doux); }
.cn-rendu p { margin: 8px 0; }
.cn-rendu ul, .cn-rendu ol { margin: 8px 0; padding-left: 22px; }
.cn-rendu li { margin: 2px 0; }
.cn-rendu blockquote {
  margin: 10px 0; padding: 4px 12px;
  border-left: 3px solid var(--accent); color: var(--texte-doux); font-style: italic;
}
.cn-rendu hr { border: 0; border-top: 1px solid var(--bord); margin: 14px 0; }
.cn-rendu code {
  background: var(--fond-3); border: 1px solid var(--bord);
  border-radius: 4px; padding: 0 4px; font-size: 13.5px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}
.cn-rendu pre.md-code {
  margin: 10px 0; padding: 9px 11px; overflow-x: auto;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: var(--rayon);
}
.cn-rendu pre.md-code code { background: none; border: 0; padding: 0; }
.cn-rendu a { color: var(--accent-fonce); }
/* Un tableau large défile **dans sa boîte** : sans ça, c'est la note entière
   qui se met à glisser latéralement, et le texte autour part avec. */
.md-table-boite { overflow-x: auto; margin: 10px 0; }
.md-table { border-collapse: collapse; font-size: 14px; min-width: 100%; }
.md-table th, .md-table td {
  border: 1px solid var(--bord); padding: 5px 8px; text-align: left; vertical-align: top;
}
.md-table th { background: var(--fond-3); font-weight: 600; }

/* les balises */
.balise {
  display: inline-flex; align-items: baseline; gap: 3px;
  background: var(--accent-doux); border: 1px solid transparent;
  border-radius: 5px; padding: 0 5px; margin: 0 1px;
  color: var(--accent-fonce); font: inherit; font-size: .95em; font-weight: 600;
  cursor: pointer; vertical-align: baseline;
}
.balise:hover { border-color: var(--accent); }
.balise-ico { font-size: .8em; opacity: .8; }
/* Une cible supprimée : la note garde ce qu'elle disait, mais elle ne mène
   plus nulle part — et ça se voit. */
.balise-perdue {
  background: var(--fond-3); color: var(--texte-faible);
  border-style: dashed; border-color: var(--bord-clair);
  text-decoration: line-through; cursor: not-allowed;
}
.balise-visee {
  outline: 2px solid var(--accent); outline-offset: 2px;
  background: var(--accent); color: #fff;
}

/* citations, dans la fiche d'une personne */
/* Une section repliable de la fiche : le titre prend la place du `<h3>` et
   porte le chevron. Deux sections s'en servent — les citations du carnet et
   l'humeur envers les joueurs — et toute autre qui deviendrait trop longue. */
.pn-repliable > summary {
  cursor: pointer; list-style: none;
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible);
}
.pn-repliable > summary::-webkit-details-marker { display: none; }
/* Le chevron dit d'un coup d'œil qu'il y a quelque chose dessous — et de quel
   côté. Sans lui, un `<summary>` déshabillé de sa flèche n'a plus l'air
   cliquable du tout. */
.pn-repliable > summary::before { content: '▸ '; letter-spacing: 0; }
.pn-repliable[open] > summary::before { content: '▾ '; }
.pn-repliable[open] > summary { margin-bottom: 8px; }
.pn-repliable > summary:hover { color: var(--texte-doux); }
.pn-repli-titre { display: inline-flex; align-items: baseline; gap: 6px; }
.pn-repli-nombre {
  font-size: 12px; color: var(--texte-faible); font-variant-numeric: tabular-nums;
}
.pn-cit-chapitre {
  margin: 10px 0 4px; font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--texte-faible);
}
.pn-cit-note { margin-bottom: 8px; }
.pn-cit-note-titre {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 13.5px; font-weight: 600; color: var(--texte-doux); margin-bottom: 3px;
}
.pn-cit-extrait {
  display: block; width: 100%; text-align: left;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 6px;
  padding: 5px 8px; margin: 3px 0; cursor: pointer;
  font-size: 13.5px; line-height: 1.5; color: var(--texte-doux);
}
.pn-cit-extrait:hover { border-color: var(--accent); color: var(--texte); }
.pn-cit-extrait b { color: var(--accent-fonce); }

/* ------------------------------------------------------------ responsive */

@media (max-width: 1180px) {
  .rail { position: absolute; left: 0; top: 0; bottom: 0; z-index: 18; box-shadow: var(--ombre-panneau); }
  .fil-ariane { display: none; }
}
@media (max-width: 860px) {
  .panneau { position: absolute; right: 0; top: 0; bottom: 0; z-index: 19; width: 300px; flex-basis: 300px; }
}

/* Le carnet en volet mange la scène avant tout le monde : à 1180 px il reste
   large, à 1040 il passe par-dessus le plan plutôt que de le réduire à une
   bande. Un plan de 200 px de large ne montre plus de réseau — autant assumer
   le recouvrement et laisser une sortie visible (le ✕ de son en-tête). */
@media (max-width: 1180px) {
  .volet-carnet { width: 360px; flex-basis: 360px; }
}
@media (max-width: 1040px) {
  .volet-carnet {
    position: absolute; right: 0; top: 0; bottom: 0; z-index: 20;
    width: 380px; flex-basis: 380px; box-shadow: var(--ombre-panneau);
  }
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--bord-clair); border-radius: 6px; border: 2px solid var(--fond-2); }
::-webkit-scrollbar-thumb:hover { background: var(--texte-faible); }

/* ------------------------------------------------- compte et téléphone */

.groupe-compte { display: flex; align-items: center; gap: 6px; }
.compte {
  font-size: 12.5px; color: var(--texte-faible);
  max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#aide-plafonds { margin: 7px 0 0; }

/* Boutons qui n'existent que sur téléphone : sur écran large, les deux volets
   sont déjà là et n'ont besoin ni d'être ouverts ni d'être fermés. */
.btn-telephone { display: none; }
.btn-fermer-volet { margin-left: auto; align-self: center; margin-right: 6px; }

/* L'en-tête du tiroir de gauche a disparu au lot 13.A, y compris son balisage.
 * Il ne servait que sur téléphone, où il portait le mot « Menu » et une croix
 * — mesuré **341 × 61 px, collants**, sur un tiroir de 694 px de haut. Neuf
 * pour cent de la hauteur utile, en permanence, pour un titre qu'on n'a pas
 * besoin de lire et une sortie qui existe ailleurs. */

/* ------------------------------------------------------------ téléphone
 *
 * L'arbre s'édite au doigt depuis le lot 8 : l'appui long ouvre le même menu
 * que le clic droit (fiche, lien, fond), « ＋ Profil » crée quelqu'un sans
 * viser, et un lien armé se termine d'une simple touche. Restent à la souris
 * le glisser d'une fiche et la poignée ＋.
 *
 * Les deux volets deviennent des tiroirs plein écran, ouverts par ☰ et 👤.
 *
 * **Repris le 13/08/2026 (lot 11.C).** La barre du haut réclamait 512 px de
 * commandes dans 375 px d'écran : les quatre derniers boutons débordaient à
 * droite, dont le ☰ lui-même, à 75 px hors de l'écran. Le rail — sauvegardes,
 * vues, joueurs, liens, maisons, options — était donc entier et strictement
 * inatteignable, et c'est ce qu'on nous a signalé.
 *
 * Depuis, la barre ne garde que de quoi **naviguer** : la marque, ☰, 👤 (et
 * « Créer un compte » pour un visiteur d'essai). Tout le reste descend dans le
 * rail, en tête, par `public/js/telephone.js` — déplacé et non dupliqué, pour
 * qu'il n'y ait jamais deux boutons pour un même geste.
 */
@media (max-width: 760px) {
  .btn-telephone { display: inline-flex; }

  .barre-haut { gap: 8px; padding: 7px 10px; flex-wrap: nowrap; }
  /* Un plancher, et non `min-width: 0`. Mesuré le 14/08/2026, boutons d'essai
     encore dans la barre : la marque tombait à **9 px** de large — ni épée, ni
     nom d'univers. `.marque-univers` sait déjà se rogner tout seul ; ce plancher
     fait que c'est lui qui perd des lettres, et non l'ensemble qui disparaît. */
  .marque { min-width: 5.5rem; }
  .marque-titre { display: none; }
  .marque-univers { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* `flex: none` sur les deux boutons de tiroir : sans ça, un nom d'univers
     long les comprimerait jusqu'à l'illisible plutôt que de se laisser rogner
     lui-même — et c'est lui qui peut se permettre de perdre des lettres. */
  .barre-controles { margin-left: auto; gap: 6px; flex: none; }
  /* La marque doit rester lisible. Mesuré le 14/08/2026 avec les boutons
     d'essai encore dans la barre : elle tombait à **9 px** de large — ni épée,
     ni nom d'univers. Un plancher, pour que ce soit le nom qui perde des
     lettres et non l'ensemble qui disparaisse. */
  .marque { min-width: 5.5rem; }
  /* ☰ et 👤 sont la navigation entière de cet écran : ils méritent la même
     cible que le reste. `flex: none` en plus, pour qu'un nom d'univers long
     les comprime jamais — c'est lui qui peut se permettre de perdre des
     lettres, pas eux. */
  .barre-haut .bouton { min-height: 40px; }
  #btn-panneau, #btn-compte { flex: none; min-width: 40px; }

  /* ☰ s'en va (13.A). Signalé le 14/08/2026 : « retire le kebab menu, qui
     prend les mêmes options que la petite icône de tête ». C'est exact — il
     ouvrait le même tiroir que 👤, et que « ⛨ Filtres » de la barre du bas.
     Trois boutons pour un tiroir, sur l'écran où la place manque le plus.
     Restent les deux qui disent *où* ils mènent : ⛨ aux filtres, 👤 au compte. */
  #btn-rail { display: none; }
  /* Ce qui ne sert pas à naviguer quitte la barre. `telephone.js` le descend
     dans le rail ; ces règles restent la ceinture, si le script n'a pas encore
     tourné — sauf pour ☰ et 👤, qui doivent tenir dans tous les cas. */
  .barre-haut .champ-couleur,
  .barre-haut #lien-document,
  .barre-haut #btn-vue-generale,
  .barre-haut #champ-annee,
  .barre-haut #btn-theme,
  .barre-haut #lien-admin,
  .barre-haut #lien-donnees,
  .barre-haut #btn-deconnexion,
  .barre-haut .etat-ecriture,
  .barre-haut .compte { display: none; }

  /* Éteints tout court, pas déplacés. Un `.zip` de toutes ses sauvegardes sur
     un téléphone ne mène nulle part, et un instantané se prend quand on
     travaille. « Vos données » (🛡) reste, et c'est le vrai endroit pour sortir
     ses données. */
  #btn-telecharger,
  #btn-instantane { display: none; }

  /* L'ordre du tiroir, refait pour un pouce.
   *
   * On l'ouvre pour filtrer — les maisons et les types de liens passent donc en
   * tête, et les réglages de compte à la fin. L'ordre du HTML, lui, ne bouge
   * pas : c'est celui d'un écran large, où tout est visible d'un coup.
   *
   * Trois blocs n'ont pas d'identifiant et se désignent par ce qu'ils
   * contiennent. Là où `:has()` manquerait, eux seuls resteraient à leur place
   * du HTML : l'ordre serait moins bon, rien ne casserait. */
  .rail { display: flex; flex-direction: column; }
  #bloc-maisons { order: 1; }
  #bloc-liens { order: 2; }
  .rail > section:has(#liste-vues) { order: 3; }
  .rail > section:has(#options-vue) { order: 4; }
  #bloc-joueurs { order: 5; }
  .rail > section:has(#liste-sauvegardes) { order: 6; }
  #bloc-partages { order: 7; }
  .rail > section:has(#stats) { order: 9; }
  #bloc-compte { order: 10; }
  #bloc-telephone { order: 11; }

  /* Les commandes descendues : en grille, avec des cibles qu'un doigt atteint
     sans viser. 44 px est la hauteur en dessous de laquelle on tape à côté. */
  #accueil-telephone,
  #accueil-compte {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: 8px; align-items: stretch;
  }
  #accueil-telephone .bouton { min-height: 44px; justify-content: center; }
  #accueil-telephone .champ { flex-direction: column; align-items: stretch; gap: 3px; }
  #accueil-telephone .champ select { min-height: 44px; }

  /* Le bloc « Votre compte » (13.B). Une colonne, pleine largeur : ce sont
     quatre gestes rares et distincts — voir son compte, administrer, sortir ses
     données, se déconnecter — et non une grille de réglages à parcourir. */
  #accueil-compte { grid-template-columns: 1fr; }
  #accueil-compte .bouton {
    min-height: 46px; justify-content: flex-start; gap: 8px; font-size: 15px;
  }
  /* C'est ici, et ici seulement, que ⚙ 🛡 ⏻ disent leur nom. */
  #accueil-compte #lien-admin .lib,
  #accueil-compte #lien-donnees .lib,
  #accueil-compte #btn-deconnexion .lib { display: inline; }
  #accueil-compte .bouton .ico { margin-right: 2px; font-size: 17px; opacity: 1; }
  #accueil-compte #btn-deconnexion { border-color: var(--bord); color: var(--texte-faible); }
  /* L'adresse du compte n'est pas un bouton : elle occupe sa propre ligne. */
  #accueil-compte .compte { max-width: none; font-size: 14.5px; padding: 2px 0; }
  /* « Créer un compte » et « Se connecter », descendus ici depuis la barre du
     haut. Ils se partagent la ligne : c'est tout ce que voit un visiteur sans
     compte qui touche 👤, et il n'a rien à chercher. */
  #accueil-compte .groupe-essai { gap: 8px; }
  #accueil-compte .groupe-essai .bouton { flex: 1; min-height: 46px; justify-content: center; }

  /* Le bandeau d'essai : le texte, pas le bouton.
   *
   * Il portait la troisième « Créer un compte » de l'écran, et réclamait 103 px
   * de haut sur 812 — 13 % de l'écran pour un rappel. Le bouton s'en va tant
   * que le bandeau est calme : le 👤 de la barre est à un doigt, et le texte
   * dit déjà quoi faire.
   *
   * Il revient dès que le bandeau est `.presse`, c'est-à-dire dès qu'on a
   * modifié quelque chose et qu'il y a du travail à perdre. C'est exactement la
   * distinction sur laquelle ce bandeau a été construit — discret tant qu'on
   * regarde, insistant dès qu'on écrit — et non une exception de plus. */
  .bandeau-essai { padding: 6px 10px; font-size: 13px; gap: 8px; }
  .bandeau-essai:not(.presse) .bouton-primaire { display: none; }
  .bandeau-essai.presse .bouton-primaire { min-height: 36px; }
  .bandeau-essai-texte { min-width: 0; }
  .bandeau-essai-fermer { min-width: 36px; min-height: 36px; }

  /* Le bandeau de démonstration : même règle, même raison. Mesuré à **85 px
   * sur 812** avec ses deux boutons — ils passaient à la ligne, et l'avertis-
   * sement coûtait 10 % de l'écran en permanence. Le texte, lui, reste : c'est
   * lui qui porte « rien n'est conservé », et il tient sur une ligne et demie.
   *
   * « ? Tutoriel » disparaît tout court : la visite guidée a déjà sa porte dans
   * le tiroir, sous 👤. « Créer un compte » / « En faire mon monde » revient
   * dès qu'on a écrit — le moment où il y a quelque chose à perdre. */
  .bandeau-demo { padding: 6px 10px; font-size: 13px; gap: 8px; }
  .bandeau-demo #btn-demo-tutoriel { display: none; }
  .bandeau-demo:not(.presse) #btn-demo-garder { display: none; }
  .bandeau-demo.presse #btn-demo-garder { min-height: 36px; }
  .bandeau-demo-texte { min-width: 0; }

  /* Cibles tactiles dans le rail.
   *
   * Mesuré le 13/08/2026, tiroir ouvert sur 375 px : une entrée de légende
   * faisait **24 px** de haut, la poignée ＋ des filtres 28×19, une puce d'axe
   * 52×21, la case « masquer les liens révolus » 13×13. Ce sont exactement les
   * commandes qu'on nous disait ne pas pouvoir utiliser — atteignables, mais
   * pas touchables. En dessous de 40 px, un doigt tape à côté.
   *
   * On agrandit par le remplissage plutôt que par la police : la légende doit
   * pouvoir montrer vingt maisons sans devenir un livre. */
  /* « ＋ Nouvelle maison », « ＋ Nouveau type », « ＋ Nouveau joueur » : 26 px
     de haut et 11 px de police. Ce sont les « options d'ajouts » qu'on nous
     disait inutilisables — elles étaient là, simplement trop fines pour un
     doigt. */
  .rail .rail-actions { gap: 6px; }
  .rail .rail-actions .bouton { min-height: 44px; padding: 8px 6px; font-size: 14px; }
  .rail .legende li { min-height: 40px; padding: 8px 8px; }
  .rail .liste-vues li,
  .rail .liste-joueurs li,
  .rail .liste-sauvegardes li { min-height: 44px; }
  .rail .axe-puce { min-height: 36px; padding: 6px 12px; font-size: 13.5px; }
  .rail .btn-entete,
  .rail .rail-plier,
  .rail .joueur-rail-fiche { min-width: 40px; min-height: 40px; font-size: 17px; }
  /* Les onglets du rail (lot 21.A) : au doigt aussi, et sans se coller au bord
     supérieur du tiroir. */
  .rail .rail-onglet { min-height: 44px; font-size: 14.5px; }
  /* L'écriteau du plan disparaît : 375 px d'écran, et la barre du bas passe
     déjà par-dessus le coin où il vit. */
  .stats-plan { display: none; }
  .rail .option { min-height: 40px; }
  .rail .option input[type='checkbox'] { width: 20px; height: 20px; flex: 0 0 20px; }
  /* Les options numériques d'une vue (taille des cartes, écartement…). */
  .rail .option input[type='number'] { min-height: 38px; font-size: 17px; }
  /* Les libellés d'aide occupaient un tiers du tiroir. On les garde — ils
     enseignent l'appui long — mais lisibles plutôt que minuscules. */
  .rail .rail-aide { font-size: 13px; }

  /* Le volet de droite, aux mêmes mesures. `font-size: 17px` sur la recherche
     n'est pas une coquetterie : en dessous, iOS zoome sur le champ au premier
     appui et laisse la page décadrée. */
  .panneau #recherche { min-height: 42px; font-size: 17px; }
  .panneau #selecteur-groupe { min-height: 42px; }
  #btn-fermer-panneau { min-width: 40px; min-height: 40px; font-size: 17px; }

  /* La fiche, éditable au doigt.
   *
   * Mesuré le 13/08/2026, fiche ouverte sur 375 px : les champs de saisie
   * faisaient **21 à 29 px**, les puces de rang 26, le ✕ de la fiche 30. Le
   * `font-size: 17px` sur tout ce qui se saisit est là pour iOS, qui zoome sur
   * un champ plus petit et laisse la page décadrée — impossible à récupérer
   * d'une main. */
  .panneau .champ-edit input,
  .panneau .champ-edit select,
  .panneau .champ-edit textarea,
  .panneau .champ-suggere input,
  .panneau .joueur-commentaire input { min-height: 40px; font-size: 17px; }
  .panneau .puce-rang { min-height: 36px; }
  .panneau .pn-fermer { min-width: 40px; min-height: 40px; }
  /* Les sept points d'humeur tiennent sur une ligne : 28 px chacun, c'est le
     plus grand qui les garde côte à côte sur un écran de 375. */
  .panneau .humeur-point { width: 28px; height: 28px; }
  .panneau .humeur-points { gap: 8px; }
  /* La liste des personnes : une ligne se touche sans viser. */
  .panneau .item-personne { min-height: 48px; }

  /* Tiroirs : hors écran par défaut, amenés par-dessus la scène au clic. */
  .rail, .panneau {
    position: absolute; top: 0; bottom: 0; z-index: 30;
    width: 100%; flex-basis: 100%; max-width: none;
    margin-left: 0; box-shadow: var(--ombre-panneau);
    transition: transform .22s ease;
  }
  .rail { left: 0; transform: translateX(-100%); }
  .panneau { right: 0; transform: translateX(100%); }
  .rail.ouvert, .panneau.ouvert { transform: translateX(0); }
  .rail.replie { margin-left: 0; }
  /* La barre du bas flotte maintenant par-dessus : sans cette réserve, le
     dernier bloc du tiroir se termine dessous et ne se lit jamais en entier. */
  .rail, .panneau { padding-bottom: 72px; }

  /* Tiroirs fermés : hors de l'écran, mais ils comptaient encore dans la
     largeur de défilement — on pouvait faire glisser la page vers le vide. */
  .corps { overflow: hidden; }

  /* La barre basse sert au doigt, mais elle ne doit pas manger le tiers de
     l'écran : les commandes de cadrage passent en icônes, seul « ＋ Profil »
     — le seul bouton qui écrit — garde son libellé.
   *
   * **`z-index: 40`, au-dessus des tiroirs (13.A).** Mesuré le 14/08/2026,
   * tiroir ouvert : `elementFromPoint` sur « ⛨ Filtres » ne rendait plus le
   * bouton mais le tiroir posé par-dessus. Tant que le tiroir avait sa barre et
   * sa croix, cela ne se voyait pas ; sans elles, la barre du bas est l'une des
   * deux sorties, et une sortie recouverte n'est pas une sortie. */
  .barre-basse {
    flex-wrap: nowrap; gap: 2px; padding: 5px 7px;
    max-width: calc(100vw - 20px); z-index: 40;
  }
  /* 40 px de haut, comme le reste : c'est ici que vit « ＋ Profil », le seul
     bouton de cette barre qui écrit. Le curseur de zoom passe de 16 à 34 px —
     en dessous, on attrape sa poignée une fois sur deux. */
  .barre-basse .bouton { padding: 6px 7px; min-height: 40px; }
  /* « Carnet » perd son libellé ici, et pas ailleurs.
   *
   * Mesuré le 15/08/2026 sur 375 px : la barre tenait à 354 px pour 354
   * disponibles — au caractère près. Le bouton du carnet, libellé compris,
   * la faisait déborder de 48 px, et ce qui débordait était le curseur de
   * zoom, hors de l'écran et donc perdu. En pastille, ✎ tient dans ce qui
   * reste ; « ＋ Profil » et « ⛨ Filtres » gardent les leurs, ce sont les deux
   * seuls dont le geste n'est pas devinable à l'icône. */
  .barre-basse #btn-ajuster .lib,
  .barre-basse #btn-carnet .lib,
  .barre-basse #btn-focus .lib { display: none; }
  .barre-basse .bouton .ico { margin-right: 0; }
  #btn-nouveau-profil { flex: 0 0 auto; }
  /* 60 px de large et non 68 : les huit derniers pixels de débordement, une
     fois « ✎ » réduit à sa pastille. C'est la **largeur** qui cède, jamais la
     hauteur — 34 px reste ce en dessous de quoi on rate la poignée. */
  #zoom-curseur { width: 60px; height: 34px; }
  .separateur-vertical { margin: 0 1px; }

  /* Le carnet, au doigt.
   *
   * En volet il prend tout l'écran : à 375 px il n'y a pas de « à côté du
   * plan », et un carnet de 380 px sur un écran de 375 est un carnet qui
   * déborde. Le ✕ de son en-tête est donc la sortie, et il reste visible.
   *
   * Le sommaire repasse en calque **même en pleine page** : une colonne de
   * 250 px sur 375, c'est les deux tiers de l'écran pour une liste. */
  .volet-carnet {
    position: absolute; left: 0; right: 0; top: 0; bottom: 0;
    width: 100%; flex-basis: 100%; z-index: 32;
    border-left: 0; padding-bottom: 0;
  }
  .carnet-large .cn-sommaire { display: none; }
  .carnet-large.cn-sommaire-ouvert .cn-sommaire {
    display: flex; position: absolute; width: 84%; box-shadow: var(--ombre-panneau);
  }
  .carnet-large .cn-btn-sommaire { display: inline-flex; }

  .cn-barre .bouton { min-height: 38px; }
  .cn-outil { min-width: 34px; height: 34px; font-size: 14px; }
  .cn-ligne { min-height: 42px; }
  .cn-chap-titre, .cn-titre { min-height: 38px; }
  /* 16 px : en dessous, iOS zoome sur le champ dès qu'on y pose le doigt, et
     la note se retrouve à moitié hors de l'écran. */
  .cn-source { font-size: 17px; }
  .cn-suggestions { min-width: 60vw; max-width: 88vw; }
  .cn-suggestions li { min-height: 40px; align-items: center; }
  .cn-chapitre-choix { min-height: 36px; }
}

/* ==========================================================================
   Le plan collectif (lot 17)

   La page `/collectif.html` réemploie tout ce qui précède — `.plan`, `.carte`,
   `.rail`, `.legende`, `.menu-contextuel`. Ne sont ajoutés ici que les objets
   qui n'existaient pas : le rail des membres, le panneau « qui l'a », et le
   panneau de geste.

   **Aucune ombre floue de plus sur les cartes.** Le lot 16.I a ramené le plan
   de 665 couches floues à 309 pour qu'il cesse de ramer ; marquer les
   divergences par un halo aurait rendu tout ce travail. Un trait, donc, et une
   pastille — les deux se peignent sans flou.
   ========================================================================== */

.liste-membres { list-style: none; margin: 0; padding: 0; }
.membre {
  display: flex; align-items: center; gap: 7px;
  padding: 4px 6px; border-radius: 6px; font-size: 13px;
}
.membre:hover { background: var(--fond-3); }
.membre input { margin: 0; flex: 0 0 auto; }
.membre-pastille { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; }
.membre-qui { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.membre-nom {
  min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--texte); font: inherit;
}
.membre-nom:hover { color: var(--accent); }
/* L'arbre regardé, sous le nom. Toujours dit, toujours changeable : sans lui, un
   plan pourrait montrer une autre campagne que celle qu'on croit lire — et un
   geste irait y écrire. */
.membre-arbre {
  min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--texte-faible); font: inherit; font-size: 11.5px;
  border-bottom: 1px dotted var(--bord-clair); align-self: flex-start; max-width: 100%;
}
.membre-arbre:hover { color: var(--accent); }
/* Un membre dont aucun arbre n'a été travaillé : il reste au rail, sinon il
   disparaîtrait de la table sans explication — et c'est de sa ligne qu'on lui
   choisit un arbre à la main. */
.membre-absent { opacity: .55; }
.membre-absent .membre-arbre { font-style: italic; }
.membre-compte {
  color: var(--texte-faible); font-size: 12px; font-variant-numeric: tabular-nums;
}

/* Ce que la superposition révèle, dit sur la carte elle-même. */
.carte.partielle { border-style: dashed; }
.carte.desaccord .carte-nom::after {
  content: ' ⚖'; color: var(--texte-faible); font-size: 11.5px;
}

/* Le panneau de droite : qui a quoi. */
.pn-titre { font-size: 14.5px; font-weight: 600; }
.pn-aide { margin: 0 0 9px; font-size: 13px; color: var(--texte-faible); line-height: 1.5; }
.pn-sous-titre {
  margin: 14px 0 6px; font-size: 11.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--texte-faible);
}
.pn-porteurs { list-style: none; margin: 0 0 10px; padding: 0; }
.pn-porteurs li {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  padding: 3px 0; font-size: 12.5px; border-bottom: 1px solid var(--bord);
}
.pn-porteur-qui { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pn-porteurs code { font-size: 11.5px; color: var(--texte-faible); }
.pn-autre { font-size: 11.5px; color: var(--accent); }

.pn-candidat {
  border: 1px solid var(--bord); border-radius: 8px;
  padding: 8px 9px; margin-bottom: 8px; background: var(--fond-3);
}
.pn-candidat-noms {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 13px; margin-bottom: 3px;
}
.pn-score {
  font-size: 11.5px; padding: 1px 5px; border-radius: 999px;
  background: var(--fond-2); border: 1px solid var(--bord);
  font-variant-numeric: tabular-nums;
}
.pn-candidat-qui { font-size: 11.5px; color: var(--texte-faible); }
.pn-candidat-verdict { font-size: 12px; color: var(--accent); margin-top: 3px; }
.pn-candidat-actions { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }

/* Le panneau de geste : un lot, posé depuis le plan. */
.flottant.flottant-geste { width: 372px; }
.gc-portee { display: block; font-size: 12px; color: var(--texte-faible); margin-top: 2px; }
.gc-bascule { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.gc-rapport { border-top: 1px solid var(--bord); padding-top: 9px; }
.gc-lignes { list-style: none; margin: 0; padding: 0; max-height: 30vh; overflow: auto; }
.gc-lignes li {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  padding: 3px 0; font-size: 12.5px; border-bottom: 1px solid var(--bord);
}
.gc-qui { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.gc-etat { font-size: 11.5px; padding: 1px 5px; border-radius: 999px; border: 1px solid var(--bord); }
.gc-quoi { flex-basis: 100%; font-size: 11.5px; color: var(--texte-faible); }
.gc-cree .gc-etat { color: #2f7d52; border-color: #a8d3ba; }
.gc-mis_a_jour .gc-etat { color: #2f6f9e; border-color: #a9c9e0; }
.gc-inchange .gc-etat { color: var(--texte-faible); }
.gc-refuse .gc-etat { color: #b03a2e; border-color: #e0b0a9; }
body.sombre .gc-cree .gc-etat { color: #7fd6b2; border-color: #2b5a49; }
body.sombre .gc-mis_a_jour .gc-etat { color: #8fc0e8; border-color: #2c4356; }
body.sombre .gc-refuse .gc-etat { color: #e0a196; border-color: #5b3636; }

/* Le curseur de seuil : une ligne, pas une grille de formulaire. */
#bloc-rapprochement .option { gap: 9px; }
#bloc-rapprochement input[type="range"] { flex: 1; min-width: 0; }
#seuil-valeur { font-variant-numeric: tabular-nums; font-size: 12.5px; }

/* Les deux tiroirs du plan collectif.
 *
 * L'application les ouvre depuis sa barre basse et son `telephone.js`, qui
 * déplacent les commandes dans le rail. Cette page-là n'a ni l'un ni l'autre :
 * ses deux boutons restent donc dans la barre du haut, à toutes les largeurs,
 * et c'est ce que ces trois règles disent. Sans elles, sous 760 px, le rail
 * était traduit hors de l'écran **sans plus rien pour l'y ramener** — le même
 * défaut que le lot 11.C avait réparé dans l'application. */
/* Les menus du plan collectif portent des détails plus longs qu'ailleurs — le
   nom d'un arbre suivi de son compte de fiches et de son état. Dans les 280 px
   du menu ordinaire, « Westeros » se voyait rogné en « Wes… » au profit de
   « ouvert par son propriétaire ». On élargit ici, et ici seulement. */
body.collectif .menu-contextuel { min-width: 264px; max-width: 340px; }

body.collectif #btn-panneau { display: none; }
@media (max-width: 860px) {
  body.collectif #btn-panneau { display: inline-flex; }
}
@media (max-width: 760px) {
  body.collectif #btn-rail { display: inline-flex; }
}

/* Les blocs du plan collectif portent des identifiants à eux —
   `#bloc-types-collectif`, `#bloc-maisons-collectif`, `#releve-plan` — et non
   ceux du rail de l'application. Ce n'est pas une coquetterie : sous 760 px,
   `#bloc-maisons { order: 1 }` et `#bloc-liens { order: 2 }` remontent ces deux
   blocs en tête du tiroir. C'est le bon ordre pour l'arbre d'une personne, et
   le mauvais pour un plan collectif, où l'on cherche d'abord ses membres — vu
   à l'essai, la légende des maisons passait devant la liste des joueurs. */

/* =====================================================================
   Feuille de personnage (lot 24)

   Même charpente que la vue « Maisons » — des onglets, deux colonnes qui
   défilent séparément — parce que c'est le même geste : on choisit une fiche
   et on la lit en entier. Les classes portent leur propre préfixe (`fp-`)
   plutôt que de réemployer `mz-` : les deux vues divergeront, et partager des
   classes rendrait chaque retouche risquée des deux côtés.
   ===================================================================== */
.vue-perso {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: var(--fond-2);
}
.fp-onglets {
  display: flex; flex-direction: column; gap: 6px; padding: 8px 12px 0;
  border-bottom: 1px solid var(--bord); flex: 0 0 auto;
}
.fp-onglets-liste {
  display: flex; gap: 2px; flex-wrap: wrap;
  max-height: 26vh; overflow-y: auto;
}
.fp-recherche {
  align-self: flex-start; width: min(320px, 100%);
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 7px;
  padding: 5px 9px; font-size: 13px; outline: none;
  font-family: inherit; color: inherit;
}
.fp-recherche:focus { border-color: var(--accent); }
.fp-onglet {
  display: flex; align-items: center; gap: 7px;
  background: none; border: 1px solid transparent; border-bottom: none;
  border-radius: 8px 8px 0 0; padding: 7px 13px; cursor: pointer;
  color: var(--texte-doux); font-size: 13.5px; font-family: inherit;
}
.fp-onglet:hover { color: var(--texte); background: var(--fond-3); }
.fp-onglet.actif {
  color: var(--texte); background: var(--fond); border-color: var(--bord);
  border-bottom: 1px solid var(--fond); margin-bottom: -1px; font-weight: 600;
}
.fp-onglet-pastille {
  width: 9px; height: 9px; border-radius: 50%; background: var(--couleur-fiche);
}
.fp-onglet-joueur {
  font-size: 11.5px; color: var(--texte-faible); background: var(--fond-3);
  border-radius: 9px; padding: 0 6px;
}
.fp-onglet .nombre {
  font-size: 11.5px; color: var(--texte-faible); background: var(--fond-3);
  border-radius: 9px; padding: 0 6px;
}

.fp-colonnes { flex: 1; display: flex; min-height: 0; }
.fp-colonne { flex: 1 1 50%; min-width: 0; overflow-y: auto; padding: 16px 18px 40px; }
.fp-competences { border-right: 1px solid var(--bord); }
.fp-fiche { background: var(--fond); }

.fp-bloc { padding: 14px 0; border-bottom: 1px solid var(--bord); }
.fp-bloc:last-child { border-bottom: 0; }
.fp-bloc h3 {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin: 0 0 10px;
}
.fp-entete-bloc { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.fp-entete-bloc h3 { margin: 0; }
.fp-entete-bloc .bouton { margin-left: auto; }
.fp-aide { font-size: 12.5px; color: var(--texte-faible); line-height: 1.5; margin: 6px 0 0; }
.fp-barre-etat { display: flex; justify-content: flex-end; min-height: 16px; }
.fp-etat { font-size: 12px; color: var(--texte-faible); }
.fp-etat.erreur { color: #c0392b; }
.fp-alerte {
  margin: 8px 0 0; padding: 6px 9px; border-radius: 7px; font-size: 13px;
  background: color-mix(in srgb, #c0392b 12%, transparent);
  color: #c0392b; border: 1px solid color-mix(in srgb, #c0392b 34%, transparent);
}

/* L'en-tête : le portrait, le nom, la maison, et le joueur derrière la fiche. */
.fp-entete-fiche { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; }
.fp-portrait {
  width: 58px; height: 58px; border-radius: 10px; object-fit: cover;
  border: 1px solid var(--bord); flex: 0 0 58px;
}
.fp-portrait-vide { background: var(--fond-3); }
.fp-titre-fiche h2 { font-size: 17px; margin: 0; }
.fp-sous-titre { margin: 2px 0 0; font-size: 13px; color: var(--texte-doux); }
.fp-joueur { margin: 2px 0 0; font-size: 12.5px; color: var(--texte-faible); }

/* Les champs : une grille qui se replie d'elle-même sur une colonne étroite. */
.fp-grille {
  display: grid; gap: 8px 12px;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
}
.fp-champ { display: flex; flex-direction: column; gap: 3px; font-size: 12.5px; }
.fp-champ.pleine { grid-column: 1 / -1; margin-top: 8px; }
.fp-champ > span { color: var(--texte-faible); }
.fp-champ input, .fp-champ textarea, .fp-champ select,
.fp-ligne-haut input, .fp-effets input, .fp-specialite input {
  width: 100%; background: var(--fond-3); border: 1px solid var(--bord);
  border-radius: 7px; padding: 5px 8px; font-size: 13.5px; outline: none;
  font-family: inherit; color: inherit;
}
.fp-champ textarea { line-height: 1.5; resize: vertical; }
.fp-champ input:focus, .fp-champ textarea:focus, .fp-champ select:focus,
.fp-ligne-haut input:focus, .fp-effets input:focus, .fp-specialite input:focus {
  border-color: var(--accent);
}
.fp-lecture { font-size: 13.5px; padding: 5px 0; color: var(--texte-doux); }

/* Les dix-neuf compétences (lot 25) : un tableau, comme le classeur. Le code
   et le nom, le rang, puis autant de triplets « Spécialité / Rang / Exp »
   qu'on en ouvre. Trois triplets ne tiennent pas dans une demi-largeur
   d'écran : la table défile chez elle, jamais la page. */
.fp-tableau { overflow-x: auto; }
.fp-table-competences { border-collapse: collapse; font-size: 13px; }
.fp-table-competences th {
  text-align: left; font-weight: 600; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--texte-faible);
  padding: 4px 6px; border-bottom: 1px solid var(--bord);
  white-space: nowrap; vertical-align: bottom;
}
.fp-th-groupe {
  text-align: center; border-left: 1px solid var(--bord); color: var(--texte-doux);
}
.fp-th-spe { border-left: 1px solid var(--bord); min-width: 150px; }
.fp-table-competences td {
  padding: 3px 6px; border-bottom: 1px solid var(--bord-clair);
  vertical-align: top;
}
.fp-table-competences tbody tr:hover { background: var(--fond-2); }
.fp-td-nom { white-space: nowrap; padding-top: 9px; }
.fp-td-spe { border-left: 1px solid var(--bord-clair); }
.fp-code {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  color: var(--texte-faible); background: var(--fond-3);
  border-radius: 5px; padding: 1px 5px; margin-right: 7px;
  font-variant-numeric: tabular-nums;
}
.fp-nom-competence { font-size: 13.5px; }
.fp-rang, .fp-spe-rang, .fp-spe-exp {
  width: 54px; text-align: center; font-variant-numeric: tabular-nums;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 6px;
  padding: 5px 4px; font-size: 13px; outline: none; color: inherit;
  font-family: inherit;
}
.fp-rang { font-weight: 600; }
.fp-rang:focus, .fp-spe-rang:focus, .fp-spe-exp:focus { border-color: var(--accent); }
/* La colonne « Spécialité » est une zone de texte, et c'est le point : on y
   décrit ce que la spécialité permet de faire, pas seulement son nom. */
.fp-spe-nom {
  width: 100%; min-width: 150px; resize: vertical; line-height: 1.45;
  background: var(--fond-3); border: 1px solid var(--bord); border-radius: 6px;
  padding: 5px 7px; font-size: 13px; outline: none; color: inherit;
  font-family: inherit;
}
.fp-spe-nom:focus { border-color: var(--accent); }

/* Les valeurs dérivées : quatre grands chiffres, parce qu'on les lit de loin
   en pleine partie et qu'on ne les saisit jamais. */
.fp-derives, .fp-intrigue-tableau {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.fp-case {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--fond-3); border: 1px solid var(--bord);
  border-radius: 9px; padding: 9px 6px;
}
.fp-case-valeur {
  font-size: 21px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.fp-case-nom {
  font-size: 11.5px; color: var(--texte-faible); text-align: center; line-height: 1.35;
}

/* Armes et équipement : une ligne de tête, puis trois effets sur la suivante. */
.fp-ligne-objet { border-bottom: 1px solid var(--bord-clair); padding: 8px 0; }
.fp-ligne-objet:last-child { border-bottom: 0; }
.fp-ligne-haut { display: flex; align-items: center; gap: 6px; padding: 4px 0; }
.fp-large { flex: 2 1 120px; }
.fp-court { flex: 0 0 84px; }
.fp-effets { display: flex; gap: 6px; margin-top: 5px; padding-left: 4px; }

/* La popup d'intrigue : elle porte sa propre largeur, et elle défile. */
.flottant-intrigue { width: min(560px, calc(100vw - 24px)); }
.fp-intrigue { max-height: min(66vh, 620px); overflow-y: auto; }
.fp-intrigue h4 {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--texte-faible); margin: 16px 0 8px;
}
.fp-intrigue-choix {
  display: grid; gap: 8px 12px; margin-bottom: 12px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.fp-intrigue-effet {
  margin-top: 10px; padding: 9px 11px; border-radius: 8px;
  background: var(--fond-3); border: 1px solid var(--bord); font-size: 13px;
}
.fp-intrigue-effet p { margin: 0 0 4px; }
.fp-intrigue-effet p:last-child { margin-bottom: 0; }
.fl-sous { font-size: 12px; color: var(--texte-faible); margin-left: 8px; }

/* Les rounds : une table dense, tabulaire, qu'on remplit de gauche à droite. */
.fp-rounds { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.fp-rounds th {
  text-align: left; font-weight: 500; color: var(--texte-faible);
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  padding: 0 4px 5px; border-bottom: 1px solid var(--bord);
}
.fp-rounds td { padding: 3px 4px; border-bottom: 1px solid var(--bord-clair); }
.fp-rounds td:first-child {
  color: var(--texte-faible); font-variant-numeric: tabular-nums; width: 22px;
}
.fp-rounds input[type="number"] {
  width: 100%; min-width: 46px; background: var(--fond-3);
  border: 1px solid var(--bord); border-radius: 6px; padding: 3px 5px;
  font-size: 12.5px; outline: none; color: inherit; font-family: inherit;
  text-align: center; font-variant-numeric: tabular-nums;
}
.fp-rounds select {
  width: 100%; min-width: 96px; background: var(--fond-3);
  border: 1px solid var(--bord); border-radius: 6px; padding: 3px 5px;
  font-size: 12.5px; outline: none; color: inherit; font-family: inherit;
}
.fp-total {
  font-variant-numeric: tabular-nums; font-weight: 600; text-align: center;
}

/* Sous 900 px les deux colonnes se rangent l'une sous l'autre : à cette
   largeur, une feuille en deux colonnes ne montre plus que des demi-mots. */
@media (max-width: 900px) {
  .fp-colonnes { flex-direction: column; overflow-y: auto; }
  .fp-competences { border-right: 0; border-bottom: 1px solid var(--bord); }
  .fp-colonne { flex: 0 0 auto; overflow-y: visible; }
}
