/*  Worms Finder — habillage provisoire.
 *
 *  Approche hybride du cadrage §H : l'interface est construite en CSS, et sera
 *  ensuite habillee avec les VRAIS elements du jeu (polices bigfont/medfont
 *  converties en webfont, 254 drapeaux, 106 pierres tombales, sons de menu).
 *  Ici on ne fait qu'evoquer la palette : ciel de nuit, terre, et le jaune des
 *  caisses de ravitaillement.
 */

:root {
    --ciel-haut:  #0a1830;
    --ciel-bas:   #143a52;
    --jaune:      #ffd23f;      /* les caisses */
    --rouge:      #e04b3a;      /* la dynamite */
    --vert:       #7ec850;
    --texte:      #eaf2f8;
    /*  🚨 REMONTÉ DE #9db4c7. C'est la couleur qui souffrait le plus : étiquettes,
        textes d'aide, méta des lignes — tout ce qui est « secondaire » tombait à
        3:1 dès que le décor passait du ciel de nuit au terrain vert. À #b8cddd on
        tient 4,9:1 sur le voile seul et 8:1 sur une carte, tout en restant
        nettement en retrait de --texte : la hiérarchie est conservée. */
    --texte-doux: #b8cddd;

    /*  🔑 LES CARTES SONT UN APLAT SOMBRE, PLUS UN VOILE BLANC.
     *
     *  Avant : rgba(255,255,255,.06). Sur un ciel de nuit ça marchait — un voile
     *  blanc à 6 % sur du bleu très sombre suffit à détacher un bloc. Sur le
     *  décor de jeu (terrain vert vif, terre orange, vers animés) ça ne masque
     *  rien du tout, et pire, ça ÉCLAIRCIT le fond : le texte se retrouve sur du
     *  vert délavé au lieu de vert saturé. Aucun gain de contraste.
     *
     *  Ici c'est l'inverse : un bleu nuit tiré du ciel du jeu, à 74 %. La carte
     *  ne laisse plus passer que 26 % du décor — assez pour qu'on devine que ça
     *  bouge derrière, pas assez pour gêner la lecture (mesuré 12:1 pour --texte
     *  sur le pire fond du décor, le liseré jaune-olive du terrain).
     *  ⚠️ La teinte est plus CLAIRE que --ciel-haut (#0a1830), volontairement :
     *  ce choix datait de guide.html (supprimée le 29/08), page sans décor où
     *  une carte plus sombre que le fond du body aurait disparu. On la garde :
     *  la teinte a été mesurée sur le décor, pas sur cette page. */
    --carte:      rgba(15, 34, 60, .74);
    --carte-fort: rgba(28, 56, 88, .82);
    /*  Bordure bleutée plutôt que blanche, et presque doublée : c'est elle qui
        dessine le bord du bloc quand le décor derrière est clair. */
    --bord:       rgba(168, 198, 224, .28);

    /*  L'ombre portée sous les blocs. Elle ne sert pas à faire joli : posée sur
        un décor animé, une carte sans ombre a l'air découpée au cutter. */
    --ombre-bloc: 0 6px 22px rgba(2, 8, 18, .45);
    /*  Et l'ombre des textes qui n'ont AUCUNE carte sous eux (titres de section,
        pied de page, onglets) : le voile les porte, l'ombre les décolle. */
    --ombre-texte: 0 1px 3px rgba(3, 9, 20, .95);
}

* { box-sizing: border-box; }

/*  🚨 Sans cette ligne, l'attribut `hidden` est purement decoratif des qu'une
 *  regle d'auteur pose un `display`. La feuille du navigateur applique bien
 *  `[hidden] { display: none }`, mais toute regle d'auteur de meme specificite
 *  la bat — et `.voile { display: flex }` en est une. Resultat mesure : la
 *  modale « Match found » s'affichait en permanence, y compris pour un visiteur
 *  sans session. Le JavaScript avait raison, c'est le CSS qui l'ignorait. */
[hidden] { display: none !important; }

body {
    margin: 0; min-height: 100vh;
    /*  Le fond est desormais le decor (#decor, en position fixe derriere tout).
        Une couleur de repli reste utile si les images du decor manquent. */
    background: var(--ciel-haut);
    color: var(--texte);
    /*  🔑 LA PILE DE POLICES. Tahoma demandé, et Tahoma seulement — pas de
     *  webfont.
     *
     *  🚨 AUCUN @font-face, AUCUN FICHIER, AUCUN CDN. Tahoma appartient à
     *  Microsoft et n'est PAS redistribuable : l'embarquer ou la servir depuis
     *  un CDN serait une infraction de licence. On prend donc la copie
     *  installée localement, ou rien. D'où une pile de repli qui doit tenir
     *  toute seule.
     *
     *  L'ordre, et pourquoi :
     *    1. Tahoma      — la demande. Livrée avec Windows depuis 2000, et avec
     *                     Microsoft Office sur macOS (donc souvent présente).
     *    2. Verdana     — le repli le plus proche : même dessinateur (Matthew
     *                     Carter), même squelette, même grande hauteur d'x.
     *                     Tahoma est en gros une Verdana resserrée : le texte
     *                     sera un peu plus large, jamais dépaysant. Présente
     *                     partout où Tahoma manque (macOS, core web font).
     *    3. DejaVu Sans — le trou restant, c'est Linux, qui n'a ni l'une ni
     *                     l'autre. DejaVu Sans y est le défaut de fait, et
     *                     elle est METRIQUEMENT compatible avec Verdana
     *                     (héritage Bitstream Vera) : les largeurs suivent.
     *    4. sans-serif  — le filet de sécurité.
     *
     *  ⚠️ `system-ui` et `-apple-system` ont été RETIRÉS volontairement : sur
     *  Windows ils résolvent vers Segoe UI, c'est-à-dire exactement la police
     *  qu'on vient de remplacer. Les laisser devant aurait annulé la demande. */
    font-family: Tahoma, Verdana, "DejaVu Sans", sans-serif;
    font-size: 16px; line-height: 1.5;
}

/* ------------------------------------------------------------------ entete */

.entete { text-align: center; padding: 2.5rem 1rem 1rem; }
/*  🚨 TAHOMA N'A QUE DEUX GRAISSES : normal (400) et gras (700). IL N'Y A RIEN
 *  AU-DESSUS. Toute valeur > 700 (800, 900, `bolder`) ne peut donc PAS être
 *  honorée : dans le meilleur des cas le navigateur retombe sur le vrai gras
 *  700 et le 900 n'aura servi à rien, dans le pire il « fabrique » la graisse
 *  manquante en épaississant le dessin lui-même (synthetic bold) — un gras
 *  bavant, aux contre-formes bouchées, et c'est sur les GRANDES tailles comme
 *  ce titre que ça se voit le plus.
 *
 *  D'où la règle, valable dans TOUT le site : on ne dépasse jamais 700. Cette
 *  marque était en 900, elle est en 700 — et à l'écran elle est identique en
 *  mieux dessinée. Toutes les autres occurrences (800/900) ont été ramenées à
 *  700 en même temps ; elles renvoient toutes ici.
 *
 *  ⚠️ 600 est SANS DANGER et n'a pas été touché : l'algorithme d'appariement
 *  CSS fait pointer 600 vers le 700 réel, pas vers une graisse synthétisée.
 *  C'est bien au-DESSUS de 700 que le piège se referme. */
/*  🚨 PLAFOND BAISSÉ DE 3.5rem À 3.3rem — ET LA MARQUE NE PEUT PAS PASSER À LA
 *  LIGNE POUR SE RATTRAPER.
 *
 *  Le HTML est `<span>WORMS</span><span>FINDER</span>`, collés, SANS espace :
 *  l'écart vient du `margin-left: .3em` de `.marque-finder`. Or sans caractère
 *  d'espace il n'y a aucun point de coupure — la marque ne se replie donc
 *  jamais sur deux lignes, elle DÉBORDE de sa boîte, purement et simplement.
 *
 *  Largeurs mesurées dans les vraies fontes (11 glyphes + interlettrage +
 *  0,3 em), pour un budget de 520 px (`.accueil-boite`) :
 *
 *                    Segoe UI    Tahoma    Verdana
 *      à 3.5rem        460 px    489 px     532 px  ← Verdana déborde de 12 px
 *      à 3.3rem        434 px    461 px     501 px  ← tout passe
 *
 *  Tahoma seule tiendrait à 3.5rem. C'est le REPLI qui casse : Verdana est ~9 %
 *  plus large que Tahoma, et c'est elle qu'on sert à tout visiteur sans Tahoma
 *  installée. Une taille qui n'est correcte que pour le premier nom de la pile
 *  n'est pas une taille correcte.
 *
 *  ⚠️ Reste un cas limite assumé : à 320 px de large, le `clamp()` bute sur son
 *  minimum de 2rem, et en Verdana la marque mord alors de ~8 px dans la marge
 *  de page de chaque côté. Elle reste à l'écran, et baisser le minimum
 *  rapetisserait la marque sur TOUS les téléphones pour couvrir la seule
 *  combinaison « écran de 320 px ET Tahoma absente ». Le minimum reste donc à
 *  2rem, qui passe partout dès 375 px, y compris en Verdana. */
.marque {
    font-weight: 700; font-size: clamp(2rem, 7vw, 3.3rem);
    letter-spacing: .04em; line-height: 1;
    text-shadow: 0 3px 0 rgba(0, 0, 0, .45);
}
.marque-worms  { color: var(--jaune); }
.marque-finder { color: var(--texte); margin-left: .3em; }
.baseline { color: var(--texte-doux); margin: .6rem 0 0; font-size: .95rem; }

/*  L'accroche de l'en-tête de l'app. Elle vit dans `.entete-marque`, le bloc
    qui empile le nom et cette ligne — voir le commentaire dans index.html pour
    la raison (`.entete--app` est un flex en `space-between`).

    ⚠️ Plus petite et bien plus serrée que la `.baseline` de l'accueil : sur
    l'accueil elle a toute la page, ici elle partage une barre avec les onglets
    justes en dessous. Chaque rem pris ici repousse le formulaire hors de vue. */
.entete-marque { min-width: 0; }
.baseline--app { margin: .15rem 0 0; font-size: .82rem; }

/*  La mascotte : le ver rose à la loupe.
 *
 *  🚨 ELLE S'AJOUTE AU NOM, ELLE NE LE REMPLACE PAS. L'image ne contient aucun
 *  texte : seule, elle ne dit pas où on est. « WORMS FINDER » reste donc écrit
 *  à côté dans les deux écrans, et l'image est décorative (alt vide).
 *
 *  `image-rendering: pixelated` est indispensable — c'est du pixel art, et le
 *  lissage par défaut du navigateur le transforme en bouillie dès qu'on le
 *  réduit. Même raison que les drapeaux et les pierres tombales.
 *
 *  🔑 On impose la HAUTEUR et on laisse la largeur libre, jamais l'inverse :
 *  c'est ce qui garantit que le ver ne s'écrase pas. Et `flex: none` avec, car
 *  dans l'en-tête l'image est un élément flex : sans ça elle se ferait
 *  comprimer en largeur quand la place manque — donc déformer. */
.marque-mascotte {
    display: block; flex: none;
    width: auto;
    image-rendering: pixelated;
    /*  Le nom porte une ombre portée dure ; sans la même sous la mascotte, le
        ver flotte au-dessus du décor alors que les lettres, elles, s'y posent. */
    filter: drop-shadow(0 3px 0 rgba(0, 0, 0, .45));
}

/*  Accueil : au-dessus des mots et centrée. 96 px, à peine plus haut que les
    lettres — assez pour qu'on la voie en arrivant, pas assez pour repousser le
    formulaire hors de l'écran. */
.marque-mascotte--grande { height: 96px; margin: 0 auto .35rem; }

/*  En-tête de l'app : à gauche des mots et alignée sur leur milieu. C'est le
    seul endroit où la marque devient une ligne : sans flex, une image de 38 px
    à côté d'un texte de 29 px se cale sur la ligne de base et pend trop bas. */
.marque--petite { display: flex; align-items: center; }

/*  L'ÉTIQUETTE « beta », posée de travers sur le nom.
 *
 *  🚨 `max(.68rem, .42em)` ET PAS `.42em` TOUT SEUL. La taille du nom vient de
 *  `.marque--petite { font-size: clamp(1.2rem, 4vw, 1.8rem) }` — et cette règle
 *  vit dans **decor.css**, pas ici (voir le piège de cascade documenté plus bas
 *  dans ce fichier). En `em` pur, l'étiquette suivrait ce clamp jusqu'à son
 *  plancher : 1,2rem × 0,42 = 8 px sur un téléphone, illisible. Le `max()` lui
 *  donne un plancher qui lui est propre, sans figer sa taille sur grand écran.
 *  ⚠️ Le `em` reste relatif au PARENT, donc peu importe qui gagne la cascade sur
 *  `.marque--petite` : l'étiquette suit, quoi qu'il arrive.
 *
 *  🚨 ET ELLE NE DOIT PAS ÉLARGIR LA MARQUE. Le nom n'a AUCUN point de coupure
 *  (deux spans collés, l'écart vient d'un `margin-left`), donc il ne se replie
 *  jamais : il déborde. Le budget mesuré est de 520 px pour la marque de
 *  l'accueil. Ici on ajoute ~34 px à 1,8rem et ~28 px au plancher — et comme
 *  l'en-tête de l'app est un flex avec `min-width: 0`, l'étiquette rétrécit la
 *  place du nom au lieu de pousser quoi que ce soit hors de l'écran.
 *
 *  🔑 `flex-start` et pas `center` : elle se pose en HAUT du nom, comme une
 *  pastille collée à la va-vite. Centrée, elle se lit comme un mot du titre.
 *  ⚠️ `transform-origin: left bottom` : la rotation pivote autour du coin qui
 *  touche le « R » de FINDER, donc l'étiquette s'écarte du nom au lieu de mordre
 *  dedans. Avec l'origine par défaut (le centre), elle recouvre la dernière
 *  lettre à cette taille. */
.marque-beta {
    align-self: flex-start;
    margin-left: .32em;
    margin-top: .12em;
    font-size: max(.68rem, .42em);
    font-weight: 700;
    letter-spacing: .1em;
    color: var(--jaune);
    transform: rotate(-11deg);
    transform-origin: left bottom;
    /*  Le relief du nom est une ombre portée héritée de `.marque`, calée sur sa
        taille : à 12 px elle empâte l'étiquette au lieu de la détacher. */
    text-shadow: 0 1px 0 rgba(0, 0, 0, .55);
}
/*  38 px : la hauteur d'un bouton de la barre d'identité en face. Au-delà,
    l'en-tête grandit et pousse les onglets vers le bas sur toutes les pages. */
.marque-mascotte--petite { height: 38px; margin-right: .5rem; }

/* -------------------------------------------------------------------- main */

main { max-width: 900px; margin: 0 auto; padding: 0 1rem 3rem; }
.bloc { margin-bottom: 2.5rem; }

/*  🔑 LES TEXTES QUI N'ONT AUCUNE CARTE SOUS EUX.
 *
 *  Tout le reste de l'interface repose sur un aplat (--carte) qui masque le
 *  décor. Ceux-là, non : les titres de section, les intertitres, l'accroche de
 *  l'accueil, les onglets et le pied de page sont posés DIRECTEMENT sur le
 *  décor animé. Le voile de lisibilité (#voile-lisibilite, dans decor.css) leur
 *  donne déjà une base sombre ; cette ombre finit le travail en détachant le
 *  glyphe du grain du terrain, qui est très contrasté à l'échelle du pixel.
 *
 *  ⚠️ Elle est posée à la pièce et JAMAIS sur un conteneur : `text-shadow`
 *  s'hérite, et l'appliquer à `main` la collerait aussi à tout le texte des
 *  cartes — où elle ne sert à rien et alourdit le rendu.
 *  ⚠️ .marque a déjà la sienne (une ombre dure, décorative) : ne pas l'ajouter
 *  ici, les deux se cumuleraient. */
.titre-section, .mes-titre, .lancement-titre, .baseline, .pied, .onglet,
/*  Une `.aide` est presque toujours DANS une carte — sauf quand elle est fille
    directe d'un `.bloc`, comme la phrase qui présente la heatmap WormNET. Ce
    sélecteur ne vise que celles-là. */
.bloc > .aide {
    text-shadow: var(--ombre-texte);
}

.titre-section {
    font-size: .8rem; text-transform: uppercase; letter-spacing: .12em;
    color: var(--texte-doux); margin: 0 0 .75rem; font-weight: 700;
}
.aide { color: var(--texte-doux); font-size: .85rem; margin: .4rem 0; }
.aide--mode { font-style: italic; }
.erreur { color: #ff9f92; font-size: .85rem; margin: .5rem 0 0; }

/*  L'accusé de réception d'un rendez-vous posé. Même gabarit que `.erreur` —
    même place, même taille — parce que c'est la même fonction : dire ce qui
    vient de se passer, à l'endroit où on l'a déclenché.
    ⚠️ Un filet à gauche plutôt qu'un pavé coloré : il doit se remarquer sans
    prendre la place de ce qu'il confirme. */
.confirme {
    color: var(--vert); font-size: .85rem; margin: .6rem 0 0;
    border-left: 3px solid var(--vert); padding: .1rem 0 .1rem .6rem;
}

/* ---------------------------------------------------------------- formulaire */

.carte {
    background: var(--carte); border: 1px solid var(--bord);
    border-radius: 12px; padding: 1.1rem; margin-bottom: 1rem;
    box-shadow: var(--ombre-bloc);
}
.carte--match { border-left: 4px solid var(--vert); }

.grille-form { display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

/*  LES BLOCS DU FORMULAIRE DE RECHERCHE
 *
 *  🔑 UNE QUESTION PAR BLOC. Empilées sans séparation, les six catégories du
 *  formulaire se lisaient comme une seule liste de champs : l'œil ne voyait plus
 *  où finissait « Games » et où commençait « Platform ». Retour du propriétaire,
 *  mot pour mot : « ça fait un peu tout mélangé ».
 *
 *  🚨 LE FILET EST BEAUCOUP PLUS PÂLE QUE `--bord`, ET C'EST VOULU. `--bord` est
 *  la bordure qui ENTOURE une carte ; le reprendre ici découperait la carte en
 *  cinq cartes empilées, ce qui est exactement l'effet inverse de celui qu'on
 *  cherche. Ici on ne délimite pas, on RESPIRE : le trait est un repère, l'espace
 *  fait le travail.
 *
 *  ⚠️ `+` et pas `:not(:first-child)` : ce qui doit être séparé, c'est un bloc
 *  D'UN AUTRE bloc. Un bloc précédé d'autre chose (un commentaire, un encart
 *  masqué) ne doit pas hériter d'un trait qui ne sépare rien. */
.bloc-form + .bloc-form {
    margin-top: 1.4rem;
    padding-top: 1.3rem;
    border-top: 1px solid rgba(168, 198, 224, .13);
}

/*  L'action se détache un peu plus : ce n'est pas une question de plus, c'est
    ce qui envoie les quatre précédentes. */
.bloc-form--action { margin-top: 1.7rem; padding-top: 1.5rem; }

/*  LE COMPTEUR D'ATTEIGNABILITÉ (FILE-ATTENTE.md §1.2, décision 2)
 *
 *  🚨 CE N'EST PAS UNE ESTIMATION DE TEMPS, ET SON HABILLAGE LE DIT AUSSI. Un
 *  chiffre en gros, en jaune, au milieu d'un formulaire, se lirait comme une
 *  promesse. Celui-ci est de la taille d'un texte d'aide et il commence par un
 *  mot, pas par un nombre : c'est un CONSTAT posé à côté d'un critère, pas un
 *  résultat qu'on attend.
 *
 *  ⚠️ Il vit DANS le `.bloc-form` du critère qu'il qualifie — c'est tout
 *  l'intérêt : un décompte rangé en bas du formulaire aurait obligé à deviner à
 *  quoi il se rapporte, et le découpage en blocs (demande explicite du
 *  propriétaire, « ça fait un peu tout mélangé ») serait resté décoratif. */
.compte {
    display: block;
    margin: .35rem 0 0;
    font-size: .8rem;
    color: var(--texte-doux);
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    line-height: 1.45;
}
/*  Dans un `.etiquette-champ` (Mode, Format), le label est en petites capitales
    et en gras : sans cette remise à plat, le décompte hériterait de la casse de
    son parent et deviendrait un second titre. */
.etiquette-champ > .compte { margin-top: .3rem; }

/*  LA CARTE STANDBY, PROPOSÉE AU PREMIER SEARCH (§1.4, décision 4)
 *
 *  🔑 Elle est plus voyante que la carte « Close the tab » du bas, et c'est le
 *  sujet du chantier : le Standby doit devenir le geste PRINCIPAL. 📏 Avec
 *  `#AnythingGoes` vide 37,5 % du temps, la chance qu'il y ait quelqu'un à
 *  l'instant précis est faible, celle qu'il y en ait un dans les 24 h est bonne.
 *  🚨 Elle n'apparaît JAMAIS au chargement — voir proposerStandby() dans
 *  public/file.js. Le CSS ne fait que l'habiller. */
.carte--standby { border-left: 4px solid var(--jaune); background: var(--carte-fort); }

/*  LE BANDEAU D'ARRIVÉE AUTOMATIQUE, EN HAUT DE LA ROOM (décision B)
 *
 *  🚨 Rouge et non jaune : ce n'est pas une bonne nouvelle de plus, c'est
 *  « attention, tu es quelque part sans l'avoir demandé ». Quelqu'un qui ne
 *  comprend pas où il a atterri doit trouver ce bandeau avant tout le reste. */
.carte--auto { border-left: 4px solid var(--rouge); background: var(--carte-fort); }

/*  LE BANDEAU D'APRÈS-SORTIE (§1.8, décision 8)
 *  Il dit que la recherche est repartie toute seule et pointe la suite. Vert :
 *  rien n'est perdu, c'est une continuation, pas un incident. */
.vide--suite { border-color: rgba(126, 200, 80, .5); }
.vide--suite .vide-titre { color: var(--vert); }

/*  L'ÉCRAN « PERSONNE » (§1.3, décision 3)
 *  C'est l'écran le plus fréquent du site : il a droit à l'espace d'un écran,
 *  pas à celui d'un message d'erreur. */
.vide--personne { padding: 1.4rem 1.2rem; text-align: left; }
.vide--personne .rangee { justify-content: flex-start; }

/*  🚨 LES DEUX ÉTATS DU RADAR NE SE DISENT PAS PAREIL, ET NE SE PEIGNENT PAS
    PAREIL. « La collecte tourne et il n'y a personne » est une INFORMATION ;
    « la collecte est muette » est une PANNE. Une liste vide ne les distingue
    pas — c'est la pire chose qu'un radar puisse faire, et c'est le principe qui
    gouverne tout ce projet. */
.etat-radar { margin: .55rem 0; padding-left: .6rem; border-left: 3px solid var(--bord); }
.etat-radar--vivant { border-left-color: var(--vert); }
.etat-radar--panne  { border-left-color: var(--rouge); color: #ff9f92; }

/*  Le pouls d'activité. 🚨 Une phrase sur LE MONDE (« voilà l'heure la plus
    chargée qu'on ait mesurée »), jamais une promesse sur VOUS, et jamais une
    durée d'attente. */
.pouls { margin: .55rem 0; }

/*  ⚠️ Le « When » n'est plus le premier élément de son bloc : il suit Mode et
    Format, et c'est cet écart-là qui les sépare — pas un trait, qui trancherait
    trois précisions portant sur la même partie. */
.rangee--quand { margin-top: .9rem; }

/*  Les deux gestes du « When », côte à côte.
 *
 *  🔑 `1 1 0` et non `1 1 auto` : les deux boutons doivent faire la MÊME
 *  largeur, sinon « Schedule a date » — plus long — écrase « Right now » et le
 *  choix n'a plus l'air d'un choix entre deux égaux. C'est de la hiérarchie
 *  visuelle, pas de l'esthétique : un bouton plus large se lit comme le bon.
 *  ⚠️ `min-width: 0` avec, sinon le contenu impose sa largeur et le `flex: 1 1 0`
 *  ne sert à rien — le piège classique du flex. */
.choix-quand { display: flex; gap: .5rem; margin-top: .3rem; }
.quand-bouton {
    flex: 1 1 0; min-width: 0;
    display: flex; align-items: center; gap: .55rem;
    padding: .5rem .7rem;
    background: var(--carte-fort); color: var(--texte);
    border: 1px solid var(--bord); border-radius: 9px;
    font: inherit; font-weight: 600; text-align: left;
    cursor: pointer;
    transition: border-color .12s, background .12s;
}
/*  🚨 Pixel art : au plus proche voisin, jamais lissé. Ces icônes font 68 px de
    large à la source ; interpolées, elles deviennent une bouillie grise. */
.quand-bouton img {
    flex: none; width: 30px; height: 30px;
    image-rendering: pixelated;
}
.quand-bouton span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.quand-bouton:hover { border-color: rgba(255, 210, 63, .5); }
/*  🚨 L'ÉTAT CHOISI NE TIENT PAS QU'À LA COULEUR. Un liseré jaune seul est
    invisible pour une partie des lecteurs — et pour tout le monde sur un écran
    au soleil. On double par le fond ET par l'épaisseur du trait. */
.quand-bouton--choisi {
    border-color: var(--jaune);
    box-shadow: inset 0 0 0 1px var(--jaune);
    background: rgba(255, 210, 63, .12);
    color: #ffe9a3;
}
.quand-bouton:focus-visible { outline: 2px solid var(--jaune); outline-offset: 2px; }

/*  Sur téléphone, deux boutons côte à côte tombent sous la largeur lisible :
    on les empile plutôt que de tronquer leur libellé. */
@media (max-width: 460px) {
    .choix-quand { flex-direction: column; }
}

.etiquette-champ {
    display: flex; flex-direction: column; gap: .3rem;
    font-size: .75rem; text-transform: uppercase; letter-spacing: .08em;
    color: var(--texte-doux); font-weight: 700;
}

.champ {
    width: 100%; padding: .6rem .7rem;
    background: rgba(0, 0, 0, .25); color: var(--texte);
    border: 1px solid var(--bord); border-radius: 8px;
    font: inherit; font-size: .95rem; font-weight: 400; letter-spacing: normal; text-transform: none;
}
.champ:focus { outline: 2px solid var(--jaune); outline-offset: 1px; }
select.champ { cursor: pointer; }
option { background: var(--ciel-haut); color: var(--texte); }

.rangee { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-top: .9rem; }
.rangee > .champ { flex: 1 1 200px; }
.rangee--centre { justify-content: center; }
.rangee--entre  { justify-content: space-between; align-items: flex-start; }
.rangee--entre .ligne-infos { flex: 1 1 260px; }

/*  Tags d'INTENTION, jamais de niveau : ils disent ce que le joueur cherche. */
.tags-choix { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .9rem; }
.tag-choix {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .7rem; border: 1px solid var(--bord); border-radius: 999px;
    font-size: .85rem; color: var(--texte-doux); cursor: pointer; user-select: none;
}
.tag-choix:hover { border-color: var(--jaune); color: var(--texte); }
.tag-choix:has(input:checked) { background: rgba(255, 210, 63, .16); border-color: var(--jaune); color: var(--jaune); }

/* ------------------------------------------------------------------ boutons */

.bouton {
    flex: none; display: inline-block; padding: .55rem 1.1rem;
    background: var(--carte-fort); color: var(--texte);
    border: 1px solid var(--bord); border-radius: 8px;
    font: inherit; font-weight: 700; font-size: .9rem;
    text-decoration: none; cursor: pointer;
    transition: transform .08s, box-shadow .08s, background .12s;
}
/*  Le survol reste dans la famille bleu nuit du repos plutôt que de virer au
    gris : un voile blanc par-dessus un aplat bleu délave la teinte au lieu
    d'éclaircir le bouton. */
.bouton:hover { background: rgba(52, 92, 136, .92); }

.bouton--principal {
    background: var(--jaune); color: #241a00; border-color: transparent;
    text-transform: uppercase; letter-spacing: .05em;
    box-shadow: 0 3px 0 #b8961f;
}
/*  🔑 UN BOUTON QUI PORTE UNE PHRASE, PAS UN VERBE. Les capitales vont bien à
    un ordre court — SEARCH, JOIN, LEAVE THIS ROOM. Sur « I'll be there » elles
    se lisent comme un cri, et l'apostrophe y devient une verrue. On rend donc sa
    casse au libellé, sans toucher au reste des boutons jaunes.
    ⚠️ L'interlettrage repart aussi : il est là pour aérer des capitales, et il
    disloque un texte en minuscules. */
.bouton--phrase { text-transform: none; letter-spacing: normal; }

.bouton--principal:hover  { background: #ffdd6b; }
.bouton--principal:active { transform: translateY(3px); box-shadow: 0 0 0 #b8961f; }

.bouton--discret { background: transparent; color: var(--texte-doux); font-weight: 600; }
.bouton--discret:hover { color: var(--texte); background: var(--carte-fort); }

/*  LE BOUTON DE SORTIE — « Leave », et lui seul pour l'instant.
 *
 *  🚨 IL PORTAIT `.bouton--discret`, C'EST-À-DIRE TRANSPARENT ET EN TEXTE DOUX :
 *  le bouton le moins visible de l'écran, posé sous le chat et sous le panneau
 *  de lancement. Or depuis la décision B (auto-join sur match direct), on peut
 *  atterrir dans une salle SANS AVOIR CLIQUÉ, et le propriétaire l'a dit
 *  lui-même dans la foulée : « ce que ça rend indispensable, c'est le bouton
 *  Leave — il devra être très visible. »
 *  🔑 Pas jaune pour autant : `.bouton--principal` reste à « We played », qui
 *  est le chemin heureux. Ici c'est un contour rouge — la même teinte que
 *  `.carte--auto`, le bandeau qui l'annonce en haut de room, pour qu'on
 *  reconnaisse la sortie d'un bout à l'autre de l'écran. */
.bouton--sortie {
    background: transparent; color: #ff9f92;
    border-color: rgba(224, 75, 58, .55); font-weight: 700;
}
.bouton--sortie:hover { background: rgba(74, 22, 18, .66); color: #ffc4bb; }

/*  « ADD TO CALENDAR » — sur un créneau futur, à côté de « Stop ».
 *
 *  🚨 IL NE PEUT PAS ÊTRE UN SECOND `.bouton--discret` NU. Les deux vivent sur
 *  la MÊME ligne, à quelques pixels l'un de l'autre, et celui qui se trompe de
 *  cible n'obtient pas un fichier : il ARRÊTE sa recherche. La teinte verte est
 *  celle du liseré de `.ligne--recherche` — « ceci appartient à ta recherche »
 *  — quand `Stop` reste en texte doux, neutre.
 *  ⚠️ C'est un `<a>`, pas un `<button>` : `.bouton` porte déjà
 *  `text-decoration: none` et `display: inline-block`, donc rien à répéter. */
.bouton--agenda {
    background: transparent; color: var(--vert);
    border-color: rgba(126, 200, 80, .35); font-weight: 600;
}
.bouton--agenda:hover { background: rgba(126, 200, 80, .12); color: #b9ea8f; }

/* ------------------------------------------------------------------- lignes */

.liste { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }

.ligne {
    display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
    padding: .85rem 1rem;
    background: var(--carte); border: 1px solid var(--bord);
    border-radius: 10px; box-shadow: var(--ombre-bloc);
    animation: apparition .25s ease-out;
}
.ligne--partie   { border-left: 4px solid var(--jaune); }
.ligne--recherche { border-left: 4px solid var(--vert); margin-bottom: .6rem; }

/*  La vignette du jeu dans une ligne « You are looking for ».
 *
 *  🔑 On impose la LARGEUR et on laisse la hauteur libre, puis `object-fit`
 *  contient le dessin : les logos officiels n'ont aucun rapport d'aspect
 *  commun — certains sont des bandeaux très larges, deux sont des jaquettes
 *  presque carrées. Imposer les deux dimensions les écraserait.
 *
 *  ⚠️ `image-rendering` n'est PAS forcé à `pixelated` ici, contrairement au
 *  reste du site : ce sont des logos vectoriels rastérisés en grand, pas du
 *  pixel art. Les réduire au plus proche voisin les hacherait. */
.recherche-logo {
    flex: none; width: 34px; height: 24px;
    object-fit: contain; object-position: center;
}
/*  Le repli garde EXACTEMENT la même place : sans lui, une ligne sans logo
    verrait son texte se décaler de 34 px par rapport aux autres. */
.recherche-logo--vide {
    display: inline-block;
    background: var(--carte-fort); border-radius: 4px;
}

/*  La méta porte maintenant le CD-ROM à côté du texte, d'où le flex. */
.ligne--recherche .ligne-meta { display: flex; align-items: center; gap: .4rem; }
/*  Le `to` est explicite alors que la spécification sait le reconstituer depuis
 *  la valeur propre de l'élément. C'est délibéré, mais ce n'est QU'UNE MESURE
 *  DE PRUDENCE — aucun bogue observé ne l'exigeait.
 *
 *  ⚠️ Pour mémoire, parce que le piège est coûteux : une ligne mesurée à
 *  `opacity: 0` en permanence ici n'est pas forcément cassée. Chrome GÈLE la
 *  timeline d'animation d'un onglet en arrière-plan — `document.timeline.
 *  currentTime` reste à 0, `requestAnimationFrame` ne se déclenche jamais, et
 *  toute animation reste plantée sur sa première image-clé. Avant de conclure
 *  à un défaut, vérifier `document.visibilityState`. */
@keyframes apparition {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

.ligne-infos { min-width: 0; flex: 1 1 260px; }
.ligne-titre { font-weight: 700; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ligne-meta  { color: var(--texte-doux); font-size: .85rem; margin-top: .15rem; }
.ligne-meta strong { color: var(--texte); font-weight: 600; }
.ligne-note  { color: var(--texte); font-size: .88rem; margin-top: .3rem; font-style: italic; opacity: .85; }

.mes-titre {
    font-size: .75rem; text-transform: uppercase; letter-spacing: .1em;
    color: var(--texte-doux); font-weight: 700; margin: 1.2rem 0 .5rem;
}
.standby { color: var(--jaune); }

.etiquettes { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.etiquette {
    font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    padding: .2rem .5rem; border-radius: 5px;
    background: rgba(255, 255, 255, .1); color: var(--texte-doux);
}
.etiquette--mode { background: rgba(255, 210, 63, .18); color: var(--jaune); }
/*  🚨 `.etiquette--tus` était POSÉE PAR LE JAVASCRIPT DEPUIS TOUJOURS et
 *  n'avait aucune règle : sur les cartes de salle et de chercheur, la ligue se
 *  peignait donc comme un tag ordinaire, en gris, au milieu des tags. Rien
 *  n'était cassé — la classe de base l'habillait — et c'est bien pourquoi ça a
 *  tenu si longtemps : un modificateur sans règle ne fait pas d'erreur, il fait
 *  une nuance qui manque.
 *  🔑 Le jaune est celui du panneau TUS de la room (`.carte--tus`), et le
 *  contour la distingue de `--mode` qui partage la teinte : une ligue n'est pas
 *  un scheme, et surtout ce n'est PAS un niveau — rien ici ne classe personne. */
.etiquette--tus {
    background: rgba(255, 210, 63, .14);
    color: var(--jaune);
    border: 1px solid rgba(255, 210, 63, .42);
}

/*  🚨 LA DEVINETTE NE SE PEINT PAS COMME UN FAIT. 📏 « <scheme> for <pseudo> »
    donne 94,5 % de justesse ; un nom libre, 25,6 %. Le second cas s'écrit
    « looks like Roper » ET porte un contour pointillé : le mot dit
    l'incertitude, la forme la redit à qui parcourt la liste sans lire.
    🔑 La règle est ICI, donc GLOBALE, et pas sous `#wormnet-file` comme avant :
    la liste de la file et celle de l'onglet Radar montrent la MÊME annonce, avec
    la même incertitude. La peindre dans une seule des deux laissait l'autre
    présenter une devinette exactement comme une certitude — ce qu'elle faisait. */
.etiquette--devine {
    background: transparent;
    border: 1px dashed rgba(255, 210, 63, .55);
    color: rgba(255, 210, 63, .85);
    font-style: italic;
}
.etiquette--mdp  { background: rgba(224, 75, 58, .18);  color: #ff9f92; }

/* ------------------------------------------------------------------ bandeau */

.bandeau {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: .7rem 1rem; margin-bottom: .8rem;
    border: 1px solid var(--bord); border-radius: 10px;
    /*  🚨 Le bandeau est l'élément le PLUS exposé de la page : il vit en haut
     *  d'un onglet, donc souvent au ras du terrain quand on a défilé. On lui
     *  donne l'aplat le plus dense (--carte-fort), pas celui des cartes. */
    background: var(--carte-fort); font-size: .9rem;
    box-shadow: var(--ombre-bloc);
}
.pastille { width: 10px; height: 10px; border-radius: 50%; flex: none; background: var(--texte-doux); }
.bandeau-detail { color: var(--texte-doux); margin-left: auto; font-variant-numeric: tabular-nums; }

.bandeau--vivant { border-color: rgba(126, 200, 80, .45); }
.bandeau--vivant .pastille { background: var(--vert); box-shadow: 0 0 10px var(--vert); }

/*  🚨 Le cas qui compte : la collecte est morte. L'ecran doit crier, sinon une
 *  liste vide passerait pour « personne ne joue » alors qu'on ne sait rien. */
/*  ⚠️ Ce fond REMPLACE --carte-fort, il ne s'y ajoute pas : à .12 il ne masquait
    donc plus rien du tout par-dessus le décor, et le bandeau d'alerte était le
    moins lisible des trois. On garde la teinte de la dynamite, on l'assombrit
    et on monte l'opacité au niveau des autres blocs. */
.bandeau--mort { border-color: rgba(224, 75, 58, .7); background: rgba(74, 22, 18, .82); }
.bandeau--mort .pastille { background: var(--rouge); box-shadow: 0 0 10px var(--rouge); }

/* ------------------------------------------------------- l'attente : le CD */

/*  🔑 SEUL L'ÉTAT « ON ATTEND » DEVIENT UN CD-ROM. `.pastille` n'est pas qu'un
 *  indicateur de chargement : c'est aussi un VOYANT D'ÉTAT — vert sous
 *  `.bandeau--vivant`, rouge sous `.bandeau--mort`, jaune sous `.wn-eteint`
 *  (wormnet.css). Ces trois-là restent des points, et c'est capital : un voyant
 *  vert « le serveur répond » qui se mettrait à tourner dirait au visiteur que
 *  ça patine alors que tout va bien. On ne convertit donc QUE `--attente`.
 *
 *  Ce qui disparaît ici : `@keyframes battement`, une pastille qui pulsait à
 *  1,2 s. Rien d'autre ne l'utilisait.
 *
 *  🚨 CE SPRITE N'EST PAS UN CD QUI TOURNE, ET LE SAVOIR CHANGE LE RÉGLAGE.
 *  Les 25 images de `Misc/cdrom.png` sont un DÉVOILEMENT en un seul coup : le
 *  disque bleu est nu à l'image 0, le trèfle radioactif se déplie en balayant,
 *  et il est complet à l'image 24. Mesuré, pas supposé — la rotation angulaire
 *  du trèfle entre les images 15 et 24 vaut 0°. Voir outils/extraire-cdrom.js.
 *  Rejouée en boucle, la séquence se lit comme un REMPLISSAGE circulaire, et le
 *  point de bouclage tombe sur la seule image où le disque est nu : la remise à
 *  zéro est franche, elle ne coupe pas un mouvement en son milieu.
 *
 *  ⚠️ POURQUOI PAS `animation-direction: alternate`, qui rendrait le raccord
 *  invisible : à l'envers, le symbole se RÉTRACTE — ça se lit comme une
 *  annulation, soit l'inverse du message.
 *
 *  Cadence : 25 images en 1,5 s, soit ~17 images/s. Plus vite, le dévoilement
 *  scintille et on ne lit plus le symbole ; plus lentement, les quatre
 *  premières images (disque quasi nu) donnent l'impression que c'est planté. */

:root {
    /*  🔑 ON IMPOSE LA HAUTEUR, LA LARGEUR EN DÉCOULE — jamais l'inverse, même
     *  piège que `.marque-mascotte`. Une bande de fond oblige à donner les deux
     *  cotes, alors la largeur est CALCULÉE sur le rapport réel du sprite
     *  découpé (147x95, soit 1,5474) et arrondie.
     *  🚨 Les deux valeurs sont ENTIÈRES exprès. `steps(25)` déplace le fond de
     *  --cdrom-l à chaque pas : une valeur fractionnaire ferait tomber le pas
     *  entre deux pixels du périphérique, et un liseré de l'image voisine
     *  apparaîtrait sur le bord. 37/24 = 1,5417 contre 1,5474 : 0,4 % d'écart,
     *  invisible, et le pas retombe pile.
     *  ⚠️ 160 px, la taille du sprite du jeu, serait énorme pour un indicateur
     *  d'attente. 24 px de haut, c'est la hauteur d'une ligne de texte. */
    --cdrom-h: 24px;
    --cdrom-l: 37px;
    /*  🚨 CE NOMBRE EST COUPLÉ À LA BANDE. C'est `outils/extraire-cdrom.js` qui
     *  découpe `Misc/cdrom.png`, et il ANNONCE ce compte à chaque exécution. S'il
     *  changeait (une case vide écartée en fin de colonne, par exemple) sans
     *  qu'on le reporte ici, rien ne casserait bruyamment : le fond se
     *  décalerait juste d'une fraction d'image à chaque pas, et l'animation
     *  montrerait des demi-disques. Le genre de panne qu'on ne voit pas. */
    --cdrom-images: 25;
}

/*  `.cdrom` est la forme réutilisable — elle sert aussi aux lignes « You are
 *  looking for » (file.js). Le sélecteur du bandeau la rejoint plutôt que de la
 *  recopier : deux définitions finiraient par diverger. */
.cdrom,
.bandeau--attente .pastille {
    width: var(--cdrom-l);
    height: var(--cdrom-h);
    flex: none;
    /*  On défait ce que `.pastille` avait posé : ce n'est plus un point. */
    border-radius: 0;
    box-shadow: none;
    /*  🚨 `image-rendering: pixelated` est obligatoire : c'est un vrai sprite du
     *  jeu, et le lissage du navigateur transforme du pixel art réduit en
     *  bouillie. Même raison que les drapeaux et la mascotte. */
    image-rendering: pixelated;
    background: url(/assets/cdrom.png) 0 0 / calc(var(--cdrom-l) * var(--cdrom-images)) var(--cdrom-h) no-repeat;
    /*  ⚠️ Seule `background-position` bouge : `transform` reste libre, comme
     *  pour les sprites du décor.
     *  ⚠️ En longhands et non en raccourci `animation:` — même forme que
     *  `.sprite` dans decor.css. Un raccourci qui contient un `var()` devient
     *  une déclaration à substitution différée : elle marche, mais elle ne se
     *  débogue plus dans l'inspecteur, où le raccourci s'affiche tel quel. */
    animation-name: cdrom-devoile;
    animation-duration: 1.5s;
    animation-timing-function: steps(var(--cdrom-images));
    animation-iteration-count: infinite;
}
@keyframes cdrom-devoile {
    to { background-position-x: calc(var(--cdrom-l) * var(--cdrom-images) * -1); }
}

/*  🔑 L'IMAGE EST DÉCORATIVE, L'INFORMATION NE L'EST PAS. C'est une image de
 *  FOND sur une boîte vide : aucun lecteur d'écran ne l'annonce, donc rien
 *  n'est dit deux fois — le sens est porté par le texte d'à côté
 *  (« Connecting to the channel… », « searching for 2min »), qui reste seul
 *  et suffisant si l'image ne se charge pas. */

@media (prefers-reduced-motion: reduce) {
    /*  🔑 QUELQU'UN QUI A DEMANDÉ MOINS D'ANIMATIONS DOIT QUAND MÊME COMPRENDRE
     *  QU'ON ATTEND. Deux choses le lui disent sans bouger d'un pixel : le
     *  texte, inchangé, et le CD lui-même — c'est un dessin, pas un point, donc
     *  l'état « attente » reste distinct du vert et du rouge à l'œil nu.
     *  ⚠️ On fige sur la DERNIÈRE image, celle où le symbole est complet : une
     *  image du milieu montrerait un trèfle à moitié déplié, qu'on lirait comme
     *  un défaut d'affichage plutôt que comme une attente. */
    .cdrom,
    .bandeau--attente .pastille {
        animation: none;
        background-position-x: calc(var(--cdrom-l) * (var(--cdrom-images) - 1) * -1);
    }
}

/* ------------------------------------------------------------------- vide */

/*  🚨 Le bloc « vide » n'avait AUCUN fond : un cadre en pointillés et du texte
 *  doux, posés à nu sur le terrain. C'était le pire cas de la page, et c'est
 *  aussi celui qu'on voit le plus souvent au début (aucun salon, aucune
 *  partie). Il prend donc un aplat, un peu plus léger que les cartes — il ne
 *  contient qu'une phrase, et le décor a le droit de rester visible dessous. */
.vide {
    text-align: center; padding: 2.2rem 1rem;
    border: 1px dashed var(--bord); border-radius: 12px; color: var(--texte-doux);
    background: rgba(12, 28, 52, .62); box-shadow: var(--ombre-bloc);
}
.vide-titre { font-size: 1.1rem; color: var(--texte); font-weight: 700; margin-bottom: .4rem; }
/*  Même correction que .bandeau--mort : .07 de rouge sur du terrain vert, c'est
    invisible. Rouge sombre opaque, pour la même raison. */
.vide--alerte { border-color: rgba(224, 75, 58, .6); background: rgba(74, 22, 18, .66); }
.vide--alerte .vide-titre { color: #ff9f92; }

/*  LE BANDEAU DE PANNE DE NOTRE PROPRE API
 *
 *  🚨 Il doit se lire AVANT le formulaire qu'il explique — sinon on lit d'abord
 *  une grille de jeux vide et on en tire la conclusion qu'elle suggère : que le
 *  site est vide. D'où le `fixed` en haut de fenêtre, sur les deux écrans
 *  (accueil et application), et pas un encadré rangé dans l'un des deux.
 *  ⚠️ `z-index: 40`, juste SOUS `.voile` (50) : une modale ouverte doit rester
 *  au-dessus de tout, y compris de ça. */
.panne-api {
    position: fixed; top: 0; left: 0; right: 0; z-index: 40;
    margin: 0; padding: .7rem 1.1rem;
    background: rgba(74, 22, 18, .96); color: #ffc4bb;
    border-bottom: 2px solid var(--rouge);
    font-size: .9rem; font-weight: 600; text-align: center;
}

/* -------------------------------------------------------------- ready check */

.voile {
    position: fixed; inset: 0; z-index: 50;
    display: flex; align-items: center; justify-content: center; padding: 1rem;
    /*  🔑 C'est LE voile qui doit tout couvrir : le ready check dure quelques
     *  secondes et rien d'autre ne compte. Monté de .82 à .90 et le flou de 3 à
     *  7 px — derrière, ce n'est plus un ciel de nuit mais un terrain vert avec
     *  des vers qui bougent, et à .82 on les voyait s'agiter à travers la
     *  modale. L'opacité seule suffit : le flou n'est qu'un bonus. */
    background: rgba(4, 12, 24, .90); backdrop-filter: blur(7px);
    animation: apparition .2s ease-out;
}
.modale {
    max-width: 460px; width: 100%; text-align: center;
    padding: 2rem 1.5rem;
    background: linear-gradient(180deg, #17405c, #102b3f);
    border: 1px solid var(--jaune); border-radius: 16px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .5);
}
/*  Graisse plafonnée à 700 : Tahoma n'a rien au-dessus (voir .marque). En
    1.6rem capitales, un 900 synthétisé se serait vu. */
.modale-titre {
    font-size: 1.6rem; font-weight: 700; color: var(--jaune);
    text-transform: uppercase; letter-spacing: .06em; margin-bottom: .6rem;
}
.modale-detail { margin: 0 0 1.2rem; }

.compte-a-rebours { height: 6px; background: rgba(0, 0, 0, .35); border-radius: 999px; overflow: hidden; }
.jauge { height: 100%; width: 100%; background: var(--jaune); border-radius: 999px; }

/* ------------------------------------------------- grille de disponibilites */

/*  Deux informations superposees dans la meme case : ce que MOI j'ai coché
 *  (le cadre jaune) et combien d'AUTRES sont libres (le fond). Les separer en
 *  deux grilles obligerait a faire l'aller-retour du regard pour repondre a la
 *  seule question qui compte : « quand est-ce que je trouverai quelqu'un ? » */
.grille-dispos {
    display: grid; gap: 2px; margin: .8rem 0;
    grid-template-columns: 2.6rem repeat(24, 1fr);
    font-size: .62rem;
}
.gd-coin  { }
.gd-heure { color: var(--texte-doux); text-align: center; }
.gd-jour  { color: var(--texte-doux); display: flex; align-items: center; }

.gd-case {
    aspect-ratio: 1; padding: 0; cursor: pointer;
    border: 1px solid transparent; border-radius: 2px;
    background: rgba(255, 255, 255, .05);
}
.gd-case:hover { border-color: var(--texte-doux); }

/*  Fond : nombre d'autres joueurs libres. Meme echelle visuelle que la heatmap
 *  d'activite WormNET, pour que les deux graphiques se lisent pareil. */
.gd-case[data-autres="1"] { background: rgba(126, 200, 80, .25); }
.gd-case[data-autres="2"] { background: rgba(126, 200, 80, .45); }
.gd-case[data-autres="3"] { background: rgba(126, 200, 80, .65); }
.gd-case[data-autres="4"] { background: rgba(126, 200, 80, .85); }

/*  Cadre : mes propres heures. Un cadre plutot qu'un fond, justement pour ne
 *  pas ecraser l'information « combien d'autres ». */
.gd-case--moi { border-color: var(--jaune); box-shadow: inset 0 0 0 1px var(--jaune); }

/* ----------------------------------------------------------------- avatars */

/*  Les 25 pierres tombales du jeu, converties en PNG par
 *  outils/convertir-assets.js. `image-rendering: pixelated` est indispensable :
 *  ce sont des images 24x32, un lissage les transformerait en bouillie. */
.avatars { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .8rem; }

.avatar {
    padding: .3rem; line-height: 0; cursor: pointer;
    background: rgba(0, 0, 0, .25);
    border: 2px solid transparent; border-radius: 8px;
}
.avatar:hover { background: rgba(255, 255, 255, .1); }
.avatar--choisi { border-color: var(--jaune); background: rgba(255, 210, 63, .15); }
.avatar img { width: 24px; height: 32px; image-rendering: pixelated; }

.vignette {
    flex: none; width: 24px; height: 32px;
    image-rendering: pixelated; object-fit: contain;
}
/*  ⚠️ `.vignette--vide` NE VIT PLUS QUE DANS decor.css. Il y avait ici une
    seconde définition, qui ne servait qu'à `guide.html` — la seule page sans
    decor.css. La page a été supprimée le 29/08 ; ce bloc devenait donc une
    règle morte, battue en silence sur index.html par decor.css chargé après.
    🔑 Vérifié AVANT de la retirer, propriété par propriété : decor.css déclare
    bien les huit qu'elle portait (`display`, `align-items`, `justify-content`,
    `background`, `border-radius`, `font-weight`, `font-size`, `color`), plus la
    taille. Rien ne change sur la page principale. */

/* -------------------------------------------------------------- la room */

.lancement-titre {
    font-size: .75rem; text-transform: uppercase; letter-spacing: .1em;
    color: var(--texte-doux); font-weight: 700; margin-bottom: .8rem;
}

/*  Deux colonnes parce qu'il y a deux roles. Un guide unique melangeant
 *  « si tu heberges » et « si tu rejoins » est la meilleure facon de faire
 *  heberger les deux joueurs en meme temps. */
.deux-colonnes { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.colonne-titre { font-weight: 700; margin-bottom: .4rem; color: var(--jaune); }

.etapes { margin: .3rem 0 0; padding-left: 1.2rem; color: var(--texte-doux); font-size: .9rem; }
.etapes li { margin-bottom: .35rem; }
.etapes code, .aide code {
    background: rgba(0, 0, 0, .35); padding: .1rem .35rem; border-radius: 4px;
    font-size: .9em; color: var(--jaune);
}

.encart { margin-top: .8rem; padding: .7rem; background: rgba(0, 0, 0, .2); border-radius: 8px; }

.lancement-pret { text-align: center; padding: .5rem 0; }
/*  ⚠️ Même histoire que `.vignette--vide` : la définition qui vivait ici ne
    servait qu'à `guide.html`, page supprimée le 29/08. Vérifié avant retrait :
    decor.css déclare `width`, `padding`, `font-size` ET `margin-top` — donc les
    deux propriétés d'ici étaient déjà écrasées sur index.html. */

/*  Fiche de scheme, lue dans le vrai fichier .wsc du jeu. Le jeu lui-meme
 *  n'affiche jamais ces valeurs sans ouvrir son editeur. */
.carte-scheme {
    margin-top: 1.1rem; padding: .9rem;
    background: rgba(0, 0, 0, .2); border-radius: 10px;
    border-left: 3px solid rgba(255, 210, 63, .5);
}
.reglages {
    display: grid; gap: .3rem .9rem; margin-top: .7rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.reglage {
    display: flex; justify-content: space-between; gap: .5rem;
    font-size: .85rem; color: var(--texte-doux);
    padding: .15rem 0; border-bottom: 1px dotted rgba(255, 255, 255, .08);
}
.reglage strong { color: var(--texte); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ chat */

.chat {
    height: 220px; overflow-y: auto;
    display: flex; flex-direction: column; gap: .35rem;
    padding: .6rem; background: rgba(0, 0, 0, .22); border-radius: 8px;
    font-size: .92rem;
}
.msg { padding: .3rem .5rem; border-radius: 6px; background: rgba(255, 255, 255, .05); }
.msg--moi { background: rgba(255, 210, 63, .12); }
.msg-qui { font-weight: 700; color: var(--jaune); margin-right: .3rem; }
.msg--systeme { background: none; color: var(--texte-doux); font-style: italic; text-align: center; }

/* ---------------------------------------------------------------- heatmap */

.heatmap { display: grid; gap: 3px; grid-template-columns: 2.5rem repeat(24, 1fr); font-size: .65rem; }
.hm-etiquette { color: var(--texte-doux); display: flex; align-items: center; }
.hm-heure { color: var(--texte-doux); text-align: center; }

/*  🚨 « Jamais mesure » et « mesure a zero » doivent rester distinguables d'un
 *  coup d'oeil : c'est la meme regle que le bandeau d'etat. Une case en
 *  pointilles dit « on ne sait pas », une case pleine sombre dit « on a
 *  regarde, il n'y avait personne ». */
/*  ⚠️ Les deux tons neutres ont été remontés (.13 → .24 / .22). La heatmap est
    l'un des rares blocs POSÉS À NU sur le décor : à 13 % de blanc, le pointillé
    « jamais mesuré » disparaissait complètement dès qu'il tombait sur le
    terrain, et la distinction ci-dessous ne voulait plus rien dire. */
.hm-case {
    aspect-ratio: 1; border-radius: 2px;
    background: transparent; border: 1px dashed rgba(255, 255, 255, .24);
}
.hm-case[data-n]     { border: 1px solid transparent; }
.hm-case[data-n="0"] { background: rgba(255, 255, 255, .22); }
.hm-case[data-n="1"] { background: rgba(255, 210, 63, .30); }
.hm-case[data-n="2"] { background: rgba(255, 210, 63, .55); }
.hm-case[data-n="3"] { background: rgba(255, 210, 63, .80); }
.hm-case[data-n="4"] { background: var(--jaune); }

/* ------------------------------------------------------------------- pied */

.pied {
    max-width: 900px; margin: 0 auto; padding: 1.5rem 1rem 3rem;
    border-top: 1px solid var(--bord);
    color: var(--texte-doux); font-size: .78rem; text-align: center;
}

/* ------------------------------------------------------------- guide */

.lien { color: var(--jaune); text-decoration: none; border-bottom: 1px solid rgba(255,210,63,.4); }
.lien:hover { border-bottom-color: var(--jaune); }
.annee { color: var(--texte-doux); font-weight: 400; font-size: .8rem; }

/* ==========================================================================
   ONGLETS
   ==========================================================================
   🚨 L'écran principal ne sert qu'à trouver une partie. Le reste — la partie en
   cours, le radar WormNET, la grille horaire — vit derrière un onglet. */

.onglets {
    display: flex; gap: .3rem; flex-wrap: wrap;
    max-width: 900px; margin: 0 auto; padding: .2rem 1rem 0;
    border-bottom: 1px solid var(--bord);
}
.onglet {
    appearance: none; cursor: pointer;
    padding: .6rem 1rem; font: inherit; font-weight: 700; font-size: .9rem;
    color: var(--texte-doux);
    background: transparent; border: 0;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.onglet:hover { color: var(--texte); }
.onglet--actif { color: var(--jaune); border-bottom-color: var(--jaune); }

/* ==========================================================================
   LA GRILLE DE JEUX
   ==========================================================================
   🔑 On en coche PLUSIEURS. Chaque vignette porte la couleur du jeu — c'est ce
   qui permet de les distinguer d'un coup d'œil sans logo officiel.
   ⚠️ La vignette est un <button type="button"> : dans un <form>, un bouton sans
   type déclenche l'envoi, et cocher un jeu lancerait la recherche. */

.jeux {
    display: grid; gap: 6px; margin: .4rem 0 .2rem;
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
}
.jeu-case {
    position: relative; cursor: pointer; overflow: hidden;
    display: flex; flex-direction: column; align-items: flex-start; gap: .1rem;
    padding: .55rem .5rem .5rem .75rem;
    font: inherit; text-align: left;
    color: var(--texte-doux);
    background: rgba(0, 0, 0, .28);
    border: 1px solid var(--bord); border-radius: 7px;
}
.jeu-barre {
    position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
    background: var(--teinte);
}
/*  La vignette ne porte QUE le logo : pas de nom, pas d annee. Le nom vit dans
 *  le title et l aria-label — voir file.js.
 *  🚨 PAS de image-rendering: pixelated ici. Ce sont les vraies jaquettes des
 *  boutiques (640x360, transparentes, lissees) : les rendre au plus proche
 *  voisin les creneles. Le pixel art, c est le decor, pas les logos. */
.jeu-logo {
    display: block; width: 100%; height: 60px;
    object-fit: contain; object-position: center;
    /*  Les emblemes generes sont deja a la couleur du jeu ; le vrai logo de
        W:A est une image en couleurs. Une legere desaturation au repos evite
        que la grille ressemble a un sapin de Noel. */
    filter: saturate(.75) brightness(.9);
}
.jeu-case--choisi .jeu-logo { filter: none; }
.jeu-case:hover { background: rgba(255, 255, 255, .07); color: var(--texte); }
.jeu-case--choisi {
    color: var(--texte);
    border-color: var(--teinte);
    background: color-mix(in srgb, var(--teinte) 22%, transparent);
}
/*  La coche : elle dit « sélectionné » sans dépendre de la couleur seule. */
.jeu-case--choisi::after {
    content: '✓'; position: absolute; right: .45rem; top: .35rem;
    /*  700 : plafond Tahoma (voir .marque). */
    font-size: .75rem; font-weight: 700; color: var(--teinte);
}

.grille-form--deux { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.aide--enligne { display: inline; margin-left: .4rem; font-weight: 400; text-transform: none; letter-spacing: 0; }

/*  L'interrupteur de son : discret, mais toujours là. Un site qui fait du bruit
 *  sans bouton pour se taire est un site qu'on ferme. */
.bouton--icone {
    padding: .3rem .5rem; min-width: 2rem; line-height: 1;
    font-size: .95rem;
}

/*  🚨 UN RECTANGLE DANS UNE PILULE, ÇA SE VOIT.
 *
 *  `.moi-carte` est en `border-radius: 999px` — donc ses extrémités sont des
 *  demi-cercles de la hauteur de la barre. `.bouton`, lui, est en `8px`. Posé
 *  contre le bord droit avec les `.35rem` de marge de la carte, le coin carré
 *  du bouton arrivait exactement là où la pilule s'incurve : il avait l'air
 *  rogné, alors que rien ne le rognait — c'est la courbe du parent qui s'en
 *  éloignait.
 *
 *  🔑 On aligne la GÉOMÉTRIE plutôt que d'ajouter de la marge : une pilule
 *  dans une pilule s'emboîte à toutes les tailles, alors qu'une marge calculée
 *  redevient fausse dès que la hauteur de la barre change (police plus grande,
 *  zoom, écran dense). */
/*  ⚠️ CETTE RÈGLE A ÉTÉ RETIRÉE, ET SON HISTOIRE VAUT D'ÊTRE GARDÉE.
 *  Elle arrondissait les boutons en pilule POUR ÉPOUSER un conteneur qui en
 *  était une. Depuis, l'habillage est passé aux angles vifs du jeu : le
 *  conteneur est carré, donc une pilule dedans recrée exactement le défaut
 *  qu'elle corrigeait, en miroir. Les boutons suivent maintenant la règle
 *  commune « angles vifs » plus bas, et le problème ne peut plus revenir —
 *  parent et enfant partagent la même géométrie par construction.
 *  🔑 La leçon : un correctif qui compense la forme d'un voisin devient faux
 *  le jour où le voisin change. Aligner les géométries vaut mieux que
 *  compenser l'écart. */

/* ==========================================================================
   MOBILE
   ==========================================================================
   🚨 Il n'y avait AUCUNE media query. Ça tenait à peu près par chance — grilles
   en `auto-fit`, en-tête en `flex-wrap` — mais trois choses cassaient vraiment
   sur un téléphone, et elles sont traitées ici. */

/*  Les deux grilles horaires font 24 colonnes. Sur 400 px, chaque case tombe à
    treize pixels : illisible, et impossible à viser au doigt. On leur donne une
    largeur plancher et on laisse glisser horizontalement.
    ⚠️ `overflow-x` doit être porté par un PARENT : une grille ne peut pas
    défiler elle-même. */
.grille-defilante {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    /*  De quoi comprendre qu'il y a quelque chose à droite. */
    scrollbar-width: thin;
}
.grille-defilante > * { min-width: 34rem; }

@media (max-width: 640px) {
    /*  Les onglets deviennent une barre défilante plutôt que de passer à la
        ligne : quatre onglets sur deux lignes mangent le haut de l'écran. */
    .onglets {
        flex-wrap: nowrap; overflow-x: auto;
        scrollbar-width: none;
    }
    .onglets::-webkit-scrollbar { display: none; }
    .onglet { flex: 0 0 auto; padding: .6rem .7rem; font-size: .85rem; }

    /*  L'en-tête : la marque au-dessus, l'identité en dessous.
     *  🚨 `body` en préfixe pour la même raison que le bloc décor plus bas :
     *  decor.css redéfinit `.entete--app` (padding ET gap) à spécificité égale
     *  et se charge après, donc ce resserrement mobile ne s'appliquait pas. */
    body .entete--app { padding: 1rem .8rem .4rem; gap: .5rem; }
    /*  ⚠️ L'accroche reste visible ici, mais rétrécie — et c'est un arbitrage
        qui tient à sa LONGUEUR, pas à un principe. Le haut d'un téléphone est
        déjà pris par la marque, la carte d'identité et les onglets : une
        accroche qui passe à deux lignes repousse le formulaire sous la pliure.
        Celle-ci fait 34 caractères et tient sur une ligne à cette taille.
        🚨 Si le texte s'allonge, vérifier ce point avant de le valider.
        (Pas de préfixe : `.baseline--app` n'existe pas dans decor.css, donc
        aucun risque de cascade ici.) */
    .baseline--app { font-size: .75rem; margin-top: .1rem; }

    /*  🚨 PIÈGE DE CASCADE — index.html charge style.css PUIS decor.css.
     *
     *  À spécificité égale, c'est le dernier chargé qui gagne, et une media
     *  query n'ajoute AUCUNE spécificité. Il y avait donc ici :
     *
     *      .marque--petite { font-size: 1.3rem; }      (0,1,0)
     *
     *  ...battu en silence par `.marque--petite { font-size: clamp(1.2rem,
     *  4vw, 1.8rem) }` de decor.css, de MÊME spécificité (0,1,0) mais chargé
     *  après. Cette ligne n'a donc jamais rien fait, depuis le jour où elle a
     *  été écrite. Elle est supprimée plutôt que ressuscitée, et c'est
     *  volontaire : le `clamp()` de decor.css fait déjà le travail, et mieux.
     *  Sur un téléphone de 375 px, 4vw = 15 px, donc le clamp plafonne au
     *  minimum, soit 1,2 rem — PLUS PETIT que le 1,3 rem qu'on essayait
     *  d'imposer ici, et sans marche d'escalier à 640 px. Forcer 1,3 rem
     *  aurait agrandi la marque sur mobile, pas l'inverse.
     *
     *  ⚠️ Les autres règles du même bloc restent, elles, bien vivantes : leurs
     *  sélecteurs (.onglet, .marque-mascotte--petite, .bloc, .jeux…) ne sont
     *  pas redéfinis par decor.css. Le seul critère, c'est « decor.css
     *  touche-t-il au même sélecteur ET à la même propriété ». */
    /*  La mascotte rétrécit avec le nom : à 38 px elle prend un cinquième de la
        largeur d'un téléphone, et l'en-tête — qui passe déjà à la ligne ici —
        se met à en occuper deux. */
    .marque-mascotte--petite { height: 28px; margin-right: .4rem; }

    .bloc { padding: 0 .8rem; }
    .carte { padding: .8rem; }

    /*  Trois vignettes de jeu par ligne, pas plus : en dessous le logo n'est
        plus reconnaissable. */
    .jeux { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 5px; }
    .jeu-logo { height: 46px; }

    /*  Le bouton de recherche passe pleine largeur sous le champ de note :
        côte à côte, les deux deviennent trop étroits pour être utilisables. */
    .rangee { flex-wrap: wrap; }
    .rangee > .champ { flex: 1 1 100%; }
    .rangee > .bouton { flex: 1 1 auto; }
}

/*  Le décor mange trop d'écran sur un téléphone : terrain et eau font ensemble
    390 px, soit près de la moitié d'un écran de 860. On les réduit, et on
    remonte les marges de pied de page en conséquence. */
@media (max-width: 640px), (max-height: 620px) {
    /*  🚨 LE MÊME PIÈGE DE CASCADE QUE `.marque--petite` ET `.chrono-*`, mais
     *  cette fois sur TOUT le décor : index.html charge style.css PUIS
     *  decor.css, et une media query n'ajoute AUCUNE spécificité.
     *
     *  Ces douze règles s'écrivaient donc `.terre`, `.eau`, `.vague--1`… en
     *  (0,1,0), et `#accueil` en (1,0,0) — chacune battue en silence par sa
     *  jumelle de decor.css, de MÊME spécificité mais chargée après. Résultat
     *  mesuré : la mise en page mobile du décor n'a JAMAIS été appliquée. Sur
     *  téléphone, le terrain gardait ses 240 px et l'eau ses 150 px, soit près
     *  de la moitié de l'écran mangée par du décor.
     *
     *  🔑 On corrige par la SPÉCIFICITÉ (`#decor` / `body` en préfixe) plutôt
     *  qu'en déplaçant le bloc dans decor.css : ces réglages se lisent
     *  ensemble avec le reste des adaptations mobiles, et une correction par
     *  spécificité survit à une réorganisation future des <link>, là où un
     *  déplacement redeviendrait faux le jour où l'ordre change. */
    #decor .terre    { bottom: 44px; height: 150px; }
    #decor .collines { bottom: 158px; height: 70px; }
    #decor .eau      { height: 94px; }
    #decor .vague--1 { top: 8px;  height: 100px; }
    #decor .vague--2 { top: 24px; }
    #decor .vague--3 { top: 40px; }
    #decor .vague--4 { top: 56px; }
    body #accueil  { padding-bottom: 13rem; }
    body .pied     { padding-bottom: 13rem; }

    /*  Même arbitrage pour la mascotte de l'accueil : sur un petit écran, ou en
        paysage, les 13 rem de terrain plus le formulaire ne laissent pas 96 px
        à donner à une image. Ce qui doit rester visible sans défiler, c'est le
        champ pseudo et le bouton. */
    .marque-mascotte--grande { height: 68px; }

    /*  🔑 La jauge de vent fait 192 px de large : sur 400 px d'écran elle et le
        chrono se rejoignent presque. On les rapproche des bords et on rétrécit
        la jauge — c'est du décor, il doit s'effacer.
        🚨 96 N'EST PAS UN CHIFFRE ROND CHOISI AU HASARD : la jauge se remplit
        par crans d'une flèche de 8 px de part et d'autre du milieu, donc la
        largeur doit être un multiple de 16 (ici 6 flèches par côté). Une
        largeur qui ne tombe pas juste fait rogner le dernier cran par
        l'`overflow: hidden` de `.vent-jauge`, en silence. */
    #decor .vent   { right: 8px; bottom: 8px; }
    #decor .vent-jauge { width: 96px; }
    #decor .chrono { left: 8px; bottom: 8px; }
    /*  🚨 MÊME PIÈGE DE CASCADE QUE `.marque--petite` PLUS HAUT, et ici il
     *  fallait sauver la règle, pas la supprimer.
     *
     *  `.chrono-tour` et `.chrono-mort` sont entièrement redéfinis dans
     *  decor.css (30 px / 11 px, mesurés au pixel sur une capture du jeu). À
     *  spécificité égale (0,1,0) et decor.css chargé après, ce rétrécissement
     *  mobile n'était jamais appliqué : le chrono restait en 30 px sur un
     *  téléphone. Contrairement à `.marque--petite`, rien d'autre ne fait le
     *  travail — decor.css pose une taille FIXE, pas un `clamp()`.
     *
     *  🔑 D'où le passage par `.chrono .chrono-tour` : (0,2,0) contre (0,1,0),
     *  ça gagne quel que soit l'ordre des <link>. On corrige par la
     *  spécificité plutôt qu'en déplaçant le bloc dans decor.css, pour deux
     *  raisons : les réglages mobiles voisins (.vent, .vent-jauge) vivent ici
     *  et doivent se lire ensemble, et une correction par spécificité survit à
     *  une réorganisation future des feuilles, là où un simple déplacement
     *  redeviendrait faux le jour où l'ordre change.
     *  ⚠️ Le HTML garantit l'imbrication : <div class="chrono"> contient les
     *  deux compteurs (index.html).
     *
     *  `min-width` fait partie du lot et n'est pas un détail de mise en page :
     *  decor.css impose l'invariant « le cube est PLUS LARGE que la plaque »,
     *  et cette largeur est calée sur la taille des chiffres. On réduit la
     *  police, on doit réduire le plancher avec — 42 px pour 22 px de chiffres
     *  garde le cube plus large que la plaque (9 px). */
    .chrono .chrono-tour { min-width: 42px; font-size: 22px; line-height: 26px; }
    .chrono .chrono-mort { font-size: 9px; line-height: 12px; }
}

/*  Le jeu concerné, en petit à côté du nom du serveur : « WormNET » tout seul
 *  ne dit pas que ça ne concerne QUE Worms Armageddon. */
.onglet-jeu {
    font-weight: 400; font-size: .78em; opacity: .6;
    text-transform: none; letter-spacing: 0;
}
.onglet--actif .onglet-jeu { opacity: .8; }

/*  La liste explicative de l'onglet vide : alignée à gauche, contrairement au
 *  reste du bloc « vide » qui est centré. Une procédure centrée est illisible. */
.etapes--vide {
    text-align: left; max-width: 34rem; margin: .9rem auto 0;
}
.etapes--vide ul {
    margin: .35rem 0 0; padding-left: 1.1rem;
    list-style: '— '; opacity: .9;
}
.etapes--vide ul li { margin-bottom: .2rem; }

/* ------------------------------------------------- participants d'une room */

/*  🔑 UNE LISTE DE PERSONNES, PAS UNE RANGÉE DE PASTILLES. C'était un
 *  `flex-wrap` de pilules, posé six lignes sous les pastilles des faits de la
 *  salle (plateforme, mode, format) : même forme, même rayon, même gris — deux
 *  joueurs se lisaient comme deux étiquettes de plus. Une ligne par personne,
 *  pleine largeur, et l'œil compte les gens au lieu de lire des mots. */
.joueurs { list-style: none; margin: .7rem 0 0; padding: 0; display: grid; gap: .3rem; }
.joueur {
    display: flex; align-items: center; gap: .5rem;
    padding: .25rem .7rem .25rem .4rem;
    background: rgba(0, 0, 0, .28);
    border: 1px solid var(--bord); border-radius: 10px;
    font-size: .9rem;
}
.joueur--moi { border-color: rgba(255, 210, 63, .45); }
/*  Le ver de Worms en tête de ligne : c'est lui qui dit « ceci est un joueur »
 *  sans un mot d'anglais de plus.
 *  🚨 19x25, SA TAILLE NATIVE, et on n'y touche pas. C'est l'icône du panneau
 *  d'armes du jeu, dessinée par Team17 POUR être lue dans une case d'icône ;
 *  `outils/extraire-ver-icone.js` la livre déjà recadrée. Toute autre valeur ici
 *  rééchantillonne un pixel art de 1999 — mesuré en le réduisant et en le
 *  REGARDANT : sous 16 px les deux yeux fusionnent en une barre blanche.
 *  `pixelated` ne sert donc à rien à 100 %, il sert à 200 % : sur un écran
 *  HiDPI le navigateur double l'image, et on veut un doublement franc. */
.joueur-ver { flex: none; width: 19px; height: 25px; image-rendering: pixelated; }
.joueur-pseudo { font-weight: 700; }
/*  `you` et `here`/`away` filent à droite : sur une ligne pleine largeur, la
 *  colonne d'états se lit d'un seul balayage vertical. */
.joueur-note { margin-left: auto; color: var(--texte-doux); font-size: .75rem; }
/*  « away » n'est pas une faute : c'est juste une information. Rouge, ça
 *  ressemblerait à un reproche. */
.joueur-etat { margin-left: auto; color: var(--texte-doux); font-size: .75rem; }
.joueur-etat--ici { color: var(--vert); }

/*  La charge brute d'une partie WormNET 2, le jour où il y en aura une :
 *  personne ne sait encore la décoder, donc on la montre telle quelle. */
.brut {
    margin: .6rem 0 0; padding: .6rem;
    background: rgba(0, 0, 0, .35); border: 1px solid var(--bord); border-radius: 6px;
    font-family: 'Consolas', monospace; font-size: .72rem;
    text-align: left; word-break: break-all; white-space: pre-wrap;
    color: var(--texte-doux);
}

/* ==========================================================================
   LES PLATEFORMES
   ==========================================================================
   🚨 Ce n'est pas un filtre d'affichage : deux joueurs sur des plateformes
   différentes ne peuvent pas se rejoindre. D'où un choix unique, obligatoire,
   et restreint aux plateformes réellement possibles pour les jeux cochés.

   ⚠️ Glyphes dessinés, pas les logos officiels : PlayStation, Xbox et Nintendo
   sont des marques déposées. */

.plateformes { display: flex; flex-wrap: wrap; gap: 6px; margin: .4rem 0 .2rem; }
.plateforme {
    display: flex; align-items: center; gap: .45rem; cursor: pointer;
    padding: .45rem .7rem; font: inherit; font-size: .85rem; font-weight: 700;
    color: var(--texte-doux);
    background: rgba(0, 0, 0, .28);
    border: 1px solid var(--bord); border-radius: 7px;
}
.plateforme svg {
    width: 18px; height: 18px; flex: none;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}
/*  Les touches PlayStation sont des formes pleines, pas des traits. */
.plateforme[data-plateforme="psn"] svg path:first-of-type,
.plateforme[data-plateforme="psn"] svg path:nth-of-type(2) { fill: currentColor; }
.plateforme:hover { color: var(--texte); background: rgba(255, 255, 255, .07); }
/*  🚨 `color: var(--fond)` — LA VARIABLE N'EXISTE PAS. Un `var()` qui ne
 *  résout rien rend la déclaration invalide à l'exécution : `color` retombe
 *  alors sur `inherit`, donc sur --texte. On avait du blanc cassé sur le jaune
 *  des caisses, soit 1,3:1 — la plateforme choisie était le texte le MOINS
 *  lisible de tout le site, et pour une fois le décor n'y était pour rien.
 *  Même brun très sombre que .bouton--principal, qui a le même fond jaune. */
.plateforme--choisie {
    color: #241a00; background: var(--jaune); border-color: var(--jaune);
}

/* ------------------------------------------------------------------- TUS */

/*  🚨 Ni rang, ni score, ni classement : TUS tient le sien, on trouve
 *  l'adversaire. Ce style n'habille qu'un rappel de règles. */
.encart--tus {
    margin-top: .9rem; padding: .7rem .8rem;
    background: rgba(255, 210, 63, .07);
    border: 1px solid rgba(255, 210, 63, .3); border-radius: 8px;
}
.tag-choix--gros { font-size: .92rem; }
.carte--tus { border-left: 3px solid var(--jaune); }
.carte--tus .ligne-titre { color: var(--jaune); margin-bottom: .3rem; }

/*  Une réserve sur la santé du multi en ligne d'un jeu. Ambre, pas rouge : ce
 *  n'est pas une erreur, c'est une information qu'il vaut mieux avoir avant
 *  d'attendre vingt minutes. */
.aide--reserve {
    color: #e8c46a;
    border-left: 2px solid rgba(232, 196, 106, .45);
    padding-left: .6rem; margin-top: .4rem;
}

/*  Une case sans vignette : on garde la couleur du jeu plutôt qu'une icône
 *  cassée. Le nom reste dans le title et l'aria-label. */
.jeu-logo--vide { display: block; width: 100%; height: 60px; }


/* ==========================================================================
   L'HABILLAGE WORMS ARMAGEDDON
   ==========================================================================

   🔑 POURQUOI CETTE SECTION EXISTE, ET CE QU'ELLE NE FAIT PAS.
   La demande était « reprends le langage visuel du jeu, comme le fait le
   snooper de la communauté, pour que les gens ne soient pas perdus ». Ce qui
   suit ne change AUCUNE structure et ne déplace rien : ce sont les mêmes
   blocs, les mêmes listes et les mêmes boutons, rhabillés. Tout est en fin de
   feuille et de même spécificité que les règles d'origine — donc lisible en
   revue, et retirable d'un bloc.

   CE QU'ON REPREND DU JEU (relevé sur https://snoop.wormnet.net/snooper/, qui
   le reprend lui-même, comme Great Snooper et WebSnoop avant lui) :
     · un cadre PLAT de 1 px, gris clair, jamais un biseau 3D — à l'échelle
       native le snooper n'a aucun relief, l'impression de biseau vient de
       l'agrandissement des captures ;
     · des angles VIFS partout : W:A n'arrondit rien ;
     · un label JAUNE GRAS au-dessus de chaque panneau (« Games », « Players »,
       « Messages ») ;
     · un intérieur bleu nuit, plus sombre que la page.

   CE QU'ON NE REPREND PAS, ET POURQUOI :
     · le #808080 de leurs bordures. Mesuré chez nous : 3,36:1 sur une carte et
       2,68:1 sans lavis — sous le seuil de 3:1 des éléments non textuels. Leurs
       panneaux sont OPAQUES, les nôtres laissent passer le décor animé : la
       même couleur n'y fait pas le même travail. D'où --wa-cadre, éclairci.
     · leur #BF0000 et leur #006040 (les lignes du journal de chat) : 2,03:1 et
       1,74:1 sur une de nos cartes. Ils appartiennent de toute façon à un
       panneau de chat qu'on ne reprend pas — ce client ne parle pas.
     · leur bande de rang à côté de chaque pseudo. Le projet n'a aucune notion
       de classement, à aucun endroit.

   🚨 TOUTES LES VALEURS CI-DESSOUS SONT MESURÉES, PAS ESTIMÉES. Méthode :
   composition réelle des couches dans l'ordre où le navigateur les peint —
   pixel le plus clair du terrain → #voile-lisibilite (rgba(6,18,38,.62)) →
   lavis ou carte → texte ; luminance WCAG 2 ensuite. Mesuré sur LES SIX thèmes
   de terrain, pas seulement celui affiché au moment du test. Le pire est
   toujours `desert-classique` (pixel le plus clair #F8D272), qui est aussi le
   thème de repli quand JavaScript ne tourne pas.
   ⚠️ Le `backdrop-filter: blur(9px) saturate(.6)` de decor.css est IGNORÉ dans
   ces mesures : il désature le fond, il ne peut donc qu'améliorer le résultat.
   On mesure sans lui, c'est le pire cas.
*/

:root {
    /*  Le lavis d'un panneau de section. 🔑 .22 N'EST PAS UN CHOIX
        ESTHÉTIQUE : c'est la valeur minimale qui porte --texte-doux
        au-dessus du plancher du projet. Mesuré : 4,14:1 sans lavis (le texte
        secondaire posé sur le voile seul, qui est le pire point de toute
        l'interface aujourd'hui) → 5,09:1 avec. Au-delà on gagne peu et on
        commence à effacer le décor, qui doit rester vivant derrière. */
    --wa-lavis:  rgba(15, 34, 60, .22);

    /*  Le filet de 1 px. Éclairci par rapport au #808080 du jeu pour la raison
        mesurée en tête de section : 3,29:1 sur un bloc, 5,24:1 sur une carte.
        🚨 IL NE SE DESSINE JAMAIS SANS SON LAVIS — sur le voile nu il tombe à
        2,68:1. Les deux vont ensemble, toujours. */
    --wa-cadre:  #9aa4ae;

    /*  Le jaune des labels. 🚨 C'EST LE JAUNE PUR DU JEU (#ffff00), ET PAS
        --jaune (#ffd23f, celui des caisses), parce que la mesure les sépare :
        sur le voile seul, là où vivent les titres de section, --jaune ne fait
        que 4,70:1 — sous le plancher — quand #ffff00 tient 6,32:1. Le jaune du
        jeu est ici à la fois le plus fidèle ET le plus lisible. --jaune reste
        ce qu'il était partout ailleurs. */
    --wa-label:  #ffff00;

    /*  La bande d'en-tête d'une liste (le « Game | Host » du snooper), posée
        SUR une carte. --texte-doux y tient 6,52:1. */
    --wa-entete: rgba(255, 255, 255, .07);

    /*  Le canal courant dans la barre : fond rouge sombre, texte jaune —
        exactement l'inversion que fait le jeu pour marquer la sélection.
        Mesuré 8,56:1 pour --wa-label dessus. */
    --wa-actif:  rgba(140, 31, 20, .92);
}

/*  Le panneau de section : un cadre plat, un lavis, des angles vifs.
    ⚠️ Le `margin-bottom` d'origine est conservé tel quel. */
.bloc {
    padding: .55rem .9rem .9rem;
    background: var(--wa-lavis);
    border: 1px solid var(--wa-cadre);
    border-radius: 0;
}

/*  🚨 UN PANNEAU QUI N'ENVELOPPE QUE DES CARTES NE REDESSINE PAS LEUR CADRE.
 *
 *  📏 Signalé à l'écran : « Find a game » montrait DEUX filets concentriques,
 *  l'un collé à l'autre — le cadre du panneau, puis celui de la carte du
 *  formulaire, tous deux pleine largeur. Deux traits à trois pixels d'écart ne
 *  se lisent pas comme une hiérarchie, ils se lisent comme du bruit.
 *
 *  🔑 CE QUI FAIT UN PANNEAU, C'EST SON LABEL. La règle du dessus décrit le
 *  panneau du snooper W:A : un cadre, un lavis, et un titre jaune souligné d'un
 *  filet. Sans titre, il ne reste que la boîte — et une boîte autour d'une boîte
 *  n'apporte rien. Les sections QUI ONT un `.titre-section` gardent donc leur
 *  cadre : c'est là qu'il veut dire « ceci est un panneau nommé ».
 *
 *  ⚠️ La condition porte sur un enfant DIRECT (`> .carte`). Une carte enfouie
 *  plus bas — celles des listes de salles, par exemple — ne doit pas décadrer sa
 *  section : elle vit DANS le panneau, elle ne le remplace pas.
 *  ⚠️ `margin-bottom` est conservé : c'est lui qui sépare les sections entre
 *  elles, et il n'a rien à voir avec le cadre.
 */
.bloc:not(:has(> .titre-section)):has(> .carte) {
    padding: 0;
    background: none;
    border: 0;
}

/*  Le label du panneau. Jaune, gras, casse normale — le snooper écrit
    « Games », pas « GAMES », et le jeu non plus.
    🔑 Le filet sous le titre est ce qui fait lire « en-tête de panneau »
    plutôt que « titre flottant » : c'est la ligne grise que le snooper pose
    entre son label et le contenu. */
.titre-section {
    color: var(--wa-label);
    font-size: .95rem;
    text-transform: none;
    letter-spacing: .01em;
    padding-bottom: .35rem;
    margin: 0 0 .7rem;
    border-bottom: 1px solid var(--wa-cadre);
}

/*  Angles vifs et filet unique sur tout ce qui est un contenant. On ne touche
    NI aux pastilles d'état (rondes : ce sont des voyants, le jeu n'en a pas
    d'équivalent) NI aux vignettes de drapeau. */
.carte, .ligne, .bandeau, .vide, .champ, .bouton, .etiquette, .tag-choix,
.carte-scheme {
    border-radius: 0;
}
/*  🚨 `.moi-carte` EST SORTIE DU GROUPE, ET IL FAUT QU'ELLE LE RESTE.
 *
 *  Elle est le seul sélecteur de cette liste que `decor.css` redéfinit
 *  (`border-radius: 999px`, ligne ~540). À spécificité égale (0,1,0) et
 *  decor.css chargé APRÈS style.css, la valeur ci-dessus était battue en
 *  silence : tout le site passait aux angles vifs du jeu sauf la barre
 *  d'identité, qui restait une pilule — sans qu'aucune erreur ne le dise.
 *
 *  🔑 `body` en préfixe (0,1,1) plutôt qu'un déplacement du bloc : c'est la
 *  correction qui survit à une réorganisation des <link>, et c'est la même
 *  qu'on a déjà appliquée quatre fois dans cette feuille. Si un cinquième cas
 *  apparaît, commencer par lancer l'audit de cascade plutôt que par l'œil. */
body .moi-carte { border-radius: 0; }
.carte, .ligne, .bandeau, .vide, .champ, .bouton {
    border-color: var(--wa-cadre);
}

/*  Les onglets : le courant en jaune du jeu, et un filet sous la barre entière
    comme le cadre qui entoure la rangée de canaux du snooper. */
.onglet--actif { color: var(--wa-label); border-bottom-color: var(--wa-label); }

/*  🚨 LE BOUTON PRINCIPAL GARDE SON JAUNE À LUI (--jaune, #ffd23f) ET SON
    RELIEF. Ce n'est pas une incohérence : c'est le seul élément de la page qui
    porte du texte SOMBRE sur fond clair, donc le seul où le jaune pur ferait
    un aplat criard, et c'est aussi le seul qui doit se distinguer du reste au
    premier coup d'œil. On lui ôte l'arrondi, on lui laisse le reste. */
.bouton--principal { border-radius: 0; }
