/* =============================================================================
   LA COQUILLE — colonne de navigation à gauche, colonne des serveurs à droite.
   =============================================================================

   Ce fichier REMPLACE `mise-en-page.css` et les blocs `<style>` des pages. Il ne
   définit aucune couleur, aucune taille, aucun espacement : tout vient des
   jetons de `yashimabot.css`, chargé avant lui.

   ⚠ Trois couches de style se superposaient — le `<style>` de la page,
   `mise-en-page.css`, puis `yashimabot.css` — et la dernière chargée n'était pas
   celle qui devait gagner. D'où les doublons mesurés : `.card`, `.btn`, `table`,
   `.badge`, `.toast`, `.global-stat` définis deux à trois fois, avec des valeurs
   déjà divergentes (rayon 4 px contre 3 px, police 14 px contre 15 px). Il n'en
   reste que deux couches, et la seconde ne fait que composer la première.

   ⚠ L'EN-TÊTE DEVIENT UNE COLONNE. La rangée souple débordait de 161 px sur
   `admin.html` et de 213 px sur la page d'un serveur : titre, boutons et nom
   d'utilisateur poussés hors de l'écran, sans rien pour signaler qu'il manquait
   quelque chose à droite. Une colonne ne déborde pas — elle se replie.

   ⚠ LES CARTES RESTENT là où elles séparent des blocs de nature différente : la
   page d'un serveur, le suivi d'une partie, le réglage d'un fil. Elles quittent
   l'accueil, où elles emboîtaient une tuile dans une carte dans une page —
   trois cadres pour une liste de deux éléments.
   ============================================================================= */

/* --- L'état des deux colonnes ------------------------------------------------

   ⚠ Les largeurs vivent dans des variables sur `:root`. `js/colonne.js` écrit
   `data-colonne` et `data-serveurs` sur `<html>` ; sans lui, les valeurs par
   défaut restent celles des requêtes média — la page est donc juste AVANT que le
   script ne s'exécute, et ne clignote pas.

   Par défaut : les deux ouvertes au bureau, les deux repliées sous 900 px. */
:root {
    --colonne: 230px;

    /* ⚠ NULLE, et non 60 px. Replier la colonne de gauche la fait DISPARAÎTRE ;
       seule la bascule reste, flottante. Le rail de 60 px gardait trois pastilles
       — « Mes parties », « Mes serveurs », « Administration » — qui sont toutes
       des BRANCHES, et dont les enfants étaient cachés par le repli : trois
       boutons qui n'ouvraient rien. Replié, le menu ne cachait pas ses icônes, il
       cachait toutes ses destinations. */
    --rail: 0px;

    /* Le ruban réservé au bouton flottant. Sans lui, le coin haut-gauche de la
       page passe SOUS la bascule : un titre illisible, et rien pour dire pourquoi. */
    --sillage: 48px;

    /* ⚠ NULLE aussi, et pour une raison voisine. Un rail de 64 px gardait ici une
       colonne de pastilles ; depuis le groupement par lettre, la plupart vivent
       dans un groupe replié, et le rail n'en montrait plus qu'une poignée sur le
       parc entier. Les deux colonnes se replient donc de la même façon : elles
       s'effacent, et leur bouton reste. */
    --serveurs: 260px;
    --rail-serveurs: 0px;

    --largeur-colonne: var(--colonne);
    --largeur-serveurs: var(--serveurs);
}

[data-colonne="rail"] { --largeur-colonne: var(--rail); }
[data-colonne="ouverte"] { --largeur-colonne: var(--colonne); }
[data-serveurs="rail"] { --largeur-serveurs: var(--rail-serveurs); }
[data-serveurs="ouverte"] { --largeur-serveurs: var(--serveurs); }

@media (max-width: 900px) {
    :root { --largeur-colonne: var(--rail); --largeur-serveurs: var(--rail-serveurs); }
    [data-colonne="ouverte"] { --largeur-colonne: var(--colonne); }
    [data-serveurs="ouverte"] { --largeur-serveurs: var(--serveurs); }
}

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

/* ⚠ SANS CETTE RÈGLE, L'ATTRIBUT `hidden` NE CACHE PLUS RIEN.

   `hidden` vaut `display: none` dans la feuille du NAVIGATEUR. Or toute règle
   d'auteur qui pose un `display` la bat — et cette feuille en pose trois sur des
   éléments que les scripts cachent :

       .btn                          display: inline-flex
       .lien-colonne                 display: flex
       .form-group                   display: flex

   Constaté en production : sur la page DeathLinkipelago, « Faire jouer ce slot »
   ET « Arrêter » s'affichaient EN MÊME TEMPS, ce que `deathlink.js` rend pourtant
   impossible — il cache l'un quand il montre l'autre. Le « Bouton rigolo » restait
   lui aussi visible alors qu'il était `hidden`, et cliquer dessus ne faisait rien :
   le serveur refusait une action que la page n'aurait jamais dû proposer.

   ⚠ Le défaut est arrivé AVEC la coquille : l'ancien `.btn` de `mise-en-page.css`
   ne posait aucun `display`, donc `hidden` fonctionnait.

   ⚠ `!important` est indispensable, pas décoratif : `.colonne-liens .lien-entete`
   compte deux classes, donc l'emporte sur `[hidden]` quelle que soit la place de
   cette règle dans le fichier. Sans lui, un lien de colonne caché resterait
   visible — et c'est le cas de `#lien-deathlink`.

   ⚠ Neuf éléments du dépôt en dépendent, dont trois bandeaux d'avertissement :
   un `hidden` qui ne cache pas fait afficher « la liste est vide, aucune partie ne
   peut se connecter » en permanence. */
[hidden] { display: none !important; }

/* ⚠ La coquille est une CLASSE, pas `body` : `login.html` et `partage.html`
   n'ont pas de colonne — l'une est la page d'avant la connexion, l'autre une
   page publique en lecture seule. Poser la disposition sur `body` aurait
   transformé leurs deux mises en page en rangée de colonnes vides. */
body { font-family: var(--texte); font-size: var(--t-m); color: var(--parchemin); background: var(--abysse); }

body.coquille {
    display: flex;
    align-items: stretch;
    min-height: 100vh;
}

/* --- LES DEUX COLONNES ------------------------------------------------------ */

.colonne, .colonne-serveurs {
    position: sticky;
    top: 0;
    align-self: flex-start;
    flex: none;
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    gap: var(--e-5);
    padding: var(--e-4) 0;
    background: color-mix(in srgb, var(--abysse) 70%, #000);
    /* Le contenu ne doit jamais élargir la colonne : c'est lui qui se replie. */
    overflow-x: hidden;
    overflow-y: auto;
    transition: width .15s ease;
}

.colonne {
    width: var(--largeur-colonne);
    border-right: 1px solid var(--bordure);
}

.colonne-serveurs {
    width: var(--largeur-serveurs);
    gap: var(--e-3);
    border-left: 1px solid var(--bordure);
}

.page {
    flex: 1;
    /* ⚠ Sans cela, un nom de serveur Discord de cent caractères sans espace
       élargit le conteneur au lieu de se couper. */
    min-width: 0;
    display: flex;
    flex-direction: column;
}

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

/* --- La marque et l'œil -----------------------------------------------------

   ⚠ L'œil est DANS la marque, à droite du nom. Il occupait auparavant un bloc à
   lui seul sous elle, `.colonne-veille`, qui est parti : il y prenait toute une
   ligne de la colonne pour une décoration de 56 px.

   ⚠ Un commentaire promettait ici qu'il « reste visible dans les DEUX états »,
   « le seul indicateur permanent de la page ». C'était déjà faux — une règle du
   rail effaçait `.colonne-veille span`, donc l'œil avec — et le repli qui
   supprime la colonne le rend faux pour de bon. La promesse est retirée plutôt
   que laissée à mentir : replié, il ne reste que la bascule. */
.colonne-marque {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    padding: 0 var(--e-3);
}

/* ⚠ La marque est un LIEN vers l'accueil : cliquer le logo y ramène, ce que tout le
   monde essaie avant de chercher une entrée de menu.

   ⚠ Le logo est désormais le SEUL contenu de ce lien — le nom écrit est parti. Il
   porte donc à lui seul la surface cliquable, et c'est pourquoi le lien garde un
   `aria-label` : le SVG est `aria-hidden`, donc sans cet attribut le lien n'aurait
   plus aucun nom accessible.

   ⓘ `flex: 1` et `min-width: 0` sont conservés : ils tenaient le nom long qui
   pouvait élargir la colonne, et ils ne coûtent rien maintenant que le lien ne
   porte qu'un dessin de taille fixe.

   ⚠ La bascule reste EN DEHORS du lien : l'y inclure ferait changer de page à chaque
   repli de la colonne. */
.colonne-accueil {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    flex: 1;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

/* ⚠ La marque fait 56 px dans sa taille d'origine ; elle est réduite ICI, sans
   toucher à sa définition.

   ⚠ Et SANS `margin-left: auto`, contrairement à l'œil qu'elle remplace. Cette
   règle-là ne servait qu'à pousser l'œil à droite DU NOM. Le nom parti, la garder
   collerait le logo au bord opposé de la colonne en laissant un vide devant lui —
   une marque qui flotte loin de son point d'ancrage. Il reste donc là où le nom
   commençait. */
.colonne-marque .yashimabot-iles { width: 40px; height: 40px; }

.colonne-bascule {
    flex: none;
    /* 44 px : une cible tactile, même au bureau où le rail est étroit. */
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    font-size: var(--t-l);
    line-height: 1;
    color: var(--brume);
    background: none;
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    cursor: pointer;
}

.colonne-bascule:hover { color: var(--lueur); border-color: var(--sortilege); }

/* ⓘ `.colonne-nom` vivait ici, avec sa règle de survol juste au-dessus. Le nom écrit
   a été RETIRÉ des onze pages : la colonne ne porte plus que l'œil.

   ⚠ Le lien reste accessible, et ce n'est pas un hasard : l'œil est
   `aria-hidden="true"`, donc le nom accessible du lien vient entièrement de son
   `aria-label` — traduit par `data-t-attr`. Retirer le texte visible SANS cet
   attribut aurait laissé un lien que rien ne nomme, et aucune vérification du
   dépôt ne l'aurait signalé. */

/* ⓘ `.colonne-veille` vivait ici : le bloc qui portait l'œil sous la marque. L'œil
   est monté à côté du nom, le bloc n'avait plus de contenu, il est parti des onze
   pages.

   ⓘ `#etat-veille`, qui l'accompagnait sur `index.html`, a été RETIRÉ : un `<span>`
   vide que rien n'écrivait, sur une seule page, alors que `js/commun/fraicheur.js`
   porte déjà l'état de connexion sur toutes. */

/* --- Les destinations -------------------------------------------------------

   ⚠ Replié ne veut pas dire caché : les destinations restent CLIQUABLES dans le
   rail, réduites à leur glyphe, avec leur libellé complet en `title`. Une
   colonne qui se vide en se repliant obligerait à la rouvrir pour naviguer,
   donc à faire deux gestes là où il y en avait un.

   ⚠ Le menu est propre à chaque page : il porte les destinations de là où l'on
   est, pas un menu global recopié neuf fois. */
.colonne-liens { display: flex; flex-direction: column; }

.lien-colonne,
/* ⚠ `.lien-entete` est la classe posée par `js/index.js` et `js/guilde.js` sur
   les liens construits à partir de la session. Elle est reprise ici plutôt que
   renommée : la renommer aurait demandé de toucher aux deux scripts, et un lien
   oublié serait resté sans style — c'est exactement ce qui est arrivé à
   `.btn-edit-cfg`, employé sans jamais avoir été déclaré. */
.colonne-liens .lien-entete {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    min-height: 44px;
    padding: 0 var(--e-3);
    font-family: var(--titre);
    font-size: var(--t-s);
    letter-spacing: .5px;
    color: var(--brume);
    text-decoration: none;
    border: none;
    border-left: 2px solid transparent;
    white-space: nowrap;
}

.lien-colonne:hover,
.colonne-liens .lien-entete:hover {
    color: var(--parchemin);
    background: var(--abysse-clair);
}

/* La page courante. `aria-current` porte déjà l'information : la classe ne fait
   que la rendre visible. */
.lien-colonne[aria-current="page"] {
    color: var(--parchemin);
    background: var(--abysse-clair);
    border-left-color: var(--lueur);
}

.colonne-glyphe {
    flex: none;
    width: 20px;
    text-align: center;
    font-family: var(--emoji);
    font-size: var(--t-m);
}

/* ⚠ Certaines entrées n'ONT PAS de glyphe, et c'est voulu : leur libellé traduit
   porte déjà son emoji — « 📊 Statistiques globales », « 📜 Journal ». Leur en
   ajouter un affichait l'icône EN DOUBLE, constaté à l'écran le 2026-09-06.

   ⓘ Une règle vivait ici, qui laissait passer le début d'un tel libellé — donc son
   emoji — sur la largeur d'un glyphe, pour qu'il ne devienne pas une ligne vide
   dans le rail. Elle est partie avec le rail : replier ne laisse plus aucune
   entrée à l'écran. Ce commentaire reste parce que la règle de `separerLEmoji`,
   elle, tient toujours. */

/* ⚠ Un libellé plus long que la colonne était COUPÉ NET, sans rien pour le
   signaler : « Boutique DeathLinkipelago » s'affichait « Boutique DeathLinkipela »,
   tranché en plein mot au bord des 230 px. Rien ne distinguait cela d'un nom qui
   se termine ainsi.

   Trois déclarations, et les trois comptent : `min-width: 0` parce qu'un enfant de
   flex refuse par défaut de descendre sous sa largeur de contenu — sans lui,
   `overflow` n'a rien à faire ; `nowrap` parce qu'un lien de menu sur deux lignes
   déformerait le rythme de la colonne ; et l'ellipse, qui DIT qu'il manque quelque
   chose.

   ⚠ Le texte complet reste en infobulle — les liens construits par script portent
   déjà `title`, et ceux écrits dans les pages le reçoivent par `data-t-attr`. Une
   ellipse sans moyen de lire la suite ne ferait que rendre la coupure polie. */
.colonne-texte {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- Les réglages, en bas ---------------------------------------------------

   ⚠ `.colonne-reglages` est le premier point d'accueil de
   `js/selecteur-langue.js` : le sélecteur se pose ici de lui-même, sur les neuf
   pages, sans qu'aucune ne le déclare. */
.colonne-reglages {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: var(--e-2);
    padding: 0 var(--e-3);
}

.colonne-reglages .boite-langue { flex-direction: column; align-items: stretch; gap: var(--e-1); }
.colonne-reglages #choix-langue { width: 100%; min-height: 44px; }
.colonne-reglages #username-display { font-size: var(--t-s); font-style: italic; color: var(--murmure); }

/* --- LA COLONNE DES SERVEURS ------------------------------------------------

   ⚠ Elle est dans la COQUILLE, donc sur toutes les pages : on change de serveur
   sans repasser par l'accueil. C'était l'unique chemin auparavant. */
.serveurs-tete {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    padding: 0 var(--e-3);
}

/* ⚠ Le titre s'ENROULE, il ne reste plus sur une ligne. « 🖥 Vos serveurs Discord »
   avec 1,5 px d'interlettrage ne tient pas dans ce qui reste des 260 px une fois
   ôtés la bascule de 44 px et les marges : il était coupé net par l'`overflow-x:
   hidden` de la colonne, sans point de suspension, et se lisait « Vos serveurs
   Discor ». Rien ne signalait qu'il manquait quelque chose.

   ⚠ `min-width: 0` avec : sans lui, un élément flex refuse de descendre sous la
   largeur de son plus long mot, et l'enroulement ne se déclenche pas. */
.serveurs-titre {
    min-width: 0;
    font-family: var(--titre);
    font-size: var(--t-s);
    line-height: 1.25;
    letter-spacing: 1.5px;
    color: var(--lueur);
    overflow-wrap: anywhere;
}

.serveurs-liste { display: flex; flex-direction: column; }

.serveur-lien {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    min-height: 52px;
    padding: var(--e-2) var(--e-3);
    color: var(--parchemin);
    text-decoration: none;
    border-left: 2px solid transparent;
}

.serveur-lien:hover { background: var(--abysse-clair); }

.serveur-lien[aria-current="page"] {
    background: var(--abysse-clair);
    border-left-color: var(--lueur);
}

.serveur-icone {
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--abysse-haut);
    border: 1px solid var(--bordure);
}

.serveur-initiale {
    display: grid;
    place-items: center;
    font-family: var(--titre);
    font-size: var(--t-s);
    color: var(--lueur);
}

.serveur-textes { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.serveur-nom {
    font-family: var(--titre);
    font-size: var(--t-s);
    letter-spacing: .5px;
    overflow-wrap: anywhere;
}

/* ⚠ La suspension se lit au MOT autant qu'à la teinte, et l'infobulle la porte
   aussi dans le rail : rouge et vert sont indistinguables pour environ 8 % des
   hommes, une bordure seule ne dirait rien à une personne sur douze. */
.serveur-lien.suspendu { border-left-color: var(--alerte); }
.serveur-lien.suspendu .serveur-icone { border-color: var(--alerte); }
.serveur-etat { font-family: var(--titre); font-size: var(--t-s); letter-spacing: .5px; color: var(--alerte); }

.serveurs-vide { padding: 0 var(--e-3); font-size: var(--t-s); font-style: italic; color: var(--murmure); }

/* ⚠ L'intertitre qui sépare « mes serveurs » de « tous les serveurs ». Les deux
   listes ne disent pas la même chose — l'une est ce qu'on administre, l'autre le
   parc entier — et sans ce filet elles se liraient comme une seule, donc comme si
   l'on administrait tout. */
.serveurs-section {
    margin: var(--e-4) 0 var(--e-2);
    padding: var(--e-3) var(--e-3) 0;
    font-family: var(--titre);
    font-size: var(--t-xs);
    letter-spacing: .5px;
    color: var(--murmure);
    border-top: 1px solid var(--bordure);
}

/* --- LA COLONNE DE GAUCHE REPLIÉE : elle disparaît --------------------------

   ⚠ Elle ne se rétrécit pas, elle S'EN VA. Un rail de 60 px vivait ici, avec ses
   pastilles centrées ; il ne servait à rien. Les trois entrées de tête du menu —
   « Mes parties », « Mes serveurs », « Administration » — sont toutes des
   BRANCHES, et le repli cachait leurs enfants : il restait trois boutons qui
   n'ouvraient rien, et deux liens atteignables sur la douzaine que porte l'arbre.
   Une bande qui prend 60 px pour ne mener nulle part est pire qu'une bande
   absente : elle a l'air de servir.

   ⚠ `display: none` sur le contenu, et non une simple largeur nulle : un texte
   de largeur nulle reste lu par les lecteurs d'écran et reste sélectionnable —
   une colonne repliée se copierait avec ses libellés invisibles. La largeur
   nulle est là POUR LA MISE EN PAGE, le `display` pour tout le reste. */
[data-colonne="rail"] .colonne {
    width: 0;
    padding: 0;
    border-right: none;
    background: none;
    /* ⚠ `visible` : le bouton flottant est un descendant, et `overflow-y: auto`
       hérité du bloc commun le rognerait à la largeur nulle de son parent. */
    overflow: visible;
}

/* Tout s'en va, sauf la marque — qui ne garde elle-même que sa bascule.

   ⚠ C'est le LIEN entier qui disparaît, et non son nom puis son œil séparément :
   vidé de ses deux enfants il resterait un `flex: 1` invisible, donc une bande
   cliquable de plusieurs centaines de pixels menant à l'accueil, posée par-dessus
   la page. Un clic à côté du bouton aurait changé de page. */
[data-colonne="rail"] .colonne > *:not(.colonne-marque) { display: none; }
[data-colonne="rail"] .colonne-accueil { display: none; }

/* ⚠ `fixed` et non `absolute` : sous 900 px la colonne est déjà `fixed`, mais au
   bureau elle est dans le flux — un `absolute` s'y ancrerait sur un parent de
   largeur nulle et le bouton suivrait le défilement de la page. */
[data-colonne="rail"] .colonne-marque {
    position: fixed;
    top: var(--e-4);
    left: var(--e-3);
    z-index: 320;
    padding: 0;
}

/* Le ruban qui garde le coin haut-gauche du contenu hors de portée du bouton. */
[data-colonne="rail"] .page { padding-left: var(--sillage); }

/* --- Le groupement par lettre -----------------------------------------------

   ⚠ Il n'apparaît qu'au-delà de dix serveurs — `serveurs.js` pose alors
   `data-groupes` sur la liste. En dessous, une lettre par serveur ou deux
   n'apporterait rien et ajouterait une ligne à replier.

   ⚠ UN SEUL groupe ouvert à la fois, et celui du serveur où l'on est s'ouvre au
   chargement : même règle que le menu de gauche, écrite dans `serveurs.js`. */
.serveurs-lettre {
    display: flex;
    align-items: center;
    width: 100%;
    /* Le plancher tactile du #54, comme les autres commandes de la coquille. */
    min-height: 44px;
    padding: 0 var(--e-3);
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    font-family: var(--chiffre);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 2px;
    color: var(--lueur);
}

.serveurs-lettre:hover { background: var(--abysse-clair); }

/* La flèche dit l'état, et elle tourne — comme celle des branches du menu. */
.serveurs-lettre::after {
    content: '›';
    margin-left: auto;
    font-size: var(--t-m);
    transition: transform .15s;
}

.serveurs-lettre[aria-expanded="true"]::after { transform: rotate(90deg); }

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

.serveurs-enfants .serveur-lien { padding-left: var(--e-5); }

/* --- LA COLONNE DE DROITE REPLIÉE : elle disparaît aussi ---------------------

   ⚠ Même geste que pour celle de gauche, et pour une raison voisine. Un rail de
   64 px gardait ici une colonne de pastilles ; depuis le groupement par lettre,
   la plupart d'entre elles vivent dans un groupe replié, et le rail ne montrait
   plus qu'une poignée de serveurs sur le parc entier. Une bande qui prend 64 px
   pour en montrer trois sur trente est pire qu'une bande absente.

   ⓘ Une règle vivait ici qui forçait l'affichage des groupes fermés dans le rail,
   pour qu'il les montre tous. Elle n'a plus d'objet : il n'y a plus de rail. */
[data-serveurs="rail"] .colonne-serveurs {
    width: 0;
    padding: 0;
    border-left: none;
    background: none;
    /* Le bouton flottant est un descendant : `overflow-y: auto` le rognerait. */
    overflow: visible;
}

[data-serveurs="rail"] .colonne-serveurs > *:not(.serveurs-tete) { display: none; }
[data-serveurs="rail"] .serveurs-titre { display: none; }

[data-serveurs="rail"] .serveurs-tete {
    position: fixed;
    top: var(--e-4);
    right: var(--e-3);
    z-index: 320;
    padding: 0;
}

[data-serveurs="rail"] .page { padding-right: var(--sillage); }

@media (max-width: 900px) {
    /* Sous 900 px, les colonnes FLOTTENT : ouvertes, elles recouvrent le contenu
       au lieu de le pousser. Le pousser sur 390 px ne laisserait que 160 px. */
    .colonne, .colonne-serveurs { position: fixed; top: 0; z-index: 300; }
    .colonne { left: 0; box-shadow: 4px 0 24px rgba(0, 0, 0, .6); }
    .colonne-serveurs { right: 0; box-shadow: -4px 0 24px rgba(0, 0, 0, .6); }

    .page { margin: 0 var(--rail-serveurs) 0 var(--rail); }

    /* ⚠ Sous 900 px, repliée est le DÉFAUT : la règle doit donc valoir avant même
       que `colonne.js` ait écrit l'attribut, sinon la colonne s'affiche ouverte
       puis saute une fois la page déjà lue. D'où `:not([data-colonne="ouverte"])`
       ici, là où le bureau se contente de `[data-colonne="rail"]`. */
    :root:not([data-colonne="ouverte"]) .colonne {
        width: 0;
        padding: 0;
        border-right: none;
        background: none;
        box-shadow: none;
        overflow: visible;
    }

    :root:not([data-colonne="ouverte"]) .colonne > *:not(.colonne-marque) { display: none; }
    :root:not([data-colonne="ouverte"]) .colonne-accueil { display: none; }

    :root:not([data-colonne="ouverte"]) .colonne-marque {
        position: fixed;
        top: var(--e-4);
        left: var(--e-3);
        z-index: 320;
        padding: 0;
    }

    :root:not([data-colonne="ouverte"]) .page { padding-left: var(--sillage); }

    :root:not([data-serveurs="ouverte"]) .colonne-serveurs {
        width: 0;
        padding: 0;
        border-left: none;
        background: none;
        box-shadow: none;
        overflow: visible;
    }

    :root:not([data-serveurs="ouverte"]) .colonne-serveurs > *:not(.serveurs-tete) { display: none; }
    :root:not([data-serveurs="ouverte"]) .serveurs-titre { display: none; }

    :root:not([data-serveurs="ouverte"]) .serveurs-tete {
        position: fixed;
        top: var(--e-4);
        right: var(--e-3);
        z-index: 320;
        padding: 0;
    }

    :root:not([data-serveurs="ouverte"]) .page { padding-right: var(--sillage); }
}

/* --- LE CONTENU ------------------------------------------------------------- */

.entete-page {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--e-2) var(--e-4);
    padding: var(--e-5) var(--e-6) 0;
}

.entete-page h1 {
    font-family: var(--titre);
    font-size: var(--t-xl);
    font-weight: 600;
    letter-spacing: 2px;
    color: var(--lueur);
    overflow-wrap: anywhere;
}

.entete-page .devise { font-size: var(--t-s); font-style: italic; color: var(--murmure); }

.container {
    width: 100%;
    max-width: 1100px;
    padding: var(--e-5) var(--e-6) var(--e-6);
    display: flex;
    flex-direction: column;
    gap: var(--e-6);
}

@media (max-width: 900px) {
    .entete-page { padding: var(--e-4) var(--e-4) 0; }
    .container { padding: var(--e-4); gap: var(--e-5); }
}

/* --- Une section, sans carte ------------------------------------------------

   ⚠ Le titre porte le filet, et c'est tout : sur l'accueil, la carte n'ajoutait
   qu'un cadre entre la page et les tuiles. Le `h2` garde son apparence de
   `yashimabot.css` — police de titre, accent, filet qui court jusqu'au bord. */
.section > h2 {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    font-family: var(--titre);
    font-size: var(--t-m);
    font-weight: 600;
    letter-spacing: 2px;
    color: var(--lueur);
    margin-bottom: var(--e-2);
}

.section > h2::after {
    content: '';
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--bordure), transparent);
}

/* ⚠ La MÊME règle pour les deux conteneurs. `.aide-champ` n'a qu'une marge haute
   dans `yashimabot.css` : dans une carte, sa phrase d'explication collait donc au
   tableau qui la suit. Les pages d'administration compensaient par un
   `style="margin-bottom:…"` en ligne, recopié dans chacune — et une page ajoutée
   sans lui aurait retrouvé le défaut sans que rien ne le signale. */
.section > .aide-champ,
.card > .aide-champ { margin: 0 0 var(--e-4); max-width: 70ch; }

/* ⓘ Les règles de la légende cliquable ne sont PAS ici : elles vivent dans
   `yashimabot.css`, avec le reste du graphique. `partage.html` ne charge que cette
   feuille-là, et le bouton y gardait sinon le style par défaut du navigateur —
   13,33 px et la couleur système, deux échecs au banc. */

/* --- La tête de la carte d'une partie ---------------------------------------

   ⚠ Les informations de la partie et son avancement, CÔTE À CÔTE. L'avancement
   vivait seul entre la grille de compteurs et la courbe : un cadre isolé à
   mi-page, que l'on dépassait en descendant vers les joueurs.

   ⚠ `flex-wrap` et une base de 280 px, et non deux colonnes fixes : sous 900 px
   la page n'a pas 600 px à donner: les deux blocs se remettent alors l'un sous
   l'autre d'eux-mêmes, sans requête média à tenir à jour en double. */
.carte-tete-partie {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: var(--e-5);
}

/* ⚠ `min-width: 0` : sans lui, une adresse de serveur sans espace — c'est le cas,
   `wss://ap.deoxis9001.ovh:61204` — élargit sa colonne au lieu de se couper, et
   pousse l'avancement hors de la carte. */
.carte-tete-partie > * { flex: 1 1 280px; min-width: 0; }

/* ⚠ La section porte ses propres marges et un `max-width` de 1400 px, hérités du
   temps où elle occupait une rangée entière. Dans la carte, ils la décaleraient. */
.carte-tete-partie .avancement { margin: 0; max-width: none; }

/* --- La carte, conservée ----------------------------------------------------

   ⚠ Elle reste pour la page d'un serveur, le suivi d'une partie et le réglage
   d'un fil, où elle sépare des blocs de nature différente. Une seule
   définition, ici, au lieu de trois. */
.card {
    position: relative;
    padding: var(--e-5);
    background: linear-gradient(160deg, var(--abysse-haut), var(--abysse-clair));
    border: 1px solid var(--bordure);
    border-radius: 4px;
    box-shadow: 0 4px 32px rgba(0, 0, 0, .6), inset 0 1px 0 color-mix(in srgb, var(--lueur) 5%, transparent);
}

.card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--bordure), var(--lueur), var(--bordure), transparent);
}

.card > h2 {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    font-family: var(--titre);
    font-size: var(--t-m);
    font-weight: 600;
    letter-spacing: 1px;
    color: var(--lueur);
    margin-bottom: var(--e-4);
}

.card > h2::after {
    content: '';
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--bordure), transparent);
}

.card-corner {
    position: absolute;
    width: 12px;
    height: 12px;
    border-color: var(--sortilege);
    border-style: solid;
}

.card-corner.tl { top: 6px; left: 6px; border-width: 1px 0 0 1px; }
.card-corner.tr { top: 6px; right: 6px; border-width: 1px 1px 0 0; }
.card-corner.bl { bottom: 6px; left: 6px; border-width: 0 0 1px 1px; }
.card-corner.br { bottom: 6px; right: 6px; border-width: 0 1px 1px 0; }

/* --- Ce qui reste commun ----------------------------------------------------
   Repris de `mise-en-page.css`, en jetons, sans doublon. */

table { width: 100%; border-collapse: collapse; font-size: var(--t-s); }

th {
    text-align: left;
    padding: var(--e-2) var(--e-3);
    font-family: var(--titre);
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: 1px;
    color: var(--brume);
    background: color-mix(in srgb, var(--abysse) 60%, transparent);
    border-bottom: 1px solid var(--bordure);
}

td {
    padding: var(--e-3);
    color: var(--parchemin);
    border-bottom: 1px solid color-mix(in srgb, var(--bordure) 45%, transparent);
    vertical-align: middle;
}

tr:last-child td { border-bottom: none; }
tr:hover td { background: color-mix(in srgb, var(--sortilege) 8%, transparent); }

.empty {
    padding: var(--e-5);
    text-align: center;
    font-style: italic;
    font-size: var(--t-s);
    color: var(--murmure);
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--e-2);
    min-height: 44px;
    padding: var(--e-2) var(--e-4);
    font-family: var(--titre);
    font-size: var(--t-s);
    font-weight: 600;
    letter-spacing: .5px;
    color: var(--brume);
    text-decoration: none;
    background: var(--abysse-clair);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    cursor: pointer;
}

.btn:hover { color: var(--lueur); border-color: var(--sortilege); }

.btn-primary { color: var(--lueur); border-color: var(--sortilege); }
.btn-primary:hover { background: color-mix(in srgb, var(--lueur) 12%, var(--abysse-clair)); }
.btn-danger, .btn-leave { color: var(--alerte); border-color: color-mix(in srgb, var(--alerte) 60%, var(--bordure)); }
.btn-danger:hover, .btn-leave:hover { background: color-mix(in srgb, var(--alerte) 12%, var(--abysse-clair)); }
.btn-secondary, .btn-edit-cfg, .btn-unblock, .btn-enable, .btn-disable { /* alias : même bouton */ }

/* ⚠ UN seul vocabulaire de badge. `.on/.off` côté accueil et
   `.enabled/.disabled` côté admin décrivaient le même état sous deux noms, avec
   deux apparences. Les anciens noms restent en alias le temps de la bascule. */
.badge {
    display: inline-block;
    padding: 2px var(--e-2);
    font-family: var(--titre);
    font-size: var(--t-xs);
    letter-spacing: .5px;
    border: 1px solid;
    border-radius: 2px;
}

.badge-actif, .badge.on, .badge.enabled, .badge.guild {
    color: var(--lueur);
    border-color: var(--sortilege);
    background: color-mix(in srgb, var(--lueur) 12%, transparent);
}

.badge-inactif, .badge.off, .badge.disabled {
    color: var(--alerte);
    border-color: color-mix(in srgb, var(--alerte) 55%, var(--bordure));
    background: color-mix(in srgb, var(--alerte) 10%, transparent);
}

.actions { display: flex; flex-wrap: wrap; gap: var(--e-2); }

code {
    font-family: var(--chiffre);
    font-size: var(--t-s);
    color: var(--lueur);
    background: color-mix(in srgb, var(--sortilege) 14%, transparent);
    padding: 1px 5px;
    border-radius: 2px;
}

/* --- LE MENU EN ARBORESCENCE -------------------------------------------------

   ⚠ Le retrait se fait par PADDING, pas par marge ni par imbrication visuelle :
   la zone cliquable garde toute la largeur de la colonne. Un lien de troisième
   niveau qui ne serait cliquable que sur sa moitié droite se raterait sans qu'on
   comprenne pourquoi. */
.menu-n1 { padding-left: var(--e-5); }
.menu-n2 { padding-left: var(--e-6); }
.menu-n3 { padding-left: calc(var(--e-6) + var(--e-4)); }

/* ⚠ Une branche est un `<button>`, donc elle hérite des styles de bouton du
   navigateur. On les défait ici, sinon elle ne ressemblerait pas aux liens
   qu'elle surmonte. */
.menu-tete {
    width: 100%;
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

/* La flèche dit l'état, et elle tourne. Le `::after` évite un élément de plus. */
.menu-tete::after {
    content: '›';
    margin-left: auto;
    font-size: var(--t-m);
    transition: transform .15s;
}

.menu-tete[aria-expanded="true"]::after { transform: rotate(90deg); }

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

/* ⓘ Deux règles vivaient ici, qui repliaient l'arbre dans le rail : `.menu-enfants`
   caché, retraits annulés. Elles sont parties avec le rail, et c'est ce qu'elles
   faisaient qui a motivé sa suppression — cacher les enfants d'un arbre dont TOUTES
   les têtes sont des branches ne laisse que des boutons inertes. La colonne repliée
   n'affiche plus rien : il n'y a plus d'arbre à replier. */

/* ⚠ Le rouge ne porte JAMAIS l'information seul — règle du dépôt depuis le #52.
   L'infobulle et l'`aria-label` disent « suspendue » en toutes lettres. */
.menu-partie-suspendue .colonne-texte { color: var(--alerte); }

.menu-etat, .menu-relance {
    flex: none;
    margin-left: auto;
    font-family: var(--emoji);
    font-size: var(--t-s);
}

.menu-suspendue { color: var(--alerte); }

.menu-relance {
    background: none;
    border: 1px solid color-mix(in srgb, var(--lueur) 50%, var(--bordure));
    border-radius: 2px;
    color: var(--lueur);
    cursor: pointer;
    padding: 0 var(--e-1);
    /* ⚠ Une cible tactile décente, même dans une ligne de menu : le #54 a posé
       44 px comme plancher, et un bouton de 12 px au bout d'un lien est
       exactement ce qu'on rate au doigt. */
    min-height: 24px;
    min-width: 24px;
}

.menu-relance:hover { background: color-mix(in srgb, var(--lueur) 15%, transparent); }
.menu-relance:disabled { opacity: .5; cursor: default; }

/* --- LE JOURNAL DU BOT -------------------------------------------------------

   ⚠ `white-space: pre` et chasse fixe : une ligne de journal est ALIGNÉE —
   horodatage, niveau, source — et la replier en morceaux la rend illisible. Elle
   défile donc horizontalement plutôt que de se couper.

   ⚠ La hauteur est bornée et le bloc défile SEUL. Sans cela, cinq mille lignes
   allongeraient la page jusqu'à emporter le menu et les cartes hors de portée. */
.journal {
    max-height: 60vh;
    overflow: auto;
    margin: 0;
    padding: var(--e-3);
    background: var(--abysse);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    font-family: var(--chiffre);
    font-size: var(--t-s);
    line-height: 1.5;
    color: var(--brume);
    white-space: pre;
    tab-size: 4;
}

/* =============================================================================
   CE QUI VIVAIT DANS LES `<style>` DES PAGES ET DANS `mise-en-page.css`
   =============================================================================

   ⚠ Ces 32 classes n'étaient définies NULLE PART ailleurs. Supprimer les blocs
   `<style>` des pages et `mise-en-page.css` — ce que ce restylage fait — les
   laissait sans une seule règle : l'œil de YashimaBot invisible sur les huit pages,
   la fenêtre modale dépliée en permanence dans le flux, les formulaires en vrac
   et l'écran de suivi en texte brut empilé.

   ⚠ Elles ont failli passer inaperçues parce que `yashimabot.css` les CITE — mais
   dans le bloc de rattrapage mobile (lignes 1282-1298), une liste de sélecteurs
   qui force `font-size: 14px`. Y figurer n'est pas y être défini. Une recherche
   par nom trouvait `.pill`, `.hint-list`, `.server-info` et concluait « c'est
   couvert » ; ça ne l'était pas.

   ⚠ AUCUNE COULEUR EN DUR ici, contrairement aux sources : les originaux
   portaient 23 valeurs littérales — `#4ade80`, `rgba(74,32,112,.2)`,
   `'Cinzel',serif` — qui auraient figé ces blocs à l'ancienne palette violette
   pendant que le reste de la page passait à l'encre et l'or. C'est exactement
   la faute que `partage.html` a payée avec son `--sortilege` recopié à la main.
   ============================================================================= */

/* --- L'ŒIL DE YASHIMABOT ----------------------------------------------------------

   ⚠ La signature du #49. Elle n'existait que dans le `<style>` d'`index.html` et
   de `guilde.html` ; le restylage l'a portée aux onze pages.

   ⓘ Ce commentaire promettait « le SEUL indicateur permanent du rail replié », et
   situait l'œil dans `.colonne-veille`. Les deux sont faux depuis les corrections
   de l'interface : `.colonne-veille` n'existe plus — l'œil est dans
   `.colonne-marque`, contre le nom — et IL N'Y A PLUS DE RAIL, le repli supprime
   la colonne entière. L'état de connexion, lui, est porté par la barre
   `#fraicheur`, qui vit sur toutes les pages.

   L'iris prend la couleur de l'accent — c'est la décision « encre et or » : elle
   dit que l'accent VIENT de l'œil, et non l'inverse. */
/* ⚠ EN SVG, ET NON EN `<span>` IMBRIQUÉS COMME L'ŒIL. Le dessin est géométrique :
   huit disques placés au degré près sur un cercle. En CSS il aurait fallu huit
   éléments positionnés en absolu, chacun avec ses coordonnées écrites à la main —
   et la moindre correction de rayon aurait demandé de recalculer les seize
   valeurs. Le `viewBox` fait ce travail, et le dessin reste net à toute taille.

   ⓘ `login.html` a longtemps porté son propre œil, sous des classes `.eye`/`.iris`
   qui lui étaient propres. Il porte désormais ce même dessin, mais garde SA taille :
   la marque y est seule au milieu de la page, sans colonne ni titre au-dessus. */
.yashimabot-iles {
    display: block;
    flex: none;
    width: 56px;
    height: 56px;
    color: var(--sortilege);
    filter: drop-shadow(0 0 5px color-mix(in srgb, var(--lueur) 30%, transparent));
}

/* ⚠ `currentColor` et non la variable en dur : c'est ce qui rend le survol
   possible d'une seule ligne, plus bas, sans toucher au SVG. */
.yashimabot-iles circle { fill: currentColor; }

/* ⚠ L'île de tête prend l'accent, les sept autres le violet. Sans elle, huit
   disques identiques en anneau se lisent comme un INDICATEUR DE CHARGEMENT —
   c'est la forme exacte d'un spinner. Une île qui tranche donne au dessin un
   haut, donc une identité plutôt qu'une rotation. */
.yashimabot-iles .ile-tete { fill: var(--lueur); }

.colonne-accueil:hover .yashimabot-iles { color: var(--parchemin); }

/* ⓘ Une garde `prefers-reduced-motion` vivait ici, pour l'œil qui battait en
   permanence dans le coin de l'écran. Elle est partie AVEC l'animation, et non
   par oubli : le nouveau logo n'en a aucune.

   ⚠ Ce n'est pas une économie, c'est le dessin qui l'interdit. Huit disques en
   anneau qui pulsent ou tournent, c'est très exactement un indicateur de
   chargement — la page semblerait attendre quelque chose en permanence. L'œil
   pouvait battre parce qu'un œil qui bat est vivant ; un archipel qui clignote
   est en panne. */

/* --- LA FENÊTRE MODALE -------------------------------------------------------

   ⚠ LA règle qui manquait le plus : `display: none`. `yashimabot.css` définit bien
   `.modal`, mais c'est `.modal-overlay` qui porte le masquage — une règle
   conservée dont la jumelle disparaît est pire qu'une règle absente. Sans elle,
   « Invoquer un portail » s'affichait dépliée dans le flux de `guilde.html`,
   sous les portails, et le bouton Annuler ne la refermait pas.

   Vaut aussi pour le `dh-modal` que `demandes-hote.js` construit. */
.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--abysse) 85%, transparent);
    z-index: 100;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(3px);
}

.modal-overlay.open { display: flex; }

.modal-corner {
    position: absolute;
    width: 14px;
    height: 14px;
    border-color: var(--bordure);
    border-style: solid;
}

.modal-corner.tl { top: var(--e-2); left: var(--e-2); border-width: 1px 0 0 1px; }
.modal-corner.tr { top: var(--e-2); right: var(--e-2); border-width: 1px 1px 0 0; }
.modal-corner.bl { bottom: var(--e-2); left: var(--e-2); border-width: 0 0 1px 1px; }
.modal-corner.br { bottom: var(--e-2); right: var(--e-2); border-width: 0 1px 1px 0; }

/* --- LES FORMULAIRES ---------------------------------------------------------

   ⚠ `.form-group` était défini DEUX fois avec deux valeurs — `gap: 5px` dans le
   `<style>` d'`index.html`, `gap: 4px` dans `mise-en-page.css`. La divergence
   exacte que ce restylage existe pour supprimer. `var(--e-1)` (4 px) tranche :
   c'est le jeton, et il vaut la valeur de la feuille partagée. */
.form-group {
    display: flex;
    flex-direction: column;
    gap: var(--e-1);
    min-width: 0;
}

.form-group.full { grid-column: 1 / -1; }

.form-actions {
    grid-column: 1 / -1;
    display: flex;
    gap: var(--e-2);
    justify-content: flex-end;
    margin-top: var(--e-2);
}

/* ⚠ Sous 900 px la grille retombe à une colonne : `1 / -1` y désignerait encore
   toute la largeur, ce qui est juste, mais le #54 avait posé `grid-column: 1`
   explicitement. Conservé — une valeur mesurée dans un navigateur ne se réécrit
   pas de mémoire. */
@media (max-width: 900px) {
    .form-group.full { grid-column: 1; }

    /* ⚠ `!important`, et il est indispensable : les `min-width` fautifs sont des
       styles EN LIGNE (`style="min-width:300px"` sur `#b-guild-group`), qu'aucune
       règle de feuille ne peut battre autrement. Même procédé que le plancher de
       14 px du #54, et pour la même raison.

       ⚠ Ce n'est pas une régression de l'archive mais une CONSÉQUENCE de la
       coquille : les deux rails prennent 124 px des 390, il n'en reste que 266
       pour un champ qui en exige 300. Le banc visuel l'a mesuré — 11 px de
       débordement sur `/admin.html` — là où la lecture du code ne pouvait pas le
       voir : la valeur fautive et la cause vivent dans deux fichiers différents. */
    .form-group { min-width: 0 !important; }
}

.file-input-wrap {
    position: relative;
    width: 100%;
}

/* ⚠ Le champ natif est rendu invisible mais reste CLIQUABLE et atteignable au
   clavier : `opacity: 0`, jamais `display: none` — qui le retirerait de l'ordre
   de tabulation et rendrait le téléversement impossible sans souris. */
.file-input-wrap input[type=file] {
    opacity: 0;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

.file-label {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    padding: var(--e-2) var(--e-3);
    background: var(--abysse-clair);
    border: 1px solid var(--bordure);
    border-radius: 2px;
    color: var(--brume);
    font-size: var(--t-s);
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.file-label:hover { border-color: var(--sortilege); }

/* --- L'AUTOCOMPLÉTION DES BLOCAGES -------------------------------------------

   ⚠ Ces quatre classes ne vivaient QUE dans `mise-en-page.css` — donc rien ne
   les rattrapait, aucune page ne les redéfinissant en ligne. Sans elles la liste
   de suggestions perd son `position: absolute` et son `display: none` : elle
   s'affiche dans le flux, en permanence, en poussant le formulaire de blocage
   vers le bas à chaque frappe. */
.ac-wrap { position: relative; }
.ac-wrap input { width: 100%; }

.ac-dropdown {
    position: absolute;
    top: calc(100% + var(--e-1));
    left: 0;
    right: 0;
    background: var(--abysse-clair);
    border: 1px solid var(--bordure);
    border-radius: 2px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 100;
    display: none;
}

.ac-dropdown.open { display: block; }

.ac-item {
    padding: 7px var(--e-3);
    cursor: pointer;
    font-size: var(--t-s);
    color: var(--parchemin);
    border-bottom: 1px solid color-mix(in srgb, var(--bordure) 50%, transparent);
}

.ac-item:last-child { border-bottom: none; }

/* ⚠ `.active` est posée par `admin.js` au clavier, `:hover` à la souris : les
   DEUX doivent produire le même aspect, sans quoi naviguer aux flèches ne montre
   pas où l'on est. */
.ac-item:hover, .ac-item.active {
    background: color-mix(in srgb, var(--sortilege) 20%, transparent);
    color: var(--lueur);
}

.ac-item .ac-id {
    font-size: var(--t-xs);
    color: var(--murmure);
    font-family: var(--chiffre);
    margin-left: var(--e-2);
}

/* --- LE SUIVI D'UNE PARTIE ---------------------------------------------------

   ⚠ `partie.html` GARDE un `<style>`, ce qui l'a fait passer pour épargnée — il
   tombe pourtant de 34 classes à 6. Les 19 ci-dessous sont posées par
   `js/partie.js` et n'étaient définies que dans le bloc supprimé.

   C'est l'écran que le #51 décrit comme « un deuxième écran posé à côté du
   jeu » : celui qu'on regarde le plus longtemps, et le seul dont tout le
   contenu est construit par script. */
.player-card {
    background: var(--abysse-clair);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    padding: var(--e-4);
}

.player-card .world-bar-wrap { margin: 10px 0 var(--e-1); }
.player-card .compte-lieux { margin-bottom: 10px; }

.player-name {
    font-family: var(--titre);
    font-size: var(--t-m);
    color: var(--lueur);
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--e-2);
}

.player-name .identite {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    min-width: 0;
}

.player-jeu {
    font-size: var(--t-xs);
    color: var(--murmure);
    font-family: var(--titre);
}

.player-stats {
    display: flex;
    gap: var(--e-3);
    margin-bottom: 10px;
    flex-wrap: wrap;
}

/* ⚠ La barre porte l'avancement d'un monde, et le pourcentage est TOUJOURS
   écrit à côté — c'est la réserve que le LISEZ-MOI laisse ouverte sur les
   paliers : aucune information ne repose sur la seule couleur. */
.world-bar-wrap {
    background: var(--abysse);
    border-radius: 2px;
    height: 8px;
    overflow: hidden;
    margin: 6px 0;
}

.world-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--sortilege), var(--lueur));
    border-radius: 2px;
    transition: width .4s ease;
}

/* ⚠ Le dégradé allait de `--purple` à `--magenta`. Sous la nouvelle palette les
   deux pointent sur `--sortilege` : la barre serait devenue unie sans que rien
   ne le signale. Elle va donc du violet à l'or, ce qui garde une progression
   lisible. */
@media (prefers-reduced-motion: reduce) {
    .world-bar-fill { transition: none; }
}

.world-card-pct {
    font-family: var(--titre);
    font-size: var(--t-l);
    color: var(--lueur);
}

/* --- Les pastilles d'un joueur --- */
.pill {
    font-size: var(--t-xs);
    padding: 2px var(--e-2);
    border-radius: 2px;
    border: 1px solid;
    font-family: var(--titre);
}

.pill.death {
    background: color-mix(in srgb, var(--alerte) 10%, transparent);
    color: var(--alerte);
    border-color: color-mix(in srgb, var(--alerte) 55%, var(--bordure));
}

.pill.hint {
    background: color-mix(in srgb, var(--sortilege) 15%, transparent);
    color: var(--lueur);
    border-color: var(--sortilege);
}

/* ⚠ Le vert vient de `--palier-haute`, le jeton des paliers de progression, et
   non d'un `#86efac` recopié : « objets majeurs seulement » est bien un état
   favorable, il doit virer avec les paliers le jour où on les retouche. */
.pill.major {
    background: color-mix(in srgb, var(--palier-haute) 10%, transparent);
    color: var(--palier-haute);
    border-color: color-mix(in srgb, var(--palier-haute) 45%, var(--bordure));
}

/* --- Les indices --- */
.hint-list {
    font-size: var(--t-s);
    color: var(--brume);
    max-height: 120px;
    overflow-y: auto;
    border-top: 1px solid var(--bordure);
    padding-top: var(--e-2);
    margin-top: var(--e-1);
    scrollbar-width: thin;
    scrollbar-color: var(--bordure) transparent;
}

.hint-list::-webkit-scrollbar { width: 3px; }
.hint-list::-webkit-scrollbar-thumb { background: var(--bordure); }

.hint-item {
    padding: 3px 0;
    border-bottom: 1px solid color-mix(in srgb, var(--bordure) 50%, transparent);
}

.hint-item:last-child { border-bottom: none; }

/* --- Le journal de connexions --- */
.conn-entry {
    padding: 5px 0;
    border-bottom: 1px solid color-mix(in srgb, var(--bordure) 50%, transparent);
    display: flex;
    align-items: center;
    gap: var(--e-2);
}

.conn-entry:last-child { border-bottom: none; }

.conn-entry .conn-time {
    margin-left: auto;
    font-size: var(--t-xs);
    color: var(--murmure);
    white-space: nowrap;
}

/* ⚠ La pastille ne porte JAMAIS l'information seule : `partie.js` écrit le nom
   du joueur à côté, et le journal dit « connecté » ou « déconnecté » en toutes
   lettres. Une personne sur douze ne distingue pas ce vert de ce rouge. */
.connected-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    margin-right: var(--e-1);
}

.connected-dot.on {
    background: var(--palier-haute);
    box-shadow: 0 0 4px var(--palier-haute);
}

.connected-dot.off { background: var(--alerte); }

.server-info {
    font-size: var(--t-s);
    color: var(--brume);
    margin-bottom: var(--e-1);
}

.server-info span { color: var(--parchemin); }

/* ⚠ `.value.red` est conservée dans le `<style>` de `partie.html`, mais PAS
   `.value.green` — que `partie.js:172` pose quand tous les objectifs sont
   atteints. L'état « partie terminée » perdait donc sa couleur, et lui seul :
   un manque qui ne se voit que le jour où quelqu'un finit. */
.stat-box .value.green { color: var(--palier-haute); }
