
@font-face {
  font-family:'Inter'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('/static/fonts/inter-latin-400-700.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Inter'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('/static/fonts/inter-latin-ext-400-700.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family:'Rajdhani'; font-style:normal; font-weight:600; font-display:swap;
  src:url('/static/fonts/rajdhani-latin-600.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Rajdhani'; font-style:normal; font-weight:600; font-display:swap;
  src:url('/static/fonts/rajdhani-latin-ext-600.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family:'Rajdhani'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/static/fonts/rajdhani-latin-700.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Rajdhani'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/static/fonts/rajdhani-latin-ext-700.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

:root {
  color-scheme: dark;

  /* --- fonds et matières --- */
  --bg:#07080B;
  --surface:#11141A;
  --surface-2:#171B22;
  /* Le survol d'une ligne de tableau : plus clair que --surface-2, jamais rouge. */
  --surface-3:#1D222B;

  /* --- rouge NRV ---
     Il ne borde PAS chaque composant : appels à l'action, information
     importante, état actif, priorité, progression. Partout ailleurs, --line. */
  --red:#D20A2E;
  --red-dark:#750719;
  /* Le rouge de la palette est fait pour être un FOND. En petit texte sur le
     fond sombre il plafonne à 3,6:1 — sous le minimum lisible (4,5:1) : les
     surtitres étaient à la limite du déchiffrable, constaté à l'écran. Cette
     variante n'est QUE pour du texte rouge ; les aplats gardent --red. */
  --red-text:#FF4A63;
  /* Le même rouge en translucide : liserés, halos et fonds d'état actif, sans
     ajouter une teinte de plus à tenir à jour. */
  --red-soft:rgba(210,10,46,.14);
  --red-line:rgba(210,10,46,.38);

  /* --- doré : distinctions et progression, rien d'autre --- */
  --gold:#C9A227;
  --gold-soft:rgba(201,162,39,.14);

  /* --- texte --- */
  --text:#F1F2F4;
  --muted:#9299A5;
  /* Le troisième niveau : mentions légales, unités, méta. Sous --muted. */
  --faint:#6B7280;

  /* --- traits --- */
  --line:#232833;
  --line-strong:#2E3542;

  /* --- signaux (hors identité : ils doivent rester lisibles comme signaux) --- */
  --ok:#2FA36B;
  --warn:#D98A2B;

  /* --- typographie --- */
  --font-title:'Rajdhani','Barlow Condensed',system-ui,sans-serif;
  --font-body:'Inter',system-ui,'Segoe UI',Arial,sans-serif;
  /* 38px sur un 360, 76px à partir de ~1400 : les bornes du cahier des charges. */
  /* ⚠️⚠️ **Tout a été réduit d'un cran le 2026-07-28** (Kevin : « je trouve
     l'ensemble gros sur le site en zoom 100 % »). Le texte courant plafonnait à
     **18 px** et les titres à 76 — des valeurs de son cahier des charges
     d'origine, qu'il révise en regardant le résultat. Le texte descend à
     **16 px**, les titres d'environ 15 %.
     ⚠️ **Le plancher ne bouge pas** : rien ne passe sous 12 px, et
     `test_lisibilite` le vérifie sur toute la feuille. Réduire l'échelle
     générale ne doit pas grignoter la lisibilité par le bas. */
  --h1:clamp(2.125rem,1rem + 4.9vw,4rem);
  --h2:clamp(1.5rem,1rem + 2vw,2.3rem);
  --h3:clamp(1.05rem,.98rem + .32vw,1.2rem);
  --body:clamp(.9375rem,.92rem + .12vw,1rem);
  --small:.875rem;
  /* Rajdhani est étroite : elle supporte — et demande — d'être resserrée. */
  --track-title:-.01em;
  --track-eyebrow:.28em;

  /* --- rythme --- */
  --wrap:1240px;
  --gutter:clamp(20px,5vw,64px);
  --nav-h:76px;
  --section-y:clamp(48px,5vw,76px);
  --gap:clamp(20px,2.4vw,32px);

  /* --- formes ---
     Le logo est fait d'angles coupés. On les reprend par petites touches
     (boutons, badges, séparateurs) et surtout PAS en transformant tout en
     hexagones : les cartes restent des rectangles arrondis. */
  --r-sm:8px;
  --r:14px;
  --r-lg:18px;
  --notch:10px;
  /* ⚠️ **38 px, et c'est la mesure de TOUT le site** (Kevin, 2026-07-28 : « si
     tous les boutons du site pouvaient être du même style, dimension, car ils
     sont en général tous trop gros »). Le repère est la pastille du QG, qu'il a
     validée : elle tient en 36 px là où le bouton en prenait **52** (48 sur
     mobile), soit une différence d'un tiers sur chaque appel à l'action.
     38 laisse 2 px au-dessus de la barre tactile du site (36 px, verrouillée par
     l'audit) — jamais moins, sous peine de rater la cible au doigt.
     ⚠️ Plus de valeur mobile distincte : un bouton de 38 px se touche aussi bien
     sur un téléphone, et deux tailles à tenir en phase, c'est deux occasions de
     diverger. */
  --btn-h:38px;
  /* Le plancher, pour les boutons enfouis dans une ligne de tableau ou une
     barre d'outils. Il ne descend PAS plus bas. */
  --btn-h-sm:36px;
  /* ⚠️ **Un champ et le bouton qui l'accompagne ont la MÊME hauteur.** Ils
     partagent une ligne (`.signup-form`, `.armory-filters`, la barre de spé) :
     un menu de 44 px à côté d'un bouton de 38 px, c'est un décalage de 6 px que
     l'œil relève même quand il ne sait pas le nommer. Les champs étaient à 44,
     40 et 38 px selon l'écran — trois valeurs pour la même chose. */
  --field-h:var(--btn-h);

  /* --- mouvement ---
     ⚠️⚠️ **Kevin a LEVÉ sa propre interdiction le 2026-07-28** : « je veux de
     l'animation sur le site, des effets visuels, des couleurs qui rappellent le
     logo, que ce soit agressif, attractif ». Son brief du 26/07 interdisait les
     particules permanentes et les animations continues ; il l'a annulé
     explicitement. Ce qui reste vrai, et qui n'est pas du goût :
     **`prefers-reduced-motion` coupe tout** (WCAG 2.3.3, et l'accessibilité
     est dans ses exigences permanentes), et **seuls `transform` et `opacity`
     s'animent** — le reste fait repeindre la page à chaque image.
     --t-slow : les mouvements de fond, ceux qu'on ne doit pas regarder. */
  --t:.45s cubic-bezier(.4,0,.2,1);
  --t-fast:.4s cubic-bezier(.4,0,.2,1);
  /* Les mouvements de fond, ceux qu'on ne doit PAS regarder. 14 s et non 7 :
     à 7 s le balayage se remarquait (retour de Kevin, 2026-07-28). */
  --t-slow:14s cubic-bezier(.4,0,.2,1);
  --lift:-4px;

  /* --- ⚔️ L'ANGLE DU LOGO : le motif dont tout le mouvement dérive ---
     Le sigle NRV est fait d'**entailles obliques**. Une seule valeur les
     gouverne toutes — balayages de survol, ouverture des cartes, traits
     lumineux, coins coupés. C'est ce qui fait qu'un bouton, une carte et un
     séparateur ont l'air de venir du même endroit : ils penchent pareil.
     ⚠️ Ne pas la faire varier par composant. Un site où chaque effet a son
     angle n'a pas de style, il a des effets. */
  --biais:-24deg;
  --biais-t:skewX(-24deg);

  --shadow:0 18px 40px -24px rgba(0,0,0,.9);
}

@media (max-width:820px) {
  :root { --nav-h:64px; --section-y:clamp(36px,7vw,56px); }
}

*,*::before,*::after { box-sizing:border-box; }
html { scroll-behavior:smooth; }
/* Une ancre ne doit pas glisser sous la barre collante. */
:target { scroll-margin-top:calc(var(--nav-h) + 16px); }
/* ⚠️⚠️ **Le fond vit sur `html`, PAS sur `body`** — et ce n'est pas un détail
   de style. Un `body::before` en `z-index:-1` se peint **derrière le fond de
   `body`** : tant que `body` portait un fond opaque, la griffure était bien
   présente et totalement invisible (constaté par Kevin le 2026-07-28, après
   déploiement — je ne l'avais pas vérifiée en rendant). En posant le fond sur
   `html`, le pseudo-élément se glisse **entre** les deux : au-dessus du noir,
   sous le contenu. */
html { background:var(--bg); }
body {
  margin:0; background:transparent; color:var(--text);
  font-family:var(--font-body); font-size:var(--body); line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
img { max-width:100%; }
a { color:inherit; text-decoration:none; }

h1,h2,h3,h4 {
  font-family:var(--font-title); font-weight:700; line-height:1.04;
  letter-spacing:var(--track-title); margin:0;
  /* Rajdhani en capitales : c'est là qu'elle ressemble au logo. */
  text-transform:uppercase;
}
h1 { font-size:var(--h1); }
h2 { font-size:var(--h2); }
h3 { font-size:var(--h3); line-height:1.2; }
/* Le rouge dans un titre souligne UN mot — celui qui porte le sens. */
h1 span.hl, h2 span.hl, h3 span.hl { color:var(--red); }

/* Le surtitre : il situe la section avant qu'on lise le titre. Petit et très
   espacé, donc en --red-text : le rouge d'aplat n'y serait pas lisible. */
/* ⚠️ **700 et non 600, et c'est une optimisation** (2026-07-28). Le surtitre
   était le seul usage de la graisse 600 sur les pages publiques — il faisait
   donc télécharger `rajdhani-latin-600.woff2` (**15,4 Ko**) partout. Comparé à
   l'écran en 600 et en 700 sur des capitales de 13 px très espacées : **aucune
   différence perceptible**. La graisse 600 reste chargée, mais seulement là où
   elle se voit vraiment — le « Bonjour X » du QG (`.qg-hello`), où la nuance
   avec le nom en gras, elle, se lit nettement. */
.eyebrow {
  font-family:var(--font-title); font-weight:700; font-size:.82rem;
  text-transform:uppercase; letter-spacing:var(--track-eyebrow);
  color:var(--red-text); margin:0 0 12px;
}
.lead { color:#C9CDD5; font-size:clamp(1.02rem,.98rem + .35vw,1.22rem); }
.muted { color:var(--muted); }
.faint { color:var(--faint); }
.num { font-variant-numeric:tabular-nums; }

/* --- Accessibilité : le focus se voit ---
   Relevé à l'audit : au clavier, rien n'indiquait où on était. Le rouge sert
   ici parce que c'est exactement son emploi — un état actif. */
:focus-visible {
  outline:2px solid var(--red); outline-offset:3px; border-radius:2px;
}
/* Le lien d'évitement : premier tabulé de la page, invisible à la souris. */
.skip {
  position:absolute; left:-9999px; top:0; z-index:100;
  background:var(--red); color:#fff; padding:.7rem 1.1rem; font-weight:600;
}
.skip:focus { left:12px; top:12px; }
.sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* --- Boutons : UNE seule famille pour tout le site ---
   ⚠️⚠️ **Refonte du 2026-07-28.** Le bouton pesait 52 px, en Rajdhani capitales
   de 16 px, avec un coin coupé — pendant que le QG servait ses raccourcis en
   pastilles de 36 px, en Inter, en casse normale. Deux familles pour un même
   geste, souvent **dans la même page** : la barre d'actions du QG et le « Voir
   l'événement » juste dessous. Kevin l'a tranché en regardant : « la taille des
   boutons me plaît mieux [sur les pastilles], si tous les boutons du site
   pouvaient être du même style, dimension, car ils sont en général tous trop
   gros. »

   Ce qui change, et pourquoi :
   - **la hauteur** : `--btn-h`, une seule valeur pour le bouton et la pastille ;
   - **la casse** : Rajdhani en capitales est superbe en titre et pénible à lire
     à 14 px sur deux mots ; le libellé reprend la police de texte, en casse
     normale, comme la pastille ;
   - **le coin coupé disparaît** : à 52 px l'entaille de 10 px était un détail,
     à 38 px elle mange le quart de la hauteur et le bouton penche. Les angles du
     logo restent portés par les badges, les séparateurs et le héros — ils ne
     tenaient pas à ce seul biseau.

   ⚠️ **La hiérarchie tient donc à la COULEUR, plus à la taille** — exactement la
   règle des pastilles du QG. Trois niveaux : `primary` (rouge, l'action qu'on
   veut), la base (l'alternative), `ghost` (le geste de service, sans aplat). */
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:.5rem;
  min-height:var(--btn-h); padding:0 18px;
  font-family:var(--font-body); font-weight:600; font-size:.875rem;
  letter-spacing:.01em; line-height:1.2; text-align:center;
  border:1px solid var(--line-strong); background:var(--surface-2);
  color:var(--text); cursor:pointer;
  border-radius:var(--r-sm);
  transition:background var(--t-fast),border-color var(--t-fast),
             transform var(--t-fast),color var(--t-fast);
}
.btn:hover { border-color:var(--red-line); background:#212734;
             transform:translateY(var(--lift)); }
.btn.primary {
  background:linear-gradient(135deg,var(--red),var(--red-dark));
  border-color:var(--red); color:#fff;
}
.btn.primary:hover { filter:brightness(1.1); border-color:var(--red); }
/* 🐞 **`ghost` était utilisé cinq fois dans le QG sans exister dans la
   feuille** : le bouton rendait comme la base, et la nuance annoncée par le nom
   de classe n'existait qu'en intention. Elle existe maintenant — fond
   transparent, trait discret : le geste de service se lit comme tel. */
.btn.ghost { background:transparent; border-color:var(--line); color:var(--muted); }
.btn.ghost:hover { background:var(--surface-2); border-color:var(--red-line);
                   color:var(--text); }
/* ⚠️ Les trois variantes de taille ne changent plus la HAUTEUR — elles
   resserrent. Depuis que la base est à 38 px, un « petit » bouton à 30 px
   repasserait sous la barre tactile, et un site à quatre tailles de bouton est
   précisément ce que cette refonte enlève. Elles restent parce que le HTML les
   porte à ~20 endroits et qu'elles disent quelque chose de vrai : ce bouton-là
   vit dans une ligne serrée. */
.btn.sm, .btn.small, .btn.tiny {
  min-height:var(--btn-h-sm); padding:0 14px; font-size:.82rem;
}

/* --- Cartes --- */
.card {
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r); padding:clamp(18px,2.4vw,26px);
}

/* --- Badges --- */
.badge {
  display:inline-flex; align-items:center; gap:.35rem;
  padding:.2rem .6rem; font-size:.76rem; font-weight:700;
  text-transform:uppercase; letter-spacing:.05em;
  background:var(--surface-2); border:1px solid var(--line); color:var(--muted);
  border-radius:var(--r-sm);
  clip-path:polygon(0 0,calc(100% - 6px) 0,100% 6px,100% 100%,6px 100%,0 calc(100% - 6px));
}
.badge.hot { background:var(--red-soft); border-color:var(--red-line); color:#FF6B84; }
.badge.gold { background:var(--gold-soft); border-color:rgba(201,162,39,.4); color:var(--gold); }

/* --- Contenu plus large que l'écran ---
   Il défile DANS son cadre ; la page, jamais. Sans ça, le tableau 5 colonnes de
   l'armurerie (largeur minimale mesurée à ~443 px) pousse toute la mise en page
   de côté sur un 360 — le point que l'audit signalait comme non vérifié.
   `tabindex` : une zone qui défile doit être atteignable au clavier. */
.scroll-x {
  overflow-x:auto; -webkit-overflow-scrolling:touch;
  scrollbar-width:thin; scrollbar-color:var(--line-strong) transparent;
}
.scroll-x > table { min-width:30rem; }

/* --- Séparateur : un trait qui s'éteint, pas une règle pleine largeur --- */
.sep {
  height:1px; border:0; margin:0;
  background:linear-gradient(90deg,transparent,var(--line-strong) 20%,
             var(--line-strong) 80%,transparent);
}

/* --- Apparition au scroll ---
   L'état de départ n'est posé que si le JS a pris la main (`.js` sur <html>) :
   sans JavaScript, le contenu doit rester visible, pas transparent à jamais. */
.js .reveal { opacity:0; transform:translateY(14px);
              transition:opacity var(--t),transform var(--t); }
.js .reveal.seen { opacity:1; transform:none; }

/* --- États de chargement ---
   Un squelette, pas un sablier : le cadre garde **la taille et la forme** de ce
   qui arrive, donc rien ne saute quand la donnée s'affiche. Un sablier centré
   dit « attends » ; un squelette dit « voilà ce qui vient ». C'est le relevé
   n°10 de l'audit (aucun état de chargement), traité une fois pour toutes les
   pages plutôt que dans le coin d'un composant.
   `--skel-h` laisse chaque écran dimensionner ses barres sans redéfinir la règle. */
.skel {
  display:block; height:var(--skel-h,14px); border-radius:6px;
  background:linear-gradient(90deg,var(--surface-2) 25%,var(--line-strong) 37%,
             var(--surface-2) 63%);
  background-size:400% 100%; animation:skel 1.4s ease infinite;
}
@keyframes skel { from { background-position:100% 50%; }
                  to   { background-position:0 50%; } }

/* ═══════════════════════════════════════════════════════════════════════════
   ⚔️  LE SYSTÈME DE MOUVEMENT — « l'entaille »
   ═══════════════════════════════════════════════════════════════════════════
   Kevin, 2026-07-28 : « je veux de l'animation, des effets visuels, des
   couleurs qui rappellent le logo, que ce soit agressif et attractif ».

   **Le parti pris, en une phrase** : tout le mouvement du site est une
   **entaille** — un trait oblique qui passe. Jamais une pulsation ronde, jamais
   un halo qui respire. Le sigle NRV est fait de lacérations ; le site bouge
   comme lui. Un seul angle (`--biais`) gouverne les balayages de survol,
   l'ouverture des cartes, les traits lumineux et les coins coupés : c'est ce
   qui fait qu'un bouton, une carte et un séparateur ont l'air de venir du même
   endroit.

   ⚠️ **Trois règles techniques, non négociables** :
   1. **`transform` et `opacity` seulement.** Animer une largeur, une couleur ou
      un `box-shadow` fait recalculer et repeindre la page à chaque image ; sur
      un portable, c'est là que le site devient poussif. Ces deux propriétés-là
      sont traitées par la carte graphique.
   2. **Rien qui tourne en boucle au-dessus de 8 s.** Un mouvement lent se
      **remarque** au lieu de se ressentir. Les balayages de fond sont à 7 s et
      à moins de 8 % d'opacité : on les voit du coin de l'œil, jamais en face.
   3. **`prefers-reduced-motion` coupe tout** (bloc en bas de fichier). Ce n'est
      pas du goût, c'est WCAG 2.3.3, et l'accessibilité est une exigence
      permanente du brief. */

/* ═══════════════════════════════════════════════════════════════════════════
   🔥  LES BRAISES — ce qui anime le site entier
   ═══════════════════════════════════════════════════════════════════════════
   Kevin, 2026-07-28 : « quelque chose de fin, de léger, d'animé, comme les
   effets de braise dans la bannière, mais sur le site, qui anime le site
   entier ».

   ⛔ **Ce qui a été essayé et RETIRÉ le même jour**, pour qu'on ne le repropose
   pas : une griffure dessinée en fond (« ça ne rend pas du tout bien, et ce sur
   toutes les pages ») et une nappe de dégradés colorés dans les gouttières
   (« c'est moche en fait »). Les deux partaient du bon constat — le site est
   noir sur les côtés — et y répondaient par de la **surface**. Ce qu'il
   demandait était du **mouvement**. Une texture de fond, aussi bien réglée
   soit-elle, reste un décor immobile qu'on finit par trouver sale ; une braise
   qui monte est vivante à 2 px.

   ⚠️⚠️ **De vrais éléments, pas des dégradés de fond.** L'ancienne version
   empilait sept `radial-gradient` dans un seul pseudo-élément : les sept points
   montaient donc **ensemble**, du même mouvement, à la même vitesse. Ça se voit
   immédiatement et ça ne ressemble à rien. Des braises n'ont l'air de braises
   que si chacune a sa trajectoire, sa durée et son retard — ce qui demande un
   élément par braise. Le script en pose 90 (voir `REVEAL_JS`).

   ⚠️ **Le coût est nul et il faut savoir pourquoi** : ~22 nœuds vides, aucune
   image, aucune requête, et **seuls `transform` et `opacity` s'animent** — donc
   tout se joue sur le compositeur, sans un seul repeint. C'est la règle du
   mouvement en tête de section, et c'est ce qui autorise une animation
   permanente sans coûter une image par seconde.

   ⚠️ **`position:fixed` sur le champ** : les braises appartiennent à la
   fenêtre, pas à la page. Sur une page longue, un champ qui défile laisse le
   bas de la page vide et rend le haut chargé.

   ⚠️ **`overflow:hidden` sur le champ, et lui seul.** Il borne les braises sans
   toucher au document : mis sur `html` ou `main`, il casserait toutes les
   `position:sticky` du site (la barre d'onglets du QG en premier).

   ⚠️ **La bannière n'est pas concernée** — « le bloc bannière est très bien
   comme il est ». Elle a ses propres braises en WebGL et un fond opaque : le
   champ passe derrière elle sans jamais s'y voir. */
.braise-champ {
  position: fixed; inset: 0; overflow: hidden;
  pointer-events: none; z-index: -1;
}
/* Une braise : un point chaud au cœur, qui se perd en rouge NRV sur les bords.
   ⚠️ Pas de `box-shadow` pour la lueur — une ombre portée animée fait repeindre
   à chaque image. Le dégradé radial fait la même lueur, gratuitement. */
.braise {
  position: absolute; bottom: -16px; left: var(--x);
  width: var(--t); height: var(--t); border-radius: 50%;
  background: radial-gradient(circle,
              rgba(255,190,120,.95) 0%,
              rgba(255,110,50,.75) 40%,
              rgba(210,10,46,.35) 62%,
              transparent 72%);
  opacity: 0;
  animation: braise-monte var(--d) linear var(--r) infinite;
  will-change: transform, opacity;
}
/* ⚠️ La braise monte de **plus d'une hauteur d'écran** et dérive de côté : une
   trajectoire strictement verticale se lit comme une barre de chargement. Elle
   naît et meurt en fondu — apparaître d'un coup au ras du bas trahit le truc. */
@keyframes braise-monte {
  0%   { transform: translate3d(0, 0, 0) scale(.6); opacity: 0; }
  10%  { opacity: var(--o); }
  75%  { opacity: var(--o); }
  100% { transform: translate3d(var(--dx), calc(-100vh - 60px), 0) scale(1.15);
         opacity: 0; }
}

/* --- 1. L'apparition : une lame qui découvre ---
   L'ancien `.reveal` montait de 14 px en fondu. Il garde ce mouvement, et
   gagne l'entaille : le bloc se dévoile derrière un bord oblique, comme si on
   l'avait tranché. `clip-path` s'anime sur le compositeur, donc gratuitement.
   ⚠️ `--i` (posé par le script) décale chaque carte d'une rangée : une grille
   qui apparaît d'un bloc paraît figée, une grille qui se dévoile de gauche à
   droite paraît vivante. Plafonné à 6 crans — au-delà, on attend. */
.js .reveal {
  opacity:0; transform:translateY(14px);
  clip-path:polygon(0 0,100% 0,100% 100%,0 100%);
  transition:opacity var(--t),transform var(--t),clip-path var(--t);
  transition-delay:calc(var(--i,0) * 70ms);
}
.js .reveal.seen { opacity:1; transform:none; }

/* --- 2. Le balayage : la signature du survol, **sur les BOUTONS seulement** ---
   Un éclat oblique traverse l'élément une fois, de gauche à droite. Il ne
   revient pas et ne boucle pas : c'est une réponse au geste, pas une
   décoration.

   ⚠️⚠️ **RETIRÉ DES CARTES le 2026-07-28** (Kevin : « l'animation flash du
   survol de la souris sur une fenêtre »). Adoucir ne suffisait pas, parce que
   le défaut n'était pas l'intensité mais **l'échelle** : le même éclat qui fait
   une seconde de vie sur un bouton de 38 sur 190 px traverse une carte de
   700 sur 350 px comme un projecteur. Et sur un tableau de bord, la souris
   survole **une carte après l'autre** en cherchant l'information — on
   déclenchait donc une lumière à chaque passage, pour un élément qu'on ne
   clique même pas la plupart du temps.
   ➡️ Une carte répond autrement, et c'est **plus précis** : son trait passe au
   rouge, elle monte de 2 px, et l'entaille apparaît dans son angle. Trois
   signaux localisés valent mieux qu'un éclairage général.
   💡 **La règle qui en sort** : le balayage appartient à ce qu'on **clique
   volontairement** (un bouton), jamais à ce qu'on **traverse** (une carte).
   ⚠️ `overflow:hidden` + `isolation` : sans quoi l'éclat déborde du rayon. */
.btn { position:relative; overflow:hidden; isolation:isolate; }
.lame { position:relative; overflow:hidden; }
/* ⚠️⚠️ **Adouci le 2026-07-28** (Kevin : « l'animation flash est un peu trop
   pétante et flash rapide »). Il avait raison, et le défaut était double :
   - **le blanc pur** à 13 % (28 % sur le bouton rouge) sur un fond noir, c'est
     un éclair d'appareil photo, pas un reflet — et il ne vient d'aucune couleur
     du site ;
   - **0,55 s en `ease-in-out`**, c'est un aller-retour qui *claque* : la lueur
     accélère au milieu, là où l'œil la suit.
   ➡️ Elle passe à **0,95 s en décélération** (`ease-out` : elle entre vite et
   s'éteint doucement, comme une lame qu'on retire), le blanc devient un
   **reflet teinté du rouge NRV** à 6 %, et les bords du dégradé sont **étalés**
   (20 %/50 %/80 % au lieu de trois arrêts serrés) pour qu'il n'y ait aucune
   arête franche à voir passer. */
.btn::after {
  content:""; position:absolute; z-index:-1; inset:-2px -40%;
  background:linear-gradient(90deg,transparent 18%,
             rgba(255,214,222,.06) 50%,transparent 82%);
  transform:translateX(-120%) var(--biais-t);
  transition:transform .95s cubic-bezier(.16,.84,.44,1);
  pointer-events:none;
}
.btn:hover::after { transform:translateX(120%) var(--biais-t); }
/* Sur le bouton rouge, le reflet est un peu plus présent — c'est l'action
   qu'on veut — mais il reste très en dessous de l'ancien blanc à 28 %. */
.btn.primary::after {
  background:linear-gradient(90deg,transparent 18%,
             rgba(255,255,255,.11) 50%,transparent 82%);
}

/* --- 3. La carte qui répond ---
   Au survol : elle monte de 2 px (pas 4 — une carte n'est pas un bouton), son
   trait passe au rouge, et une **entaille rouge** apparaît dans l'angle haut
   droit. C'est le clin d'œil au logo, et il ne coûte qu'un pseudo-élément. */
.lame { transition:transform var(--t-fast),border-color var(--t-fast); }
.lame:hover { transform:translateY(-2px); border-color:var(--red-line); }
.lame::before {
  content:""; position:absolute; top:0; right:0; width:34px; height:34px;
  background:linear-gradient(var(--biais),transparent 48%,var(--red) 48%,
             var(--red) 56%,transparent 56%);
  opacity:0; transition:opacity var(--t-fast); pointer-events:none;
}
.lame:hover::before { opacity:.85; }

/* --- 4. Le trait qui vit ---
   Le séparateur `.sep` portait un dégradé fixe. Un éclat le parcourt
   maintenant, très lentement et très discrètement : c'est ce qui donne
   l'impression que la page est sous tension plutôt qu'imprimée. */
.sep { position:relative; overflow:hidden; }
/* ⚠️ **Deux fois plus lent et deux fois plus discret** (même retour). Un éclat
   vif qui traverse toutes les 7 s se **remarque** — donc on le regarde, donc il
   coûte l'attention qu'on voulait mettre ailleurs. À 14 s et 32 %, il se
   ressent : on sait que la page est sous tension sans savoir pourquoi.
   Le trait est aussi plus **large** (34 % au lieu de 22 %) : plus il est étalé,
   moins son passage a d'arête. */
.sep::after {
  content:""; position:absolute; inset:0; width:34%;
  background:linear-gradient(90deg,transparent,var(--red),transparent);
  animation:balayage var(--t-slow) linear infinite;
  opacity:.32;
}
@keyframes balayage {
  from { transform:translateX(-120%); }
  to   { transform:translateX(560%); }
}

/* --- 5. Le titre qui se pose ---
   Le mot rouge d'un titre (`span.hl`) est souligné d'une entaille qui se
   déploie quand la section apparaît. Elle part de zéro et s'ouvre : le titre
   « se signe » lui-même. */
h1 span.hl, h2 span.hl { position:relative; }
h1 span.hl::after, h2 span.hl::after {
  content:""; position:absolute; left:0; right:0; bottom:-.12em; height:3px;
  background:linear-gradient(90deg,var(--red),transparent);
  transform:scaleX(0) var(--biais-t); transform-origin:left;
  transition:transform .7s cubic-bezier(.4,0,.2,1) .2s;
}
.reveal.seen h1 span.hl::after, .reveal.seen h2 span.hl::after,
h1.seen span.hl::after, h2.seen span.hl::after { transform:scaleX(1) var(--biais-t); }

/* --- 6. Le focus clavier, lui aussi ---
   Le contour rouge arrive maintenant avec une très courte impulsion : au
   clavier, on veut *voir* où l'on vient d'atterrir, pas le déduire. */
/* ⚠️ Le contour de focus se **pose**, il ne claque pas : 0,35 s en
   décélération et un écart de départ réduit (6 px au lieu de 9). Il doit dire
   « tu es ici », pas attirer le regard comme une alerte. */
:focus-visible { animation:atterrissage .35s cubic-bezier(.16,.84,.44,1); }
@keyframes atterrissage {
  from { outline-offset:6px; }
  to   { outline-offset:3px; }
}

/* --- Le mouvement est une option, pas un dû --- */
@media (prefers-reduced-motion:reduce) {
  html { scroll-behavior:auto; }
  *,*::before,*::after {
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
  .js .reveal { opacity:1; transform:none; clip-path:none; }
  .btn:hover, .lame:hover { transform:none; }
  /* Le squelette redevient un aplat : sa **présence** dit déjà « ça charge »,
     le scintillement n'y ajoute rien pour qui a demandé le calme. */
  .skel { animation:none; background:var(--surface-2); }
  /* ⚠️⚠️ **Tout le système d'entailles s'éteint ici**, et il faut le faire
     élément par élément : `animation-duration:.01ms` (la règle globale
     au-dessus) accélère les boucles mais laisse un balayage traverser l'écran
     en un éclair — pire qu'une animation lente pour quelqu'un qui a demandé le
     calme. On les **supprime**, on ne les précipite pas. */
  .sep::after { animation:none; display:none; }
  .btn::after { display:none; }
  .lame::before { transition:none; }
  h1 span.hl::after, h2 span.hl::after { transform:scaleX(1) var(--biais-t);
                                         transition:none; }
  :focus-visible { animation:none; }
  /* ⚠️⚠️ **Les braises sont RETIRÉES, pas ralenties.** La règle globale
     au-dessus (`animation-duration:.01ms`) ferait traverser l'écran à vingt
     points en un éclair, puis recommencerait à l'infini : pire qu'une animation
     lente pour quelqu'un qui a demandé le calme. Et contrairement à un décor de
     fond, une braise arrêtée n'a aucun sens — c'est un point orange posé au
     hasard. On les efface. */
  .braise-champ { display:none; }
}

/* 🐞🐞 **CE FOND ENTERRAIT LA GRIFFURE** (constaté par Kevin le 2026-07-28 :
   « je ne la vois nulle part »). Un `body::before` en `z-index:-1` se peint
   **derrière le fond de `body`** : tant que cette règle posait un dégradé
   opaque ici, la griffure était bien rendue et totalement invisible.
   ➡️ Le halo déménage sur **`html`**, où il reste ce qu'il a toujours été — une
   lueur douce en haut de page — mais laisse la griffure se glisser entre lui
   et le contenu.
   ⚠️ **Deux fichiers posaient un fond sur le même élément** (`design.py` et
   celui-ci) : c'est le second qui gagnait, en silence. Une propriété partagée
   entre le socle et une façade doit vivre dans le socle, sinon on débogue à
   l'aveugle. */
html{background:radial-gradient(1100px 620px at 50% -12%,#141821,var(--bg) 62%)
      no-repeat,var(--bg)}

/* ===================== Barre de navigation collante =====================
   Une seule barre pour TOUTES les pages publiques (avant, l'accueil avait la
   sienne dans le héros et les autres pages une autre : deux navigations à
   tenir à jour, et le lien actif ne se voyait pas pareil). */
.topbar{
  position:sticky; top:0; z-index:50;
  height:var(--nav-h); display:flex; align-items:center; gap:1rem;
  padding:0 var(--gutter);
  border-bottom:1px solid var(--line);
  background:rgba(7,8,11,.88);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  backdrop-filter:blur(14px) saturate(140%);
  transition:background var(--t-fast),border-color var(--t-fast);
}
/* Sur l'accueil, la barre flotte AU-DESSUS du héros : la marge négative fait
   remonter le héros sous elle, sans le pousser de 76 px vers le bas. */
.topbar.over{
  margin-bottom:calc(-1 * var(--nav-h));
  background:linear-gradient(rgba(0,0,0,.55),transparent);
  border-bottom-color:transparent; backdrop-filter:none;
}
/* Dès qu'on a scrollé, elle redevient une barre : sinon les liens se perdent
   dans l'image. Classe posée par le JS ; sans JS elle reste translucide, ce
   qui est le pire cas acceptable. */
.topbar.over.solid{
  background:rgba(7,8,11,.92); border-bottom-color:var(--line);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  backdrop-filter:blur(14px) saturate(140%);
}
.brand{display:flex;align-items:center;gap:12px;font-family:var(--font-title);
  font-weight:700;font-size:1.4rem;text-transform:uppercase;letter-spacing:.06em;
  line-height:1}
.brand img{height:40px;width:40px;display:block}
.brand small{display:block;margin-top:2px;font-family:var(--font-body);
  font-size:.75rem;color:var(--muted);letter-spacing:.16em;font-weight:600;
  text-transform:uppercase}
.links{margin-left:auto;display:flex;gap:clamp(14px,1.8vw,26px);align-items:center}
.links a{position:relative;color:#C9CDD5;font-weight:600;font-size:.95rem;
  padding:6px 0;transition:color var(--t-fast)}
.links a:hover{color:#fff}
/* L'état actif : un trait rouge sous le lien. Le rouge sert ici à ce pour quoi
   il est fait — dire où l'on est. */
.links a.on{color:#fff}
.links a.on:after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--red)}
.links a.staff{
  padding:0 16px;height:38px;display:inline-flex;align-items:center;color:#fff;
  border:1px solid var(--red-line);background:var(--red-soft);
  border-radius:var(--r-sm);
  clip-path:polygon(0 0,calc(100% - 8px) 0,100% 8px,100% 100%,0 100%);
  transition:background var(--t-fast),border-color var(--t-fast);
}
.links a.staff:hover{background:var(--red);border-color:var(--red)}
.links a.staff:after{display:none}
/* ⚠️ `min-height:36px` : mesuré à **33 px** le 2026-07-28, le dernier lien
   de la barre sous la cible tactile du site. La hauteur vient d'un
   remplissage — le texte garde sa taille et sa place. */
.links a.logout{color:var(--muted);font-size:.82rem;
  display:inline-flex;align-items:center;min-height:36px}
.links a.logout:hover{color:var(--red)}
/* La pastille des nouveautés. Le rouge sert ici à ce pour quoi la palette le
   réserve : signaler quelque chose qui demande une attention — pas décorer.
   ⚠️ `min-width` et non `width` : à deux chiffres, un cercle fixe rognerait le
   nombre. Elle grandit, elle ne tronque pas. */
.links .pastille{display:inline-flex;align-items:center;justify-content:center;
  min-width:19px;height:19px;margin-left:7px;padding:0 5px;border-radius:999px;
  background:var(--red);color:#fff;font-family:var(--font-title);font-weight:700;
  font-size:.75rem;line-height:1;vertical-align:middle}

/* Le bouton du menu mobile : trois traits qui deviennent une croix. */
.burger{display:none;margin-left:auto;width:44px;height:44px;padding:0;
  background:transparent;border:1px solid var(--line);border-radius:var(--r-sm);
  cursor:pointer;place-items:center;gap:5px;grid-auto-rows:2px}
.burger span{display:block;width:20px;height:2px;background:var(--text);
  transition:transform var(--t-fast),opacity var(--t-fast)}
.topbar.open .burger span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.topbar.open .burger span:nth-child(2){opacity:0}
.topbar.open .burger span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* ============================== Héros ============================== */
/* ⚠️ **Hauteur revue le 2026-07-27** (Kevin : « la bannière est grande et vite
   vide »). Elle valait `min(88vh,760px)` : sur un écran large, ça faisait un
   grand aplat vide autour de trois lignes de texte, et la barre de chiffres —
   la vraie substance — passait sous la ligne de flottaison. Désormais la
   hauteur suit le contenu (`min-height` plancher, pas consigne), plafonnée à
   58vh : le titre reste grand, et on voit les chiffres sans faire défiler. */
.hero{position:relative;overflow:hidden;
  min-height:min(58vh,520px);display:flex;align-items:flex-end}
/* ⚠️ `banner-nrv.jpg` N'EST PAS le fond du héros, et ne doit pas le redevenir :
   elle porte « NET REALISABLE VALUE » **peint dessus**. Derrière un H1 qui dit
   la même chose, on lisait le nom deux fois, en travers l'un de l'autre — vu à
   l'écran, invisible dans le code, et c'est la redondance que Kevin a signalée.
   Assez floutée pour disparaître, elle laissait un rectangle noir (« trop
   sombre », qu'il exclut). Elle garde donc son vrai emploi : l'aperçu social
   (og:image), où ce texte est un atout.
   Le décor est fabriqué ici : une lueur rouge, deux entailles obliques dans la
   grammaire du logo, et le loup en filigrane. Aucune image lourde, aucun mot
   répété. */
.hero-media{position:absolute;inset:0;
  background:
    /* La lueur est **calée sur le loup** (70 %, à mi-hauteur) : c'est elle qui
       le pose sur le fond au lieu de le laisser flotter. */
    radial-gradient(720px 520px at 70% 48%,rgba(210,10,46,.20),transparent 68%),
    radial-gradient(900px 620px at 8% 104%,rgba(117,7,25,.26),transparent 72%),
    linear-gradient(180deg,#0B0E14,var(--bg) 80%)}
/* Les entailles : trois traits obliques, dont un seul franchement rouge. Le
   logo est fait de coups de lame ; on en reprend l'idée, pas le dessin.
   ⚠️ **Elles s'éteignent avant le loup** (masque à droite, 2026-07-27). Kevin :
   « je le trouve cassé / caché par le design barré rouge du fond ». Les traits
   traversaient le filigrane de part en part et le hachaient. Ils gardent donc
   la moitié gauche — celle du titre, où ils font le mouvement — et libèrent la
   droite, où le loup a besoin d'un fond calme. */
.hero-media:after{content:"";position:absolute;inset:0;
  background:
    linear-gradient(104deg,transparent 47.6%,rgba(210,10,46,.55) 47.9%,transparent 48.3%),
    linear-gradient(104deg,transparent 53.4%,rgba(241,242,244,.07) 53.6%,transparent 53.9%),
    linear-gradient(104deg,transparent 61.2%,rgba(210,10,46,.18) 61.6%,transparent 62.1%);
  -webkit-mask-image:linear-gradient(90deg,#000 0 42%,transparent 66%);
  mask-image:linear-gradient(90deg,#000 0 42%,transparent 66%)}

/* ---------------------- La vallée animée (WebGL) ----------------------
   Un canvas plein cadre, purement décoratif, dessiné par `_HERO_JS`. Il
   couvre `.hero-media` quand WebGL répond, et disparaît sinon.
   La classe `gl` n'est posée QUE si le rendu a démarré : tout ce qui
   suppose la vallée est accroché à elle, jamais au canvas seul. */
#hero-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
  opacity:0;transition:opacity .9s ease;pointer-events:none;z-index:0}
.hero.gl #hero-canvas{opacity:1}
/* ⚠️ Le loup passe de filigrane (0,16) à SUJET (0,92) — et seulement ici.
   Le commentaire de `.hero-mark` dit qu'un filigrane lisible devient un
   fond : c'était juste tant que le fond était un aplat CSS. Avec une
   vallée derrière lui, le loup n'est plus le fond, il est ce qui s'y
   tient — et à 0,16 il redevenait invisible. Sans WebGL, il reprend
   0,16 tout seul, la règle d'origine n'est pas touchée. */
.hero.gl .hero-mark{opacity:.92}
/* Le texte est désormais posé sur une image, plus sur un aplat : sans ces
   ombres portées il perd son contraste dès que la brume passe derrière. */
.hero.gl h1{text-shadow:0 2px 30px rgba(0,0,0,.85)}
.hero.gl .eyebrow,.hero.gl .lead{text-shadow:0 1px 16px rgba(0,0,0,.9)}
/* Le loup : l'identité sans un mot de plus.
   ⚠️ **Ramené vers le centre et agrandi le 2026-07-27** (Kevin : « plus ramené
   vers le centre, peut-être plus gros, pour donner du relief »). Il était collé
   au bord droit (`right:-1%`), donc quasi hors champ sur un écran large — il
   décorait le vide au lieu d'habiller le titre. Ancré à **62 %** : il tient
   compagnie au H1 sans passer dessous (mesuré à 1440 : à 62 % il
   chevauchait le titre, à 70 % il s'arrête juste après).
   La lueur rouge derrière lui fait le relief demandé ; l'opacité reste basse
   (0,11) parce qu'un filigrane qui se lit devient un fond, et le fond du héros
   est fabriqué en CSS, pas en image (voir plus haut). */
/* ⚠️ **Ancré au bord droit de la zone de contenu**, pas à un pourcentage de
   l'écran : c'est ce qui garantit qu'il ne touche jamais le titre, quelle que
   soit la largeur (le titre, lui, s'arrête forcément avant ce bord). Mesuré à
   1024, 1440 et 1920 : l'écart ne descend jamais sous 100 px. */
.hero-mark{position:absolute;top:50%;left:auto;
  right:calc((100vw - min(var(--wrap),92vw)) / 2 - 90px);transform:translateY(-50%);
  width:clamp(240px,24vw,420px);height:auto;opacity:.16;pointer-events:none;
  /* Un contre-jour, pas un halo : la lueur sort du dessin et retombe, ce qui
     donne le relief demandé sans allumer la page. */
  filter:drop-shadow(0 22px 46px rgba(0,0,0,.85))
         drop-shadow(0 0 90px rgba(210,10,46,.45));
  -webkit-user-select:none;user-select:none}
/* Sous 1024 px, le titre occupe toute la largeur : le loup repart sur le bord
   et rapetit, sinon il passe derrière le texte au lieu de l'accompagner. */
/* Sous 1024 px le titre prend toute la largeur : le loup ne peut plus être à
   côté. Il passe **derrière, plus haut et plus effacé** — un filigrane, pas un
   voisin. C'est le seul cas où il se superpose au texte, et à 6 % il ne le
   gêne pas. */
@media(max-width:1024px){
  .hero-mark{top:34%;right:-10%;width:clamp(220px,44vw,340px);opacity:.06}
}
/* Le liseré rouge du bas, qui accroche la barre de chiffres. */
.hero:after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:linear-gradient(90deg,var(--red),var(--red-dark) 40%,transparent 80%)}

/* ---------------------------- MODE CINÉMA -----------------------------
   La vallée animée mérite mieux qu'un bandeau de 58vh : ce mode lui donne
   l'écran entier, efface tout le reste, et fait défiler les chiffres RÉELS
   de la guilde par-dessus. Une bande-annonce dont rien n'est inventé.

   ⚠️ **Il n'y a AUCUN bouton pour l'ouvrir** (Kevin, 2026-07-27 : « enlève le
   bouton mode cinéma, par contre l'affichage en mode cinéma pourrait servir
   d'entrée de page »). C'est devenu l'entrée du site : elle s'ouvre seule à la
   première arrivée, puis s'efface. Personne ne clique sur « mode cinéma » ;
   tout le monde passe par l'entrée.

   ⚠️ Tout est accroché à `.hero.gl` : sans WebGL, pas d'intro. Passer un aplat
   CSS en plein écran n'aurait rien de spectaculaire — et l'intro serait alors
   un temps d'attente offert à personne. */
/* Le héros quitte le flux et prend tout : `fixed`, pas `absolute` — il doit
   rester en place même si la page avait été défilée avant. */
body.cinema{overflow:hidden}
body.cinema .hero{position:fixed;inset:0;height:100dvh;z-index:9999;
  border:0;margin:0}
body.cinema .hero:after{display:none}
/* Le reste de la page ne se contente pas d'être caché derrière : il est retiré
   du rendu, sinon la barre collante reste au-dessus de tout. */
body.cinema .topbar,body.cinema main,body.cinema .site-footer,
body.cinema .stats{display:none}
/* Le loup, en grand et centré : c'est lui l'affiche.
   ⚠️ **Opacité 0,22 et non 0,42** : à 0,42 son blanc passait à travers les
   cartons de chiffres et « 4 105 » devenait illisible (vu à l'écran). En mode
   cinéma il redevient ce qu'il était sur la page — un filigrane, pas un
   concurrent du texte posé dessus. */
/* ⚠️ **`.hero.gl` dans le sélecteur, sinon la règle ne s'applique pas.**
   `.hero.gl .hero-mark` pèse trois classes et gagnait contre `body.cinema
   .hero-mark` (deux classes) : le loup restait à 0,92 et écrasait les chiffres.
   Vu à l'écran — la valeur était juste, elle n'était simplement jamais lue. */
body.cinema .hero.gl .hero-mark{top:50%;right:auto;left:50%;
  transform:translate(-50%,-50%);width:clamp(300px,42vw,620px);opacity:.22}
/* Le texte est posé sur une image : sans ombre portée il perd tout contraste
   dès qu'il croise le loup ou une balafre. */
body.cinema .hero-inner .eyebrow,body.cinema .cine-carton b,
body.cinema .cine-carton span,body.cinema .hero .lead{
  text-shadow:0 2px 18px rgba(0,0,0,.95)}
body.cinema .hero-inner{position:absolute;inset:0;display:grid;
  place-content:center;text-align:center;padding:0 24px;width:100%}
body.cinema .hero h1{font-size:clamp(2.2rem,7vw,5.6rem)}
/* Les cartons de chiffres, sous le titre. Ils apparaissent l'un après l'autre
   puis restent : une bande-annonce qui efface ses informations oblige à la
   regarder deux fois. */
.cine-cartons{display:flex;flex-wrap:wrap;gap:clamp(18px,4vw,52px);
  justify-content:center;margin-top:clamp(24px,4vw,44px)}
.cine-carton{opacity:0;animation:cine-entree .9s ease forwards}
.cine-carton b{display:block;font-family:var(--font-title);font-weight:700;
  font-size:clamp(1.8rem,4.4vw,3.4rem);line-height:1;color:var(--text)}
.cine-carton span{display:block;margin-top:6px;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.1em}
@keyframes cine-entree{from{opacity:0;transform:translateY(14px)}
  to{opacity:1;transform:none}}
.cine-sortie{position:fixed;top:20px;right:20px;z-index:10000;
  padding:9px 16px;border-radius:999px;cursor:pointer;
  background:rgba(8,9,12,.62);border:1px solid var(--line);color:var(--text);
  font-family:var(--font-title);font-weight:700;font-size:.76rem;
  text-transform:uppercase;letter-spacing:.07em}
.cine-sortie:hover{border-color:var(--red)}
/* ⚠️ Animations réduites : les cartons apparaissent d'un coup plutôt que de
   monter. Le mode reste utilisable — c'est l'effet qu'on retire, pas la
   fonction. */
@media (prefers-reduced-motion:reduce){
  .cine-carton{animation:none;opacity:1}
}
.hero-inner{position:relative;z-index:1;
  width:min(var(--wrap),92vw);margin:0 auto;
  padding:calc(var(--nav-h) + clamp(28px,5vw,56px)) 0 clamp(32px,5vw,56px)}
/* ⚠️ **Le nom tient sur UNE ligne** (Kevin, 2026-07-27 : « écrire le nom entier
   dans sa largeur »). Il était plafonné à 16 caractères et cassait en deux —
   « NET REALISABLE / VALUE », ce qui coupait le nom en son milieu.
   La taille est donc pilotée par la **largeur de la fenêtre** (`vw`) et non par
   le pas typographique commun : c'est la seule façon qu'une ligne de 20
   caractères reste entière de 360 px à un ultrawide. Mesuré aux deux bouts. */
.hero .eyebrow{margin-bottom:14px;font-size:.8rem;letter-spacing:.3em}
/* ⚠️ **Plafonné à 4,6rem, et ce plafond n'est pas cosmétique** : sans lui, un
   titre en `vw` continue de grandir alors que la zone de contenu, elle, est
   bloquée à 1240 px — sur un ultrawide il sortait du cadre et venait toucher
   le loup (signalé par Kevin le 2026-07-27). Les deux sont désormais calés sur
   la **même** zone de contenu, avec un écart garanti (voir `.hero-mark`). */
.hero h1{max-width:none;white-space:nowrap;font-size:clamp(1.6rem,5.6vw,4.6rem);
  line-height:1.04}
/* Le sous-titre remonte d'un cran : c'est lui qui dit ce qu'on joue, il ne doit
   pas être une note de bas de page sous un titre géant. */
.hero .lead{max-width:52ch;margin:18px 0 0;font-size:clamp(1rem,.9rem + .5vw,1.3rem);
  color:#C9CED8}
.cta-row{display:flex;gap:14px;flex-wrap:wrap;margin-top:clamp(26px,3vw,36px)}

/* ====================== Barre de chiffres ======================
   Sous le héros, quatre faits. Tous viennent de la base : aucun n'est écrit
   en dur, et la barre disparaît entièrement quand il n'y a rien à dire. */
.stats{border-bottom:1px solid var(--line);
  background:linear-gradient(180deg,var(--surface),rgba(17,20,26,0))}
/* Le nombre de cases VARIE (3, 4 ou 5) : une case n'apparaît que si la base a
   de quoi la remplir. D'où `grid-auto-flow:column` plutôt qu'un nombre de
   colonnes figé — c'était `repeat(4,1fr)`, et la cinquième passait à la ligne
   toute seule au milieu de la barre. */
.stats-wrap{width:min(var(--wrap),92vw);margin:0 auto;
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr}
/* ⚠️ **Barre resserrée le 2026-07-27** (Kevin : « la réduire, plus en mode
   détails, toujours sur la largeur de page, mais plus petite »). Elle garde
   ses cinq colonnes sur toute la largeur ; ce sont la hauteur et la taille des
   chiffres qui baissent — c'est une ligne de faits, pas une affiche. */
.stat{position:relative;padding:clamp(14px,1.7vw,20px) clamp(8px,1.6vw,18px);
  text-align:center}
.stat + .stat:before{content:"";position:absolute;left:0;top:24%;bottom:24%;
  width:1px;background:var(--line)}
.stat-n{display:block;font-family:var(--font-title);font-weight:700;line-height:1;
  font-size:clamp(1.35rem,.9rem + 1.3vw,1.9rem);letter-spacing:var(--track-title)}
/* Le doré, exactement pour ce à quoi il est réservé : une distinction. */
.stat-n.gold{color:var(--gold)}
.stat-n.txt{font-size:clamp(1.1rem,.85rem + .8vw,1.5rem)}
.stat-l{display:block;margin-top:6px;font-size:.75rem;text-transform:uppercase;
  letter-spacing:.14em;color:var(--muted)}
.stat-note{display:block;margin-top:3px;font-size:.75rem;color:var(--faint)}

/* ============================ Sections ============================ */
.section{width:min(var(--wrap),92vw);margin:0 auto;padding:var(--section-y) 0;
  position:relative}
/* --- Le trait de séparation entre deux sections ---
   Demandé par Kevin le 2026-07-27 (« un p'tit trait lissé, laser, en
   décoration pour indiquer la séparation »). Un **trait qui s'allume au
   centre et s'éteint aux bords** : une règle pleine largeur couperait la page
   en morceaux, alors qu'on veut seulement marquer le passage.
   Il ne se pose qu'ENTRE deux sections (`+`), jamais au-dessus de la
   première ni sous la dernière. */
.section + .section:before{content:"";position:absolute;top:0;left:8%;right:8%;
  height:1px;background:linear-gradient(90deg,transparent,
    rgba(210,10,46,.55) 35%,rgba(255,74,99,.85) 50%,rgba(210,10,46,.55) 65%,
    transparent)}
/* La lueur, très basse : elle se voit sur du noir, sans éclairer la page. */
.section + .section:after{content:"";position:absolute;top:-2px;left:26%;right:26%;
  height:5px;background:radial-gradient(60% 100% at 50% 50%,
    rgba(210,10,46,.22),transparent 70%);pointer-events:none}
.section h2{margin:0 0 16px}
.section h2 span,.hero h1 span,.armory h1 span,
.recruit h1 span,.recruit h2 span{color:var(--red)}
.section > p{color:var(--muted);max-width:66ch}

/* --- Présentation + aperçu Discord --- */
/* ⚠️ Kevin, 2026-07-27, en deux temps : la carte était « un peu petite », je
   l'ai élargie (1fr/1fr), et il a tranché — « plus haut, pas forcément plus
   large, je voudrais voir plus de chans, tu peux réduire la largeur », puis
   « encore un peu moins large », deux fois. Mesuré à 1440 : 1fr/1fr → 604 px,
   1,35fr → 514, 1,6fr → 465, **1,9fr → 414**, la valeur retenue. Tout
   l'agrandissement passe donc par la **hauteur** (voir `.live`). */
.about{display:grid;grid-template-columns:1.9fr 1fr;gap:var(--gap);align-items:start}
.widget-card{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.widget-head{display:flex;align-items:center;gap:8px;padding:14px 18px;
  font-family:var(--font-title);font-weight:700;font-size:1rem;
  text-transform:uppercase;letter-spacing:.06em;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.widget-head b{color:var(--red)}
.widget-empty{padding:40px 20px;text-align:center;color:var(--muted);font-size:.9rem}
.widget-foot{margin:0;padding:12px 18px;font-size:.78rem;color:var(--muted);
  border-top:1px solid var(--line)}

/* --- Le widget maison (remplace l'iframe de Discord) ---
   Hauteur minimale : le cadre occupe sa place AVANT que les chiffres arrivent,
   sinon la section « Qui sommes-nous » sauterait au moment de la réponse.
   ⚠️ **750 px** : demandé par Kevin (« plus haut, je voudrais voir plus de
   chans »), et la place existe — la section « Qui sommes-nous » laissait un
   grand vide sous la carte. La hauteur est **réservée d'avance** parce qu'une
   carte qui grandit à l'arrivée de la réponse fait sauter toute la page
   (mesuré : 190 px de saut quand elle ne l'était pas). C'est aussi pourquoi
   l'arborescence est plafonnée : sans plafond, la hauteur finale dépendrait du
   nombre de salons, donc de qui regarde. */
.live{padding:20px;min-height:750px}
/* Les squelettes n'existent que si le JavaScript répond : sans lui, ils
   attendraient une donnée qui n'arrivera jamais. */
.live-skel{display:none;gap:12px}
.js .live-skel{display:grid}
.live-skel .skel-n{--skel-h:34px;width:52%}
.live-skel .skel-l{--skel-h:12px;width:72%}
.live-skel .skel-p{--skel-h:12px;width:88%}
/* Sans JavaScript, les squelettes ne se rempliraient jamais : on montre la
   phrase à la place. Même règle que les filtres de l'armurerie. */
.js .live-noscript{display:none}
.live-noscript{margin:0;color:var(--muted);font-size:.92rem}
.live-top{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.live-n{font-family:var(--font-title);font-weight:700;line-height:1;
  font-size:clamp(2rem,1.2rem + 1.8vw,2.6rem);font-variant-numeric:tabular-nums}
.live-lab{color:var(--muted);font-size:.95rem}
.live-tags{list-style:none;display:flex;flex-wrap:wrap;gap:8px 16px;
  margin:14px 0 0;padding:0;font-size:.86rem;color:var(--muted)}
.live-tags li{display:flex;align-items:center;gap:7px}
/* La pastille ne porte PAS l'information à elle seule : le mot est écrit à
   côté (même correctif que les couleurs de classe de l'armurerie). */
.pt{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none}
.pt.on{background:#3ba55d}.pt.idle{background:#faa81a}.pt.dnd{background:#ed4245}
.live-game{margin:14px 0 0;font-size:.9rem}
.live-game b{color:var(--red-text)}
/* Les deux blocs sont EMPILÉS, jamais côte à côte : la carte est étroite par
   choix, et deux colonnes de 180 px n'auraient été lisibles ni l'une ni
   l'autre. « En ligne » d'abord (c'est ce qui bouge), les salons ensuite (c'est
   ce qui est long). Chacun sa fenêtre de défilement, donc une hauteur qui ne
   dépend ni du nombre de connectés ni du nombre de salons. */
.live-cols{display:grid;grid-template-columns:1fr;gap:14px;
  margin:16px 0 0;padding:14px 0 0;border-top:1px solid var(--line)}
.live-tree{max-height:560px}

/* L'arborescence du serveur, telle que le grade du visiteur l'autorise.
   Elle défile DANS la carte : un Discord fourni ferait une page d'accueil
   interminable (même principe que `.scroll-x` pour les tableaux). */
.live-tree{overflow-y:auto;scrollbar-width:thin;
  scrollbar-color:var(--line-strong) transparent}
/* Qui est dans un salon vocal — en retrait sous le salon, comme dans Discord.
   La liste n'existe que pour les visiteurs reconnus : le serveur ne l'envoie
   pas aux autres. Rien à masquer en CSS, donc rien à contourner. */
.live-in{list-style:none;margin:2px 0 6px;padding:0 0 0 26px;display:grid;gap:3px}
.live-in li{display:flex;align-items:center;gap:8px;font-size:.82rem;
  color:var(--text);padding:1px 0}
.live-in .vname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.live-in .pt{width:7px;height:7px}
.live-cat{margin:12px 0 6px;font-size:.75rem;text-transform:uppercase;
  letter-spacing:.12em;color:var(--faint)}
.live-cat:first-child{margin-top:0}
.live-rooms{list-style:none;margin:0;padding:0;display:grid;gap:4px}
.live-rooms li{display:flex;align-items:center;gap:8px;font-size:.88rem;
  color:var(--muted);padding:2px 0}
/* Un salon vocal occupé remonte en couleur pleine : c'est là que ça se passe. */
.live-rooms li.ici{color:var(--text)}
.live-rooms .ric{color:var(--faint);width:1.1em;text-align:center;flex:none}
.live-rooms li.ici .ric{color:var(--red-text)}
.live-rooms .vn{margin-left:auto;color:var(--muted);font-size:.82rem;
  font-variant-numeric:tabular-nums;background:var(--surface-2);
  border:1px solid var(--line);border-radius:999px;padding:1px 8px}
.live-rooms .vname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.live-note{margin:14px 0 0;font-size:.78rem;color:var(--faint)}
.live-note a{color:var(--red-text)}
.live-none{margin:16px 0 0;padding-top:14px;border-top:1px solid var(--line);
  color:var(--faint);font-size:.86rem}
/* L'état d'erreur : ce qui s'est passé, et de quoi y revenir. */
.live-err{margin:0;color:var(--muted);font-size:.92rem}
.live-err .btn{margin-top:14px}

/* --- La chaîne Twitch ---
   Trois vignettes en ligne, une seule sur mobile. Le violet de Twitch n'entre
   pas dans la page : seul le badge « EN DIRECT » a le droit au rouge NRV, qui
   est justement la couleur des états actifs. */
.tw-bloc{margin-top:clamp(28px,3vw,40px)}
.tw-h{margin:0 0 14px;font-size:1.15rem;text-transform:uppercase;
  letter-spacing:.04em}
.tw-h span{color:var(--red)}
/* Deux vignettes de front : la colonne fait la moitié de la page, trois
   auraient donné des cartes trop petites pour qu'on distingue quoi que ce
   soit. La troisième vidéo passe simplement à la ligne. */
.tw{min-height:200px}
.tw-skel{display:none;grid-template-columns:repeat(2,1fr);gap:16px}
.js .tw-skel{display:grid}
.tw-skel .skel-v{--skel-h:170px;border-radius:var(--r)}
.tw-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}
.tw-card{display:block;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden;color:inherit;text-decoration:none;
  transition:transform var(--t),border-color var(--t)}
.tw-card:hover{transform:translateY(var(--lift));border-color:var(--line-strong)}
.tw-vig{position:relative;aspect-ratio:16/9;background:var(--surface-2);display:block}
.tw-vig img{width:100%;height:100%;object-fit:cover;display:block}
.tw-dur{position:absolute;right:8px;bottom:8px;background:rgba(7,8,11,.82);
  border-radius:4px;padding:2px 7px;font-size:.75rem;font-variant-numeric:tabular-nums}
.tw-txt{padding:12px 14px}
.tw-titre{margin:0;font-size:.95rem;line-height:1.35;display:-webkit-box;
  -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tw-meta{margin:6px 0 0;font-size:.78rem;color:var(--muted)}
/* Le live prend toute la largeur : c'est l'information du moment. */
.tw-direct{grid-column:1/-1;display:grid;grid-template-columns:1.1fr 1fr;
  gap:var(--gap);align-items:center}
.tw-direct .tw-vig{border-radius:var(--r)}
.tw-badge{display:inline-flex;align-items:center;gap:7px;background:var(--red);
  color:#fff;border-radius:999px;padding:3px 11px;font-size:.75rem;font-weight:700;
  text-transform:uppercase;letter-spacing:.1em}
.tw-badge i{width:7px;height:7px;border-radius:50%;background:#fff;display:block}
.tw-direct h3{margin:12px 0 6px;font-size:1.25rem}
.tw-none{color:var(--muted)}
/* Les chaînes amies : un intertitre discret, et le nom SUR la carte — une
   vignette de plus ne dit pas à qui elle appartient. */
.tw-part{margin:22px 0 12px;font-size:.75rem;text-transform:uppercase;
  letter-spacing:.14em;color:var(--faint)}
/* Le décompte par rôle sur une carte de soirée. Les quatre sont toujours là ;
   une case à zéro s'efface sans disparaître — c'est elle qui appelle du monde.
   ⚠️⚠️ **Ne PAS l'appeler `.roles`** : ce nom est déjà pris par la grille des
   rôles de la page recrutement (`repeat(auto-fit,minmax(260px,1fr))`, plus bas
   dans cette même feuille). Les deux façades partagent un seul fichier de
   style : une classe générique réutilisée, et la règle la plus basse gagne.
   Constaté à l'écran par Kevin — quatre barres de 260 px empilées au lieu de
   quatre pastilles — alors que le HTML et le flex étaient justes. */
.ev-roles{display:inline-flex;flex:0 0 auto;flex-wrap:nowrap;gap:7px}
/* Une largeur MINIMALE identique pour les quatre : les pastilles s'alignent
   alors d'une carte à l'autre, et « 12 » ne fait pas sauter la rangée. */
.ev-roles .rl{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  flex:0 0 auto;min-width:62px;white-space:nowrap;font-size:1.02rem;line-height:1.85;
  background:var(--surface-2);border:1px solid var(--line);border-radius:999px;
  padding:0 12px;font-variant-numeric:tabular-nums}
.ev-roles .rl.zero{color:var(--faint);opacity:.6}

.tw-fiche{display:flex;align-items:center;gap:14px;padding:14px}
.tw-ava{width:64px;height:64px;border-radius:50%;flex:none;object-fit:cover;
  border:1px solid var(--line-strong)}
.tw-fiche .tw-txt{padding:0;min-width:0}
.tw-chaine{margin:0 0 4px;font-size:.76rem;font-weight:700;color:var(--red-text);
  text-transform:uppercase;letter-spacing:.06em}
/* Le lecteur ne remplace la vignette qu'au clic (voir `_twitch_section`). */
.tw-player{width:100%;aspect-ratio:16/9;border:0;border-radius:var(--r);display:block}
.tw-play{position:absolute;inset:0;display:grid;place-items:center;
  background:linear-gradient(180deg,rgba(7,8,11,.15),rgba(7,8,11,.6));border:0;
  color:var(--text);cursor:pointer;font-size:2.4rem}
@media(max-width:640px){
  .tw-grid,.tw-skel{grid-template-columns:1fr}
}
/* Sur mobile la carte s'empile : la rangée de rôles passe sous le titre et
   s'étale sur toute la largeur, plutôt que d'être écrasée en bout de ligne. */
@media(max-width:820px){
  .ev{grid-template-columns:1fr}
  .ev-side{justify-self:start;flex-wrap:wrap}
  .ev-roles .rl{min-width:46px}
}
@media(max-width:900px){
  .tw-direct{grid-template-columns:1fr}
}

/* --- Les actions de « Mon profil » ---
   ⚠️ **Une barre de raccourcis, pas un tableau de bord** (Kevin, 2026-07-27 :
   « je ne veux pas que le panel soit énorme, volumineux… plus simpliste,
   pratique, intuitif »). Les formulaires sont partis sur leur propre page ; il
   ne reste ici que la porte d'entrée, en deux lignes de texte. */
.pan{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:12px;margin:0 0 clamp(24px,3vw,36px)}
.pan-tuile{display:block;background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--red);border-radius:var(--r-sm);padding:12px 16px;
  text-decoration:none;color:inherit;
  transition:transform var(--t-fast),background var(--t-fast)}
.pan-tuile:hover{transform:translateY(var(--lift));background:var(--surface-2)}
.pan-t{display:block;font-weight:700;font-size:.98rem}
.pan-s{display:block;margin-top:2px;font-size:.8rem;color:var(--muted)}
/* 🐞 La tuile « Synchro » est un bouton d'envoi enveloppé : l'enveloppe était la
   case de la grille, et le bouton flottait dedans, centré, pendant que les deux
   autres tuiles étaient alignées à gauche (vu à l'écran le 2026-07-28).
   `display:contents` retire l'enveloppe de la mise en page : c'est le bouton
   lui-même qui devient la case, comme les liens voisins.
   ⚠️ Ne pas écrire de balise HTML dans ce commentaire : la feuille de style part
   avec chaque page, et un test vérifie qu'aucun formulaire n'apparaît sur les
   pages publiques — il attrapait le commentaire. */
.pan-form{display:contents}
.pan-tuile{width:100%;text-align:left;font:inherit;cursor:pointer}
/* --- Les catégories repliables de « Mon profil » ---
   Même composant que les fiches de perso : `<details>` natif, donc utilisable
   sans JavaScript et au clavier. Le titre porte le compte quand il y en a un
   (« Mes personnages 3 ») : on sait ce qu'il y a dedans avant d'ouvrir. */
.cat{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  margin-bottom:14px;overflow:hidden}
.cat > summary{display:flex;align-items:center;gap:10px;cursor:pointer;
  padding:14px 18px;list-style:none;background:var(--surface-2);
  transition:background var(--t-fast)}
.cat > summary::-webkit-details-marker{display:none}
.cat > summary:hover{background:var(--surface-3)}
/* Le chevron est écrit en CSS : rien à charger, et il tourne à l'ouverture. */
.cat > summary:after{content:"⌄";margin-left:auto;color:var(--muted);
  font-size:1.1rem;line-height:1;transition:transform var(--t-fast)}
.cat[open] > summary:after{transform:rotate(180deg)}
.cat-t{font-family:var(--font-title);font-weight:700;font-size:1.05rem;
  text-transform:uppercase;letter-spacing:.04em}
.cat-n{background:var(--surface);border:1px solid var(--line);border-radius:999px;
  padding:0 9px;font-size:.78rem;color:var(--muted);font-variant-numeric:tabular-nums}
.cat-in{padding:16px 18px}
.cat-in .pan{margin:0}

/* La semaine à cocher. Trois états qui s'excluent, donc des radios — et la
   couleur n'est PAS seule à parler : le mot est écrit dans chaque bouton. */
.dispos{display:grid;gap:8px}
.dj{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.dj-n{width:96px;flex:none;font-size:.9rem;color:var(--muted)}
.dj-o{display:inline-flex;align-items:center;cursor:pointer}
.dj-o input{position:absolute;opacity:0;width:0;height:0}
.dj-o span{display:inline-block;border:1px solid var(--line);background:var(--surface-2);
  border-radius:999px;padding:4px 12px;font-size:.82rem;color:var(--muted);
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast)}
.dj-o + .dj-o{margin-left:6px}
.dj-o input:focus-visible + span{outline:2px solid var(--red-text);outline-offset:2px}
.dj-o.oui input:checked + span{background:rgba(47,163,107,.18);border-color:var(--ok);
  color:#8FE3B8}
.dj-o.peut input:checked + span{background:rgba(217,138,43,.18);border-color:var(--warn);
  color:#F0C089}
.dj-o.non input:checked + span{background:var(--red-soft);border-color:var(--red-line);
  color:var(--red-text)}
.dispos .btn{margin-top:8px;justify-self:start}
@media(max-width:520px){.dj-n{width:100%}}

/* Les actions d'une fiche, sous elle : un geste, à l'endroit où on le pense. */
.char-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;
  padding:10px 14px 14px}
.prof-form{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:clamp(16px,2vw,24px)}
/* Sur sa propre page, le formulaire ne s'étale pas sur 1240 px : un champ de
   saisie large n'est pas plus facile à remplir, il est plus dur à viser. */
.prof-form.solo{max-width:560px}
.prof-form h3{margin:0 0 14px;font-size:1.05rem}
.prof-h3{margin:20px 0 10px;font-size:.95rem;color:var(--muted)}
.prof-form .f{display:grid;gap:6px;margin-bottom:12px}
.prof-form label{font-size:.8rem;color:var(--muted)}
.prof-form input,.prof-form select{background:var(--surface-2);color:var(--text);
  border:1px solid var(--line);border-radius:var(--r-sm);
  height:var(--field-h);padding:0 12px;
  font:inherit;font-size:.92rem;width:100%}
/* ⚠️ **`:not(:focus-visible)` et non `:focus` tout court.** Ces trois règles
   posaient `outline:none` sur `:focus` : au **clavier** aussi, le contour
   disparaissait, et il ne restait qu'un changement de **couleur** de bordure
   pour dire où l'on se trouve. C'est précisément ce que la règle du site
   interdit — ne jamais faire porter une information par la seule couleur — et
   ça touchait les champs qui **écrivent** (s'inscrire à une soirée, changer de
   spé, rejoindre une clé). Avec `:not(:focus-visible)`, le contour ne saute
   qu'au clic souris ; au clavier, le `:focus-visible` global reprend la main. */
.prof-form input:focus,.prof-form select:focus{border-color:var(--red-line)}
.prof-form input:focus:not(:focus-visible),
.prof-form select:focus:not(:focus-visible){outline:none}
.prof-mains{display:flex;flex-wrap:wrap;gap:8px}
/* ⛔ La règle `.btn.small` qui vivait ici a été SUPPRIMÉE le 2026-07-28, même
   motif que `.btn.tiny` : la taille des boutons se décide dans `design.py`, une
   fois, pour les deux façades. */
/* Changer la spé : le menu et son bouton sur une même ligne, alignés avec les
   autres actions de la fiche. */
.char-spec-form{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
/* Les actions de la barre d'un personnage, regroupees (Kevin, 2026-07-28 :
   « ca rassemble les boutons options du perso aux memes endroits »).
   ATTENTION dans la barre le menu de spe doit rester COMPACT : il partage la
   ligne avec le nom, la classe, le royaume, l'ilvl et le score. */
.fold-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  justify-content:flex-end}
.fold-actions .char-spec-form select{max-width:11rem;font-size:.78rem;
  height:var(--btn-h-sm)}
.fold-actions .char-spec-form .btn{white-space:nowrap}
.char-spec-form select{background:var(--surface-2);color:var(--text);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:0 10px;
  height:var(--field-h);font:inherit;font-size:.82rem}
.char-spec-form select:focus{border-color:var(--red-line)}
.char-spec-form select:focus:not(:focus-visible){outline:none}
/* ⚠️ Le retrait est REPLIÉ et discret : c'est irréversible, ça ne doit pas se
   cliquer par mégarde ni attirer l'œil autant qu'une action courante. Le rouge
   n'arrive qu'une fois le pli ouvert, sur le bouton qui agit vraiment. */
.char-danger{margin:0 14px 14px;border-top:1px solid var(--line);padding-top:10px}
.char-danger > summary{cursor:pointer;list-style:none;color:var(--faint);
  font-size:.8rem}
.char-danger > summary:hover{color:var(--red-text)}
.char-danger > summary::-webkit-details-marker{display:none}
/* Cette phrase-là dit ce qu'on va perdre en supprimant un personnage : c'est du
   texte courant, pas une mention — 14 px au minimum. */
.char-danger p{color:var(--muted);font-size:.875rem;margin:10px 0}
.btn.danger{background:var(--red);border-color:var(--red);color:#fff}
.btn.danger:hover{background:var(--red-dark);border-color:var(--red-dark)}

/* ================== Mon QG : le tableau de bord du membre ==================
   Refondu le 2026-07-27 au soir sur le retour de Kevin (« le panneau devient
   énorme et pas ergonomique »). La version d'avant empilait sept cadres de même
   poids sur 2 210 px : rien ne se détachait.

   **Trois principes** tiennent toute cette feuille :
   1. **une seule chose en avant** — la prochaine sortie ; le reste se range
      autour, en colonne principale et en rail latéral ;
   2. **des espacements sur une trame de 8 px** (8 · 16 · 24 · 32) : les
      valeurs libres d'avant faisaient vibrer les alignements d'une carte à
      l'autre ;
   3. **le rouge ne décore pas** — appel à l'action, non-lu, manque. Le doré
      reste au classement, la couleur de classe au nom d'un personnage. */
.qg-page{max-width:1320px;margin:0 auto;padding:clamp(24px,3.5vw,40px) 20px}

/* --- A. L'en-tête compact ---
   ⚠️⚠️ **Refonte du 2026-07-28 (maquette E, retenue par Kevin).** Le bandeau
   précédent mesurait **482 px** : identité, cinq chiffres, panel d'actions et
   toute la carte « Ma progression » empilés avant la première information
   utile. Il devient une **barre d'identité de ~120 px** :
   à gauche qui je suis, à droite mes trois chiffres, et **un seul bouton** qui
   ouvre le menu des actions. Le reste redescend dans les cartes, où chaque
   information a désormais un endroit et un seul.

   La lueur rouge et le loup en filigrane sont **partis avec le bandeau** :
   c'étaient des effets de héros, et cette barre n'en est plus un. */
.qg-tete{display:flex;align-items:center;gap:20px;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:14px clamp(14px,2vw,20px);margin-bottom:16px}
.qg-tete-id{display:flex;align-items:center;gap:14px;min-width:0}
.qg-ava{width:56px;height:56px;flex:none;border-radius:50%;
  border:2px solid var(--red-line);object-fit:cover;background:var(--surface-2)}
.qg-ava-txt{display:grid;place-items:center;font-family:var(--font-title);
  font-size:1.25rem;font-weight:700;color:var(--muted)}
.qg-nom{margin:0;font-family:var(--font-title);font-size:clamp(1.2rem,2.4vw,1.5rem);
  line-height:1.1;font-weight:700;text-transform:uppercase;
  letter-spacing:var(--track-title)}
/* Le personnage principal sous le pseudo : classe, spé et royaume tiennent en
   une ligne grise, là où le bandeau leur donnait un paragraphe à part. */
.qg-tete-sous{margin:3px 0 0;color:var(--muted);font-size:.85rem}
.qg-badges{display:flex;flex-wrap:wrap;gap:6px;margin:7px 0 0}
/* Les trois chiffres : ce sont les valeurs **actuelles**, et elles ne sont
   répétées nulle part ailleurs — « Ma progression » ne montre que ce que ces
   chiffres ne peuvent pas dire (l'évolution, le rang, la meilleure clé). */
.qg-tete-chiffres{margin-left:auto;display:flex;align-items:stretch}
.qg-tete-chiffres > div{padding:0 clamp(12px,1.6vw,22px);text-align:center;
  border-left:1px solid var(--line)}
.qg-tete-chiffres > div:first-child{border-left:0}
.qg-tete-chiffres b{display:block;font-family:var(--font-title);
  font-size:clamp(1.25rem,2.2vw,1.6rem);line-height:1.1;
  font-variant-numeric:tabular-nums}
/* 0,75 rem = 12 px pile : le plancher du site, verrouillé par `test_lisibilite`. */
.qg-tete-chiffres span{display:block;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.05em;margin-top:2px}
/* --- Le menu « Action rapide » ---
   ⚠️ **Un `<details>`, donc aucun JavaScript** : il s'ouvre au clic et au
   clavier, se ferme à Échap, et fonctionne script bloqué. Un menu maison aurait
   demandé du JS, des `aria-expanded` à tenir et une gestion du clic extérieur.
   ⚠️ Il remplace le panel de cinq pastilles : Kevin a fait retirer
   « Mes disponibilités », « Événements à venir » et « Ajouter un personnage »
   parce que les trois sont déjà dans leur carte. Repliés dans un menu, ils ne
   coûtent plus une ligne de page — et l'action principale garde le rouge. */
.qg-menu{position:relative;flex:none}
.qg-menu > summary{list-style:none;cursor:pointer}
.qg-menu > summary::-webkit-details-marker{display:none}
.qg-menu ul{position:absolute;right:0;top:calc(var(--btn-h) + 6px);z-index:30;
  list-style:none;margin:0;padding:6px;min-width:240px;
  background:var(--surface-2);border:1px solid var(--line-strong);
  border-radius:var(--r-sm);box-shadow:var(--shadow);display:grid;gap:2px}
.qg-menu li a{display:flex;align-items:center;gap:10px;
  min-height:var(--btn-h-sm);padding:0 10px;border-radius:6px;font-size:.9rem}
.qg-menu li a:hover{background:var(--surface-3)}
@media(max-width:620px){
  /* Sur un téléphone la barre s'empile : le menu prend alors la largeur, et
     s'ouvre **dans le flux** plutôt qu'en surimpression au-dessus du contenu. */
  .qg-tete{gap:14px}
  .qg-tete-chiffres{margin-left:0;width:100%}
  .qg-tete-chiffres > div{flex:1}
  .qg-menu{width:100%}
  .qg-menu > summary{width:100%}
  .qg-menu ul{position:static;width:100%;margin-top:8px}
}
/* ⚠️ `min-height:36px` : mesuré à **15 px** le 2026-07-28 dans la nouvelle
   carte de progression. C'est un lien de service isolé au bas d'une carte, donc
   une vraie cible tactile — la hauteur vient d'un remplissage, le texte ne
   bouge pas. Même correctif que les « Tout voir » (`.qg-plus`). */
.qg-prog-lien{margin:12px 0 0;font-size:.8rem}
.qg-prog-lien a{display:inline-flex;align-items:center;min-height:36px;
  color:var(--muted);text-decoration:underline}
.qg-prog-lien a:hover{color:var(--red-text)}
/* Un badge de grade : l'icône que Kevin a posée sur le rôle Discord, **et** le
   mot. L'icône seule ne dirait rien à un lecteur d'écran ni à un nouveau venu —
   même règle que la classe, écrite sous le nom d'un personnage. */
.qg-badge{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;
  border-radius:999px;background:var(--surface-2);
  border:1px solid var(--line);font-size:.78rem;font-weight:600;
  letter-spacing:.04em;color:var(--muted)}
.qg-badge-ic{width:20px;height:20px;display:block;object-fit:contain}
/* Les mêmes badges sur la fiche publique d'un membre (armurerie). */
.grades{display:flex;flex-wrap:wrap;gap:8px;margin:-8px 0 24px}
/* Le staff est le seul grade qui change ce qu'on peut FAIRE sur le site : il
   est le seul à porter le rouge. Les autres se distinguent sans crier. */
.qg-badge.staff{border-color:var(--red-line);color:var(--red-text)}
.qg-badge.membre{color:var(--text)}
.qg-badge.principal{border-color:var(--gold);color:var(--gold)}
/* ⛔ **SUPPRIMÉ le 2026-07-28 avec le bandeau** : les règles qui habillaient la
   ligne d'actions du héros, le paragraphe « Personnage principal : » et sa
   rangée de chiffres. Trois blocs que l'en-tête compact absorbe désormais dans
   sa seule barre ; aucun HTML ne les produit plus.
   ⚠️⚠️ **Aucun nom de classe supprimée n'est écrit ici, et c'est délibéré** :
   la feuille part avec CHAQUE page, donc un nom cité dans un commentaire reste
   cherchable et répond « oui, la règle existe encore » à qui la cherche. Le
   projet a payé ce piège quatre fois. On décrit en français, jamais en CSS. */

/* --- La barre d'état ---
   ⚠️⚠️ **Elle n'apparaît que si elle a quelque chose à signaler.** Une barre qui
   dit toujours « tout va bien » devient un décor qu'on cesse de lire au bout de
   trois visites, et elle aura alors coûté 34 px à chaque page. Quand rien ne
   cloche, elle n'existe pas. */
.qg-etats{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.qg-etats span{display:inline-flex;align-items:center;gap:7px;min-height:32px;
  padding:0 12px;border-radius:999px;font-size:.82rem;
  background:var(--surface);border:1px solid var(--line);color:var(--muted)}
/* ⚠️ L'icône ne porte jamais l'information seule : le mot est écrit à côté.
   La couleur ne fait que **renforcer** ce que la phrase dit déjà. */
.qg-etats .att{color:var(--warn);border-color:rgba(217,138,43,.35)}

/* --- Mes priorités ---
   ⚠️ **Trois niveaux, et ils se voient.** Quatre lignes de même poids ne sont
   pas des priorités, c'est une liste. Le liseré gauche porte le rang : rouge
   pour ce qui bloque, orange pour la semaine, gris pour le facultatif. Le mot
   (« Urgent », « Cette semaine », « Optionnel ») est écrit à droite — la
   couleur seule ne dirait rien à un daltonien. */
.qg-prio{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.qg-prio a{display:flex;align-items:center;gap:12px;padding:9px 12px;
  border-radius:var(--r-sm);background:var(--surface-2);color:inherit;
  border:1px solid var(--line);border-left:3px solid var(--line-strong);
  transition:background var(--t-fast),border-color var(--t-fast)}
.qg-prio a:hover{background:var(--surface-3);border-color:var(--line-strong)}
.qg-prio .n1 a{border-left-color:var(--red);background:var(--red-soft)}
.qg-prio .n2 a{border-left-color:var(--warn)}
.qg-prio .n3 a{border-left-color:var(--line-strong)}
.qg-prio-ic{font-size:1.05rem;line-height:1;flex:none}
.qg-prio b{display:block;font-size:.9rem}
.qg-prio em{display:block;font-style:normal;color:var(--muted);font-size:.8rem}
.qg-prio-q{margin-left:auto;font-size:.75rem;text-transform:uppercase;
  letter-spacing:.05em;color:var(--muted);white-space:nowrap}
.qg-prio .n1 .qg-prio-q{color:var(--red-text)}
.qg-prio .n2 .qg-prio-q{color:var(--warn)}

/* --- Ma progression, en carte ---
   ⚠️ Elle **ne répète pas** les trois chiffres de l'en-tête : elle dit ce qu'ils
   ne peuvent pas dire — l'évolution, la meilleure clé, le rang, la synchro. */
.qg-prog-lignes{display:grid;margin-top:14px}
.qg-prog-ligne{display:flex;align-items:baseline;gap:12px;margin:0;
  padding:8px 0;border-top:1px solid var(--line);font-size:.86rem}
.qg-prog-ligne > span{color:var(--muted)}
.qg-prog-ligne > b{margin-left:auto;font-variant-numeric:tabular-nums;
  text-align:right}

/* --- Trois personnages en cartes ---
   ⚠️ Trois au plus dans l'aperçu, la liste complète est l'onglet : à sept
   personnages, le résumé devenait la moitié de la page. */
.qg-pcards{display:grid;gap:12px;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.qg-pcard{display:grid;gap:8px;padding:12px;border-radius:var(--r-sm);
  background:var(--surface-2);border:1px solid var(--line);color:inherit;
  transition:border-color var(--t-fast),background var(--t-fast)}
.qg-pcard:hover{border-color:var(--red-line);background:var(--surface-3)}
.qg-pcard-t{display:flex;align-items:center;gap:9px;min-width:0}
.qg-pcard-t b{font-size:.98rem;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.qg-pcard-s{color:var(--muted);font-size:.8rem;margin:0}
.qg-pcard-n{display:flex;gap:18px;font-variant-numeric:tabular-nums}
.qg-pcard-n b{display:block;font-size:1.05rem}
.qg-pcard-n span span{color:var(--muted);font-size:.75rem}

/* --- Le fil d'activité, groupé par jour ---
   ⚠️ **Un fil vertical, pas un carrousel** : cinq cartes qui défilent
   horizontalement cachent quatre lignes sur cinq et ne se lisent pas au
   clavier. */
.qg-fil{margin:0;padding:0}
.qg-fil dt{font-size:.75rem;text-transform:uppercase;letter-spacing:.07em;
  color:var(--muted);font-weight:700;margin:14px 0 6px}
.qg-fil dt:first-child{margin-top:0}
.qg-fil dd{margin:0 0 4px;display:flex;align-items:center;gap:10px;
  padding:8px 10px;border-radius:6px;background:var(--surface-2);
  font-size:.88rem}
.qg-fil dd .qg-fil-h{margin-left:auto;color:var(--faint);font-size:.8rem;
  font-variant-numeric:tabular-nums;white-space:nowrap}

/* --- La sous-navigation --- */
/* ⚠️ **Des ancres, pas des onglets.** Kevin a déjà refusé le masquage sur la
   fiche de perso (« je préférais l'affichage juste avant »), et un test
   verrouille ce refus. Ici on fait défiler : rien n'est caché.
   `scroll-margin-top` sur les sections, sinon la barre collante du site
   recouvre le titre où l'on vient d'atterrir. */
/* ⚠️ `scroll-margin-top` : sans lui, `scrollIntoView` amène la barre d'onglets
   **sous** la barre collante du site, qui lui mange 76 px. */
.qg-nav{position:sticky;top:var(--nav-h);scroll-margin-top:var(--nav-h);
  z-index:20;margin:0 -20px 16px;
  padding:0 20px;background:rgba(7,8,11,.92);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line)}
.qg-nav ul{list-style:none;display:flex;gap:24px;margin:0;padding:0;
  overflow-x:auto;scrollbar-width:none}
.qg-nav ul::-webkit-scrollbar{display:none}
.qg-nav a{display:block;padding:12px 0;font-size:.88rem;font-weight:600;
  color:var(--muted);white-space:nowrap;border-bottom:2px solid transparent;
  transition:color var(--t-fast),border-color var(--t-fast)}
.qg-nav a:hover{color:var(--text);border-bottom-color:var(--red)}

/* --- Les onglets ---
   ⚠️ **Le CSS ne cache RIEN** : c'est le script qui pose `hidden` sur les
   panneaux. Sans JavaScript, les cinq s'affichent à la suite et les liens
   redeviennent des ancres — aucun contenu ne devient inatteignable. */
.qg-nav a[aria-selected="true"]{color:var(--text);border-bottom-color:var(--red)}
.qg-panneau[hidden]{display:none}
.qg-panneau + .qg-panneau{margin-top:24px}

/* --- B. Actions rapides ---
   ⚠️ **Trois niveaux, un seul rouge.** Cinq tuiles identiques ne hiérarchisent
   rien : l'action principale prend toute la largeur, les deux courantes se
   partagent la ligne suivante, les deux secondaires sont plus petites et sans
   description — on ne les cherche qu'une fois par mois. */
/* ⛔ **Les règles de grille des anciennes tuiles ont été SUPPRIMÉES** le
   2026-07-28 (3ᵉ passe). Elles habillaient les trois familles de tuiles de
   raccourcis, que le HTML ne produit plus. (Leurs noms ne sont **pas** écrits
   ici : voir le test des fantômes de la feuille.)
   🐞 **Et elles n'étaient pas inoffensives** : l'ancienne grille de trois
   colonnes posée sur le conteneur survivait à la refonte et rangeait le titre
   et la liste dans deux de ses cellules — les cinq pastilles tombaient alors
   **une par ligne** (mesuré : 240 px de haut,
   soit plus que les tuiles qu'elles remplaçaient). Le style vit plus bas,
   section « panel d'actions rapides ». */

/* --- Les outils de l'onglet Personnages (recherche, filtres, tri) --- */
.persos-outils{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}
.persos-outils input,.persos-outils select{height:var(--field-h);padding:0 12px;
  background:var(--surface-2);color:var(--text);border:1px solid var(--line);
  border-radius:var(--r-sm);font:inherit;font-size:.9rem}
.persos-outils input{flex:1;min-width:200px}
.persos-outils input:focus-visible,.persos-outils select:focus-visible{
  outline:2px solid var(--red-line);outline-offset:1px}
.liste-persos .char-fold[hidden]{display:none}

/* ⛔ **La liste de personnages de la vue d'ensemble a été SUPPRIMÉE** le
   2026-07-28 : elle est devenue une rangée de trois cartes (maquette E). Ses
   règles — la liste, la ligne pleine largeur cliquable, sa méta, ses chiffres
   et son chevron — n'habillaient plus aucun HTML. La fonction Python qui les
   produisait est partie avec elles.
   ⚠️ Le geste, lui, a survécu : cliquer une carte bascule sur l'onglet
   Personnages **puis** déplie ce personnage. C'était la demande de Kevin, elle
   n'a pas été perdue dans le déménagement. */

/* ⛔⛔ **LE PANEL DE PASTILLES A ÉTÉ SUPPRIMÉ le 2026-07-28** (maquette E).
   Son cadre, son surtitre, sa liste et les deux états de la pastille ne
   décrivaient plus aucun HTML : les cinq raccourcis sont devenus le menu
   « Action rapide » de l'en-tête (voir plus haut, section A).
   ⚠️ **Leurs noms ne sont pas écrits ici** — un nom de classe cité dans un
   commentaire part avec chaque page et fait croire que la règle survit.
   Kevin l'avait d'ailleurs demandé avant la refonte : « enlève Mes dispos,
   Ajouter un perso et Événements à venir » — les trois sont déjà dans leur
   carte. Repliés dans un menu, ils ne coûtent plus une ligne de page.
   ⚠️ C'est cette pastille qui a servi de **repère de taille** pour tout le
   site : sa hauteur vit maintenant dans `--btn-h` (`design.py`), elle ne s'est
   pas perdue avec la classe. */
/* --- C. La grille : DOUZE colonnes ---
   ⚠️⚠️ **Remplace la grille 65/35 le 2026-07-28** (maquette E). L'ancienne était
   une colonne principale et un rail : deux largeurs possibles, donc deux
   familles de cartes, et tout ce qui ne rentrait dans aucune des deux tombait
   en pleine largeur sous la grille. Les douze colonnes donnent quatre rangées
   qui respectent l'ordre de lecture demandé :
   progression 7 / priorités 5 · activité 7 / semaine 5 · personnages 12 ·
   clé 4 / activité récente 8.

   ⚠️ **Deux paliers, pas cinq.** Sous 1100 px toute carte prend la moitié
   (span 6), sous 760 px toute la largeur : une carte de 5/12 sur une tablette
   fait 300 px, où la semaine de dispos et les cartes de personnage se cassent.
   Vérifié à 1440, 1280, 1024, 768 et 390. */
.qg-grille{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
  gap:24px;align-items:start;margin-bottom:24px}
.qg-grille > *{margin:0;min-width:0}
.c4{grid-column:span 4}
.c5{grid-column:span 5}
.c7{grid-column:span 7}
.c8{grid-column:span 8}
.c12{grid-column:span 12}
@media(max-width:1100px){.c4,.c5,.c7,.c8{grid-column:span 6}}
@media(max-width:760px){.c4,.c5,.c7,.c8{grid-column:span 12}}

/* --- Le cadre commun d'une section --- */
.qg-sec{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:16px clamp(16px,2vw,24px) 24px;
  scroll-margin-top:calc(var(--nav-h) + 56px)}
.qg-sec-tete{display:flex;align-items:baseline;gap:16px;margin-bottom:16px}
.qg-sec-tete h2{margin:0;font-size:1.05rem;letter-spacing:.02em}
/* ⚠️ `min-height:36px` : mesurés à **20 px** dans le navigateur (2026-07-28),
   très en dessous de la barre du site. Ce sont de petits liens de service
   (« Tout voir »), mais sur un téléphone une cible de 20 px se rate. La hauteur
   vient d'un remplissage vertical, donc **rien ne bouge visuellement** : le
   texte garde sa taille et sa place, la zone cliquable seule grandit. */
.qg-plus{margin-left:auto;font-size:.8rem;color:var(--muted);font-weight:400;
  display:inline-flex;align-items:center;min-height:36px}
a.qg-plus:hover{color:var(--red-text)}
/* La section « prochaine activité » est la seule à porter un liseré rouge :
   c'est elle qu'on vient lire. */
.qg-sec-fort{border-left:3px solid var(--red)}
/* Les grandes sections hors grille s'espacent comme la grille elle-même. */
#persos,#dispos,#activite,#compte{margin-bottom:24px}

/* --- Ma prochaine activité --- */
.qg-next-head h3{margin:4px 0 0;font-size:clamp(1.15rem,2vw,1.5rem);line-height:1.2}
.qg-next-meta{display:flex;flex-wrap:wrap;gap:16px;margin-top:8px;
  font-size:.85rem;color:var(--muted)}
.qg-quand{color:var(--text);font-weight:600}
.qg-roles{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.qg-role{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;
  border-radius:999px;background:var(--surface-2);border:1px solid var(--line);
  font-size:.85rem;font-variant-numeric:tabular-nums}
/* Un rôle à zéro est précisément ce qui fait venir quelqu'un : on le signale
   au lieu de le noyer avec les autres. */
.qg-role.vide{color:var(--red-text);border-color:var(--red-line)}
.qg-next-pied{display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  margin-top:24px;padding-top:16px;border-top:1px solid var(--line)}
.qg-etat{font-size:.85rem;color:var(--muted)}
.qg-etat.ok{color:var(--ok)}
.qg-etat.peut{color:var(--gold)}
.qg-etat.non{color:var(--faint)}
.qg-mine{font-size:.85rem;color:var(--muted)}
.qg-next-pied .cta-row{margin-left:auto;margin-top:0}

/* --- Mes sorties à venir --- */
.qg-minis{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:8px}
.qg-mini{display:grid;gap:4px;padding:12px 16px;border-radius:var(--r-sm);
  background:var(--surface-2);border:1px solid transparent;
  transition:border-color var(--t-fast),transform var(--t-fast)}
.qg-mini:hover{border-color:var(--red-line);transform:translateY(var(--lift))}
.qg-mini .qg-quand{font-size:.75rem;color:var(--muted);font-weight:400}
.qg-mini .qg-roles{margin-top:8px}
.qg-mini .qg-role{padding:2px 8px;font-size:.78rem}

/* --- Clés M+ --- */
.qg-cles{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.qg-cles a{display:flex;align-items:center;gap:16px;padding:12px;
  border-radius:var(--r-sm);background:var(--surface-2);
  border:1px solid transparent;transition:border-color var(--t-fast)}
.qg-cles a:hover{border-color:var(--red-line)}
/* Le niveau en gros : c'est la première chose qu'on cherche sur une clé. */
.qg-lvl{flex:none;min-width:56px;padding:8px;text-align:center;
  border-radius:var(--r-sm);background:var(--red-soft);
  border:1px solid var(--red-line);font-family:var(--font-title);
  font-weight:700;font-size:1.1rem;color:#fff}
.qg-cle-corps{display:grid;gap:2px;min-width:0}
.qg-cle-corps strong{font-size:.95rem}
.qg-cle-meta{font-size:.78rem;color:var(--muted)}
.qg-cle-meta .manque{color:var(--red-text)}

/* --- Ma progression --- */
.qg-chiffres{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;text-align:center}
.qg-chiffres b{display:block;font-family:var(--font-title);font-size:1.6rem;line-height:1.1}
.qg-chiffres span{font-size:.78rem;color:var(--muted);letter-spacing:.04em}
.qg-prog-perso{margin:16px 0 0;text-align:center}
.qg-prog-plus{margin:16px 0 0;display:grid;gap:8px;font-size:.85rem}
.qg-prog-plus > div{display:flex;gap:16px;padding-top:8px;
  border-top:1px solid var(--line)}
.qg-prog-plus dt{color:var(--muted)}
.qg-prog-plus dd{margin:0 0 0 auto;font-weight:600;text-align:right}
/* --- La tendance : est-ce que ça monte ? ---
   ⚠️ La couleur ne porte JAMAIS l'information seule : la phrase dit « +120 en
   7 jours » et la flèche double le signe. Le vert et le rouge ne font que
   souligner — ils ne disent rien à un daltonien ni à un lecteur d'écran. */
.tendance{display:inline-flex;align-items:center;gap:4px;font-size:.75rem;
  font-weight:600;color:var(--muted);white-space:nowrap}
/* ⚠️ En `rem`, pas en `em` : dans un bloc déjà réduit (`.qg-chiffres`, un pied
   de fiche), une flèche en `em` retombait à 10 px. Un `rem` la tient au même
   plancher partout, quel que soit son voisinage. */
.tendance i{font-style:normal;font-size:.75rem;line-height:1}
.tendance.monte{color:var(--ok)}
.tendance.baisse{color:var(--red-text)}
.tendance.plat{color:var(--faint)}
.qg-chiffres .tendance{margin-top:4px}
/* --- La barre de progression de raid ---
   « 6/8 » se lit, une barre aux trois quarts pleine se **voit**. ⚠️ Le chiffre
   et la difficulté restent écrits dessous : une barre seule ne dit ni combien
   de boss ni à quelle difficulté, et ne dit rien du tout à un lecteur d'écran
   (d'où le `role="img"` et son libellé complet côté HTML). */
.barre-raid{position:relative;height:8px;margin-top:16px;border-radius:999px;
  background:var(--surface-2);border:1px solid var(--line);overflow:hidden}
.barre-raid > span{display:block;height:100%;
  background:linear-gradient(90deg,var(--red-dark),var(--red))}
.barre-legende{margin:8px 0 0;font-size:.78rem;color:var(--muted);text-align:center}
.barre-legende b{color:var(--text)}
/* --- La fiche en deux colonnes : les tableaux, puis l'évolution ---
   Les clés et le raid n'ont pas besoin de 1200 px ; l'évolution, elle, se lit
   mieux dans une colonne étroite à côté que sous un tableau qu'on vient de
   parcourir. Sous 900 px, tout s'empile dans l'ordre de lecture. */
/* 380 px (et non 300) : la courbe doit occuper la place disponible, pas s'y
   serrer (Kevin, 2026-07-27 : « agrandis un peu les graphiques, qu'ils prennent
   la largeur de la fiche perso »). Les tableaux gardent largement leur compte —
   mesuré à 1440 : 783 px pour les clés, ce qui reste sans coupure. */
/* ⚠️ **Deux colonnes PROPORTIONNELLES, plus une largeur figée.** Elle valait
   380 px ; Kevin a dessiné le cadre qu'il voulait le 2026-07-28, et il occupe
   un peu plus de la moitié de la fiche. Une valeur fixe donnait un graphe qui
   *rétrécissait* à mesure qu'on élargissait l'écran — l'inverse de ce qu'on
   attend d'un graphique.
   ⚠️ `align-items:stretch` (et non `start`) : le panneau prend **toute la
   hauteur de sa zone**, donc celle du tableau d'en face. C'est ce qui rend le
   graphe aussi haut que ce qu'il commente. Le plancher de 340 px protège la
   lisibilité des graduations sur un écran étroit. */
.char-corps{display:grid;grid-template-columns:minmax(0,0.9fr) minmax(340px,1.1fr);
  gap:clamp(16px,2vw,32px);align-items:stretch}
.char-tables{min-width:0}
/* ATTENTION plus de `position:sticky` depuis le 2026-07-28. Le panneau colle
   maintenant a SA zone (chaque categorie a le sien) : une colle verticale le
   ferait glisser hors de la rangee qu'il commente, et il irait commenter la
   suivante. Il tient sa place, c'est tout ce qu'on lui demande. */
.char-cote{display:flex;flex-direction:column;gap:16px;min-height:0;
  padding:16px;border-radius:var(--r-sm);background:var(--surface-2);
  border:1px solid var(--line)}
.char-cote .barre-raid{margin-top:0}
/* Une barre par raid (Kevin, 2026-07-28 : « ici je veux voir les barres de
   progression de tous les raids »). ATTENTION la legende vient AU-DESSUS de sa
   barre : sous elle, on lisait la barre du raid suivant en croyant lire celle
   qu'on venait de nommer. */
.barres-raids{display:flex;flex-direction:column;gap:14px}
.barre-bloc .barre-legende{margin:0 0 6px;text-align:left}
/* « CLEAN » : le raid est fini. Kevin, 2026-07-29 : « inscris juste CLEAN en
   gros en rouge NRV ».
   ATTENTION c'est du TEXTE, pas une image : rien a charger, net a toute taille,
   et un lecteur d'ecran l'entend. La barre pleine seule ne suffisait pas — a
   distance, 100 % et 95 % se ressemblent, le mot non. */
.clean{font-family:var(--font-title);font-weight:700;font-size:1.05rem;
  letter-spacing:.14em;color:var(--red-text);margin-left:8px;
  text-shadow:0 0 14px rgba(210,10,46,.55)}
/* Le liseré de la barre suit : un raid termine se repere aussi du coin de
   l'oeil, sans lire. */
.barre-bloc.clean .barre-raid{box-shadow:0 0 0 1px var(--red-line)}
.char-cote .courbe-cadre{margin:0;padding:0;border:0}
/* Plus haute ici qu'en pied de bloc : c'est la seule chose que cette colonne a
   à montrer, autant qu'elle se lise. Une sparkline écrasée à 24 px transforme
   +250 points en un frémissement. */
.char-cote .courbe{height:132px}
.char-cote .courbe-echelle{display:flex;justify-content:space-between;
  margin-top:4px;font-size:.78rem;color:var(--faint);
  font-variant-numeric:tabular-nums}
/* Dans le rail du QG, la même courbe reste plus basse : la colonne y porte
   aussi les chiffres et le rang, elle ne peut pas s'offrir 132 px. */
.courbe-echelle{display:flex;justify-content:space-between;margin-top:4px;
  font-size:.78rem;color:var(--faint);font-variant-numeric:tabular-nums}
@media(max-width:900px){
  .char-corps{grid-template-columns:1fr}
  .char-cote{position:static}
}
/* La courbe : un SVG écrit à la main, 12 points au plus. Elle **répète** le
   chiffre — d'où `aria-hidden` côté HTML, et une couleur discrète ici. */
.courbe{display:block;color:var(--red-text);opacity:.85;width:100%;height:24px}
.courbe-cadre{margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
/* ⚠️ Une légende doit se lire APRÈS la courbe, pas avant elle. Mesurée à
   **17,4 px en blanc plein** (2026-07-28) : elle passait pour un titre. Elle
   était noyée dans une phrase longue tant qu'elle disait « Score M+ · 12
   derniers relevés · ▲ +256 » ; réduite à sa part utile, elle criait. */
.courbe-cadre figcaption{font-size:.8rem;color:var(--muted);margin-bottom:6px}
.qg-perso-tend{margin:8px 0 0;text-align:center}
/* Sur la fiche publique d'un membre, sous les badges de grade. */
.tendances{display:flex;align-items:center;gap:16px;margin:-8px 0 24px;max-width:320px}

/* Ma ligne dans le classement : reconnaissable sans être criarde, et repérable
   autrement que par la couleur (le liseré à gauche). */
.hof-liste li.moi{background:var(--surface-2);border-left:2px solid var(--red);
  margin-left:-8px;padding-left:6px;border-radius:2px}

/* --- D. Mes personnages ---
   ⚠️⚠️ **Une liste pleine largeur, pas des cartes en colonnes.** Les cartes de
   300 px ont été essayées puis retirées le 2026-07-27 : le tableau des clés M+
   se coupait au milieu du niveau (« +1… » pour « +18 »), la progression de raid
   défilait de côté, et trois cartes de hauteurs différentes laissaient de grands
   vides. Une fiche de personnage a besoin de toute la largeur.
   Le composant est celui de l'armurerie (`.char-fold`), déjà validé : le nom en
   couleur de classe, la fiche entière au clic. */
#persos .char-fold{background:var(--surface-2)}
#persos .char-fold:first-of-type{border-color:var(--red-line)}
/* Le bandeau entre le résumé et la fiche : quand la donnée a été relue, si ça
   monte, et où aller voir. */
.qg-perso-pied{display:flex;flex-wrap:wrap;align-items:center;gap:8px 16px;
  margin:0;padding:12px 18px;border-bottom:1px solid var(--line);
  font-size:.78rem;color:var(--faint)}
.qg-synchro{color:var(--muted)}
/* « Jamais synchronisé » explique un score absent : c'est une information, pas
   une erreur — d'où le doré (attention) et non le rouge (problème). */
.qg-synchro.alerte{color:var(--gold)}
.qg-perso-liens{margin-left:auto;display:flex;align-items:center;gap:16px}
/* Idem, et c'est ici que ça compte le plus : ces deux liens se répètent sur
   CHAQUE personnage (14 cibles de 20 px collées les unes aux autres sur une
   fiche à sept persos). `align-items:center` sur le conteneur garde
   l'alignement de la barre inchangé. */
.qg-perso-liens a{color:var(--muted);text-decoration:underline;
  display:inline-flex;align-items:center;min-height:36px}
.qg-perso-liens a:hover{color:var(--red-text)}
/* --- Le main, dans la barre --- */
/* Kevin, 2026-07-27 : « plus pratique d'un coup d'œil de faire un changement de
   main ou de reroll ». Avant, il fallait déplier chaque reroll pour trouver le
   bouton.
   🐞 **Les boutons ne s'alignaient pas** (signalé le 2026-07-27) : le chevron du
   dépliant porte DÉJÀ `margin-left:auto`, et deux marges automatiques dans un
   même flex **se partagent** l'espace libre — chaque bouton se posait donc juste
   après un texte de longueur variable, en escalier. La marge automatique doit
   rester sur **un seul** élément ; le chevron reprend un écart fixe.
   ⚠️ Une largeur minimale identique pour les deux : sans elle, la pastille
   « ★ Principal » et le bouton « Définir principal » caleraient leur bord droit
   au même endroit mais donneraient deux colonnes de largeurs différentes. */
#persos .char-fold > summary .qg-badge,
#persos .char-fold > summary .fold-main{margin-left:auto;flex:none;
  min-width:150px;justify-content:center}
#persos .char-fold > summary:after{margin-left:16px}
#persos .char-fold > summary .fold-main .btn{width:100%}
#persos .char-fold > summary .qg-badge{font-size:.78rem;color:var(--gold);
  border-color:var(--gold)}
/* La méta prend la place qui reste : c'est elle qui doit céder quand le nom est
   long, pas la colonne de droite. */
#persos .char-fold > summary .fold-meta{flex:1;min-width:0}
.fold-main{display:flex}
/* ⛔ La règle `.btn.tiny` qui vivait ici a été SUPPRIMÉE le 2026-07-28 : elle
   redéfinissait une hauteur et une taille de texte que le socle porte désormais
   pour toute la famille (voir `design.py`). Deux endroits qui décident de la
   taille d'un bouton, c'est celui d'en bas qui gagne — et personne ne sait
   lequel c'est. */

/* --- E. Mes disponibilités --- */
/* Lisible d'abord, modifiable ensuite : avant, la semaine était enfermée dans
   un accordéon fermé — il fallait l'ouvrir pour savoir ce qu'on avait déclaré. */
/* 🐞 **`minmax(0,1fr)` et non `1fr`** (2026-07-28). Un `1fr` garde un plancher
   implicite à `min-content` : la case la plus large — « Pas dispo » — imposait
   sa largeur aux sept, et la semaine réclamait **416 px dans une colonne de
   361**. Mesuré en essayant la carte des dispos dans une colonne de tiers de
   page : elle débordait. Avec `minmax(0,1fr)` les cases acceptent de rétrécir et
   le mot passe à la ligne. Vaut aussi pour le téléphone. */
.qg-semaine{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(7,minmax(0,1fr));gap:8px}
.qg-semaine li{display:grid;gap:4px;padding:8px;border-radius:var(--r-sm);
  background:var(--surface-2);border:1px solid var(--line);
  border-top:2px solid var(--line);text-align:center;font-size:.75rem}
.qg-jour{font-weight:700;font-size:.8rem}
/* L'horaire sous les jours où l'on vient (2ᵉ passe : « le créneau horaire »).
   Gris : c'est une précision, pas l'état — l'état est le mot au-dessus. */
.qg-heures{display:block;margin-top:2px;font-size:.75rem;color:var(--faint);
  font-variant-numeric:tabular-nums}
/* --- Le créneau habituel, en tête du formulaire --- */
.dispo-creneau{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:0 0 16px;padding:12px;border-radius:var(--r-sm);
  background:var(--surface-2);border:1px solid var(--line)}
.dispo-creneau-t{font-weight:600;font-size:.88rem;margin-right:8px}
.dispo-creneau input[type=time]{height:var(--field-h);padding:0 10px;background:var(--surface);
  color:var(--text);border:1px solid var(--line);border-radius:var(--r-sm);
  font:inherit;font-size:.9rem}
.dispo-creneau input[type=time]:focus-visible{outline:2px solid var(--red-line);
  outline-offset:1px}
.dispo-creneau .btn{margin-left:auto}
/* Trois états, trois couleurs — **et le mot est écrit dans la case** : la
   couleur seule ne dit rien à un daltonien. */
.qg-semaine li.oui{border-top-color:var(--ok);color:var(--ok)}
.qg-semaine li.peut{border-top-color:var(--gold);color:var(--gold)}
.qg-semaine li.non{border-top-color:var(--red);color:var(--red-text)}
.qg-semaine li.vide{color:var(--faint)}
.qg-dispo-libre{margin:0;color:var(--muted)}
.qg-suite{margin-top:16px}
.qg-suite > summary{cursor:pointer;font-weight:600;font-size:.88rem;
  color:var(--red-text);list-style:none}
.qg-suite > summary::-webkit-details-marker{display:none}
.qg-suite > summary:hover{color:#fff}
.qg-suite[open] > summary{margin-bottom:16px}

/* --- F. Activité récente --- */
.qg-filtres{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}
.notifs{list-style:none;margin:0;padding:0;display:grid}
.notifs li{border-top:1px solid var(--line)}
.notifs li:first-child{border-top:0}
.notifs a,.notifs .no-lien{display:grid;grid-template-columns:auto 1fr auto;
  align-items:baseline;gap:16px;padding:12px 8px;
  transition:background var(--t-fast)}
.notifs a:hover{background:var(--surface-2)}
.notifs .nt-icone{font-size:1rem;line-height:1.3}
.notifs .nt-corps{min-width:0}
.notifs .nt-titre{font-size:.9rem;color:var(--text)}
.notifs .nt-detail{display:block;margin-top:2px;font-size:.78rem;color:var(--muted)}
.notifs .nt-quand{font-size:.75rem;color:var(--faint);white-space:nowrap}
/* ⚠️ Le non-lu se dit **trois fois** : un liseré, un fond à peine plus clair, et
   le mot « Non lu » pour les lecteurs d'écran. Kevin : plus de ligne entièrement
   rouge — le rouge ici ne fait que pointer. */
.notifs li.neuf{border-left:2px solid var(--red);background:rgba(210,10,46,.05)}
.notifs li.neuf .nt-titre{color:#fff;font-weight:600}

/* --- G. Mon compte --- */
/* Les deux sujets du panneau « Compte », côte à côte (Kevin, 2026-07-28 :
   « trop d'espace perdu »). `align-items:start` : chaque bloc se règle sur SON
   contenu — étirés, celui de gauche traînait du vide sous son bouton. */
.compte-duo{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
  gap:clamp(20px,3vw,36px);align-items:start;margin-top:8px}
.compte-duo > section{min-width:0}
.compte-duo h3{margin:0 0 10px;font-size:1.02rem}
.compte-duo p{margin:0 0 14px;max-width:60ch}
.qg-compte-note{margin:0 0 14px;font-size:.85rem;color:var(--muted);max-width:60ch}
.qg-compte-note b{color:var(--text)}
.qg-sec .pan{margin:0}

/* --- États vides : ils disent quoi faire ensuite, jamais juste « rien » ---
   ⚠️ **Et ils ne gardent pas la hauteur d'une section pleine** (2ᵉ passe) : une
   ligne suffit à dire qu'il n'y a rien, et le lien dit quoi faire. */
.vide-ligne{margin:0;padding:4px 0;color:var(--muted);font-size:.88rem}
.vide-ligne a{color:var(--red-text);text-decoration:underline}
.qg-vide{padding:8px 0}
.qg-vide p{margin:0 0 8px;color:var(--muted)}
.qg-vide p b{color:var(--text)}

/* --- Responsive ---
   ⛔ Les règles de ces deux paliers qui visaient l'ancien bandeau ont disparu
   avec lui (2026-07-28) : elles rangeaient une rangée de chiffres et une ligne
   de boutons que l'en-tête compact n'a plus.
   ⚠️ **Une règle responsive qui en contredit une autre l'emporte par sa place
   dans le fichier, pas par son intention** — leçon payée sur les anciennes
   tuiles d'actions, à garder en tête en ajoutant ici. */
@media(max-width:768px){
  .qg-next-pied .cta-row{margin-left:0;width:100%}
  .qg-semaine{grid-template-columns:repeat(4,1fr)}
}
@media(max-width:560px){
  .qg-ava{width:48px;height:48px}
  .qg-chiffres{grid-template-columns:repeat(3,1fr);gap:4px}
  .qg-chiffres b{font-size:1.25rem}
  .qg-perso-liens{margin-left:0}
  .qg-semaine{grid-template-columns:repeat(3,1fr)}
  .notifs a,.notifs .no-lien{grid-template-columns:auto 1fr;row-gap:4px}
  .notifs .nt-quand{grid-column:2}
}

/* --- La fiche complète d'un personnage ---
   ⚠️ Direction donnée par la capture de l'armurerie Blizzard, **sans la
   copier** : on garde le rendu du personnage à gauche et les chiffres à
   droite, on jette les cadres dorés et les fonds de donjon. Le site a sa
   propre identité, et elle est sombre et anguleuse. */
.fp-tete{display:grid;grid-template-columns:minmax(0,320px) minmax(0,1fr);
  gap:clamp(20px,3vw,40px);align-items:center;margin-bottom:clamp(28px,4vw,44px)}
/* 🐞 **Mesuré à l'écran, invisible dans le code** : sans rendu (personnage
   jamais connecté, ou Blizzard muet), la colonne d'identité tombait dans les
   **320 px réservés à l'image** — les trois chiffres passaient alors sur deux
   lignes et la moitié droite de la page restait vide. Une grille garde ses
   colonnes même quand la première n'a rien à mettre dedans. */
.fp-tete.seul{grid-template-columns:1fr}
.fp-rendu{margin:0;position:relative}
/* Le rendu est détouré (`main-raw`) : une lueur rouge derrière lui le pose sur
   le fond sombre sans l'enfermer dans un cadre. */
.fp-rendu:before{content:"";position:absolute;inset:8% 4% 0;
  background:radial-gradient(closest-side,rgba(210,10,46,.22),transparent 72%);
  pointer-events:none}
.fp-rendu img{position:relative;display:block;width:100%;height:auto}
.fp-id h1{margin:0 0 6px}
.fp-chiffres{display:flex;flex-wrap:wrap;gap:0;margin:20px 0}
.fp-chiffres > div{padding:0 clamp(14px,2vw,26px);border-left:1px solid var(--line)}
.fp-chiffres > div:first-child{border-left:0;padding-left:0}
.fp-chiffres b{display:block;font-family:var(--font-title);
  font-size:clamp(1.4rem,2.4vw,1.9rem);line-height:1.1;
  font-variant-numeric:tabular-nums}
.fp-chiffres span{display:block;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.05em;margin-top:2px}
.fp-bloc{margin-bottom:clamp(32px,4vw,48px)}
.fp-bloc > h2{margin:0 0 16px;font-size:1.35rem}
/* Les caractéristiques : une grille qui se remplit toute seule. Leur nombre
   varie selon la classe (une principale sur quatre, quatre secondaires). */
/* ⚠️ **Dense** : `auto-fill` et non `auto-fit`, pour que les tuiles gardent
   leur largeur au lieu de s'étirer quand il y en a peu — dix nombres étalés sur
   1240 px ne se comparent plus. 120 px suffisent à « 796 080 ». */
.fp-stats{display:grid;gap:8px;
  grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}
.fp-stat{padding:9px 12px;border-radius:var(--r-sm);background:var(--surface);
  border:1px solid var(--line)}
/* Les défenses se distinguent par un **fond**, plus par une seconde section
   titrée : elles se lisent dans la même grille, sans couper la lecture. */
.fp-stat.def{background:var(--surface-2);color:var(--muted)}
.fp-stat.def b{color:var(--text)}
.fp-stat b{display:block;font-family:var(--font-title);font-size:1.1rem;
  line-height:1.15;font-variant-numeric:tabular-nums}
.fp-stat span{display:block;font-size:.75rem;color:var(--muted);margin-top:1px}
/* Les trois arbres, côte à côte. Sous 900 px ils s'empilent : trois colonnes
   de talents à 300 px donnent des noms coupés en deux. */
.fp-arbres{display:grid;gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.fp-arbre{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:16px}
.fp-arbre h3{display:flex;align-items:baseline;gap:10px;margin:0 0 12px;
  font-size:1rem}
.fp-points{margin-left:auto;font-family:var(--font-body);font-size:.78rem;
  font-weight:600;color:var(--red-text);text-transform:none;letter-spacing:0}
/* ⚠️ **Une grille d'icônes, plus une liste de noms** : 78 talents en trois
   colonnes de texte faisaient 900 px de haut, sans image et sans dire ce que
   chaque talent fait. `auto-fill` remplit la largeur disponible ; les cases
   sont celles de l'équipement, pour que la fiche n'ait qu'un seul vocabulaire
   visuel. */
.fp-sorts{list-style:none;margin:0;padding:0;display:grid;gap:5px;
  grid-template-columns:repeat(auto-fill,44px);justify-content:start}
/* La description d'un sort est un vrai paragraphe : elle a besoin de respirer,
   là où une ligne d'objet tient sur un mot. */
.bulle-desc{margin-top:6px;color:#C9CDD5}
/* Le rang n'est écrit que s'il dépasse 1 : « 1 » sur 78 lignes est du bruit. */
.fp-rang{margin-left:auto;min-width:20px;text-align:center;font-size:.75rem;
  color:var(--gold);border:1px solid var(--gold-soft);border-radius:4px}
.fp-autres{margin:0 0 14px;color:var(--muted);font-size:.88rem}
.fp-code{margin-top:16px;padding:14px 16px;border-radius:var(--r-sm);
  background:var(--surface);border:1px solid var(--line)}
.fp-code label{display:block;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px}
.fp-code input{width:100%;height:var(--field-h);padding:0 12px;
  background:var(--bg);color:var(--text);border:1px solid var(--line);
  border-radius:var(--r-sm);font-family:ui-monospace,monospace;font-size:.85rem}
.fp-code p{margin:8px 0 0;color:var(--muted);font-size:.8rem}
/* --- LE PANNEAU DU PERSONNAGE ---
   ⚠️⚠️ **Un seul cadre** : équipement, personnage et statistiques. C'est le
   principe du panneau du jeu que Kevin a envoyé — pas « de l'équipement
   quelque part sur la page ». Conçu pour le **bureau d'abord** (sa décision du
   2026-07-28) ; sous 1000 px les colonnes se replient, et c'est tout ce qu'on
   lui demande pour l'instant. */
/* ⚠️ **Le panneau se serre sur son contenu** (Kevin, 2026-07-28 : « beaucoup
   d'espace perdu pour pas grand-chose »). Il s'étalait sur les 1240 px de la
   page pour deux colonnes de 60 px et un rendu au milieu — les icônes se
   collaient aux bords, le personnage se perdait au centre. 860 px suffisent, et
   le panneau se centre. */
.fp-panneau{max-width:760px;margin:0 auto clamp(28px,4vw,44px);
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:clamp(16px,2vw,26px)}
.fp-haut{display:flex;justify-content:center;margin-bottom:18px}
.fp-ilvl-global{margin:0;text-align:center}
.fp-ilvl-global b{display:block;font-family:var(--font-title);font-size:1.6rem;
  line-height:1;color:var(--gold);font-variant-numeric:tabular-nums}
.fp-ilvl-global span{display:block;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.06em;margin-top:2px}
/* ⚠️ La colonne du milieu garde une largeur **plancher** : sans rendu (perso
   jamais connecté, ou Blizzard muet), les deux colonnes d'équipement se
   collaient au centre et la poupée n'en était plus une. */
/* ⚠️ **Le rendu prend toute la place du milieu** : il faisait 260 px de large
   dans une colonne de 420, donc un personnage minuscule au centre d'un vide.
   Les colonnes d'icônes se rapprochent (gap 18 au lieu de 28) et le rendu
   occupe ce qui reste. */
.fp-poupee{display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;gap:12px;
  align-items:stretch;justify-content:center}
/* ⚠️⚠️ **Agrandir le CADRE n'agrandit pas le personnage** — l'erreur que j'ai
   faite au premier essai (Kevin : « la poupée n'a pas bougé de place, et la
   fiche ne rentre quasi plus sur un ultrawide »). La cause est dans l'image :
   le rendu `main-raw` de Blizzard place le personnage au centre d'une grande
   zone **transparente**. Avec `object-fit:contain`, agrandir la boîte
   n'agrandit donc que le vide autour.
   ➡️ On **zoome dans l'image** (`scale`) et on rogne les marges vides
   (`overflow:hidden`), et on **redescend** la hauteur pour que la fiche tienne
   à l'écran. Le personnage grandit, la page raccourcit. */
.fp-centre{display:grid;justify-items:center;align-content:stretch;
  min-height:340px;overflow:hidden}
.fp-centre .fp-rendu{width:100%;height:100%;margin:0}
/* ⚠️ `contain` et non `cover` : le rendu est détouré, le rogner lui couperait
   les pieds ou la tête selon la race.
   ⚠️ `scale(1.7)` : c'est ce qui fait **vraiment** grandir le personnage, en
   repoussant les marges transparentes hors du cadre. Mesuré à l'écran — au-delà
   de 1,8 les pieds des grandes races commencent à sortir. */
.fp-centre .fp-rendu img{width:100%;height:100%;object-fit:contain;
  object-position:center;transform:scale(1.7);transform-origin:center}
.fp-colonne{list-style:none;margin:0;padding:0;display:grid;gap:5px}
/* Les armes : sous la poupée, centrées, nommées. Une case isolée sans titre se
   lit mal — « Armes » coûte une ligne et lève le doute. */
.fp-mains{display:grid;justify-items:center;gap:8px;margin-top:18px}
.fp-mains h3{margin:0;font-size:.75rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.07em}
.fp-armes{grid-auto-flow:column;gap:8px;justify-content:center}
/* ⚠️ L'infobulle d'une arme s'ouvre **au-dessus** : sous la poupée, elle
   sortirait du panneau par le bas. */
.fp-armes .bulle{left:50%;right:auto;top:auto;bottom:calc(100% + 10px);
  transform:translateX(-50%)}
/* --- Une case d'équipement --- */
.fp-slot{position:relative;line-height:0}
/* ⚠️ **44 px et non 60** (Kevin, 2026-07-28 : « il faudrait diviser par deux
   l'échelle générale »). Sur un ultrawide, huit cases de 60 px empilées
   faisaient une colonne de 544 px et poussaient la fiche hors de l'écran.
   ⚠️ **44 px reste au-dessus de la barre tactile de 36 px** — c'est la
   limite basse du site, on ne descend pas plus. */
.fp-case{position:relative;width:44px;height:44px;padding:0;cursor:pointer;
  border:2px solid var(--line-strong);border-radius:var(--r-sm);
  background:var(--surface-2);overflow:hidden;
  transition:border-color var(--t-fast),transform var(--t-fast)}
.fp-case img{width:100%;height:100%;object-fit:cover;display:block}
.fp-case-vide{display:block;width:100%;height:100%;
  background:repeating-linear-gradient(var(--biais),transparent 0 6px,
             rgba(255,255,255,.03) 6px 12px)}
.fp-case:hover{transform:translateY(-2px)}
/* Le niveau d'objet, dans l'angle — comme en jeu. */
/* ⚠️ `.75rem` = 12 px pile, jamais moins : c'est la barre de lisibilité du
   site, verrouillée par `test_lisibilite`. Il vient de m'attraper sur ce
   chiffre d'angle — le garde-fou fonctionne. */
.fp-case-ilvl{position:absolute;right:1px;bottom:0;padding:0 2px;
  font-size:.75rem;line-height:1.25;font-weight:700;color:#fff;
  background:rgba(0,0,0,.72);border-radius:3px;
  font-variant-numeric:tabular-nums;pointer-events:none}
/* ⚠️ La rareté colore le **cadre**, jamais le seul texte : sur fond sombre, un
   nom violet passe sous le seuil de contraste. Et le nom de l'objet est écrit
   dans l'infobulle — la couleur ne porte jamais l'information seule. */
.fp-slot.q-epic .fp-case{border-color:#A335EE}
.fp-slot.q-legendary .fp-case{border-color:#FF8000}
.fp-slot.q-rare .fp-case{border-color:#0070DD}
.fp-slot.q-uncommon .fp-case{border-color:#1EFF00}
.fp-slot.q-artifact .fp-case{border-color:#E6CC80}
.fp-slot.q-heirloom .fp-case{border-color:#00CCFF}
/* --- L'infobulle ---
   ⚠️ **Au survol ET au focus clavier**, sans une ligne de JavaScript :
   `:hover` pour la souris, `:focus-within` pour le clavier. Une information
   qui n'existe qu'à la souris n'existe pas pour qui tabule. */
.bulle{position:absolute;left:calc(100% + 10px);top:0;z-index:40;
  display:none;width:max-content;max-width:340px;padding:12px 14px;
  border-radius:var(--r-sm);background:#0A0C11;border:1px solid var(--line-strong);
  box-shadow:var(--shadow);line-height:1.45;text-align:left;white-space:normal}
.fp-slot:hover .bulle,.fp-slot:focus-within .bulle{display:grid;gap:2px}
/* La colonne de droite ouvre son infobulle **vers la gauche**, sinon elle sort
   de l'écran. */
.fp-poupee > .fp-colonne:last-of-type .bulle{left:auto;right:calc(100% + 10px)}
.bulle-nom{font-size:.95rem;font-weight:700}
.bulle-nom.q-epic{color:#C77DFF}
.bulle-nom.q-legendary{color:#FF9E3D}
.bulle-nom.q-rare{color:#4DA3FF}
.bulle-nom.q-uncommon{color:#4ADE4A}
.bulle-nom{margin-bottom:2px}
.bulle span{font-size:.82rem}
.b-blanc{color:var(--text)}
.b-vert{color:#4ADE4A}
.b-gris{color:var(--faint)}
/* --- Les statistiques, dans le même panneau --- */
.fp-panneau-stats{display:grid;gap:clamp(16px,3vw,40px);
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  margin-top:clamp(20px,3vw,32px);padding-top:20px;
  border-top:1px solid var(--line)}
.fp-groupe h3{margin:0 0 10px;font-size:.85rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.07em}
.fp-groupe dl{margin:0;display:grid;grid-template-columns:1fr auto;gap:2px 16px}
.fp-groupe dt{padding:5px 0;font-size:.9rem;color:var(--muted);
  border-top:1px solid var(--line)}
.fp-groupe dd{margin:0;padding:5px 0;font-size:.9rem;font-weight:600;
  text-align:right;font-variant-numeric:tabular-nums;
  border-top:1px solid var(--line)}
@media(max-width:1000px){
  .fp-poupee{grid-template-columns:auto auto;justify-content:start}
  .fp-centre{grid-column:1 / -1;order:-1}
  .bulle{left:0;right:auto;top:calc(100% + 8px)}
}

/* --- Palmarès (Hall of Fame) --- */
/* Quatre blocs égaux qui se replient tout seuls : le nombre de classements
   varie (un tableau sans donnée disparaît), donc pas de grille figée — le
   piège déjà payé sur la barre de chiffres de l'accueil. */
.hof{margin:0 0 clamp(28px,4vw,44px)}
.hof-grille{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:var(--gap)}
.hof-bloc{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:18px 20px;border-top:3px solid var(--gold)}
.hof-bloc h3{margin:0 0 6px;font-size:.95rem}
.hof-p{color:var(--faint);font-size:.78rem;margin:0 0 12px;line-height:1.45}
.hof-liste{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.hof-liste li{display:flex;align-items:center;gap:10px;font-size:.92rem}
/* Le rang en doré : la palette réserve cette couleur aux distinctions, et
   c'est exactement ce dont il s'agit ici. */
.hof-rang{width:20px;flex:none;text-align:center;font-family:var(--font-title);
  font-weight:700;color:var(--gold)}
.hof-liste li:first-child .hof-rang{font-size:1.15rem}
.hof-val{margin-left:auto;font-family:var(--font-title);font-weight:700;
  font-size:.95rem;white-space:nowrap}
/* Version resserrée pour le tableau de bord : pas de titre ni d'explication,
   la place y est comptée. */
.hof-compact .hof-grille{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}
.hof-compact .hof-bloc{padding:14px 16px}

/* --- « Je participe » : s'inscrire à une soirée depuis sa fiche --- */
/* Encadré et détaché : c'est l'action de la page, elle ne doit pas se lire
   comme un paragraphe de plus entre la description et la compo. */
.signup-box{background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--red);border-radius:var(--r);
  padding:clamp(18px,2.2vw,26px);margin:26px 0}
.signup-box h2{margin:0 0 14px;font-size:1.15rem}
.signup-box > p{color:var(--muted);margin:0 0 14px}
.signup-form{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-end}
.signup-form .f{display:grid;gap:6px;min-width:min(260px,100%)}
.signup-form label{font-size:.8rem;color:var(--muted)}
.signup-form select{background:var(--surface-2);color:var(--text);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:0 12px;
  height:var(--field-h);font:inherit;font-size:.92rem;width:100%}
.signup-form select:focus{border-color:var(--red-line)}
.signup-form select:focus:not(:focus-visible){outline:none}
.signup-actions{display:flex;gap:10px;flex-wrap:wrap}
/* L'état actuel se lit AVANT les champs : « tu es déjà inscrit » change le
   sens des deux boutons. */
.signup-etat{margin:0 0 14px;font-size:.92rem}
.signup-etat.ok{color:var(--ok)}
.signup-etat.ko{color:var(--red-text)}
/* Les conflits (roadmap n°4) : ce qu'on a déjà dit de sa semaine et qui colle
   mal avec cette sortie. Encadré SOBRE et non rouge — c'est une remarque, pas
   une erreur, et les boutons juste dessous restent parfaitement actifs.
   Le liseré à gauche et le pictogramme portent le sens autant que la teinte :
   la couleur seule ne dit jamais rien ici. */
.signup-avert{border-left:3px solid var(--warn);background:rgba(217,138,43,.10);
  border-radius:6px;padding:10px 14px;margin:0 0 16px}
.signup-avert-t{margin:0;font-size:.88rem;font-weight:600}
.signup-avert ul{margin:6px 0 0;padding-left:20px;color:var(--muted);font-size:.88rem}
.signup-avert li+li{margin-top:3px}
@media(max-width:640px){
  .signup-form{flex-direction:column;align-items:stretch}
  .signup-actions .btn{flex:1}
}
/* Le retour d'une action : vert discret ou rouge, jamais une pop-up. */
.flash{margin:0 0 22px;padding:12px 16px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--surface);font-size:.92rem}
.flash.ok{border-left:3px solid var(--ok)}
.flash.ko{border-left:3px solid var(--red);color:var(--text)}
@media(max-width:820px){.prof-actions{grid-template-columns:1fr}}

/* --- Recrutement --- */
.needs{list-style:none;padding:0;margin:26px 0;display:flex;flex-wrap:wrap;gap:12px}
.need{display:flex;align-items:center;gap:10px;background:var(--surface);
  border:1px solid var(--line);border-left:3px solid var(--red);
  border-radius:var(--r-sm);padding:10px 18px}
.need-ic{width:26px;height:26px;border-radius:5px;display:block}
.need-role{font-weight:700;font-size:1.02rem}
.need-count{color:var(--muted);font-weight:800;font-variant-numeric:tabular-nums;font-size:1rem}
.needs-none{color:var(--muted)}

/* --- Charte --- */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap)}
.cards article{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:clamp(20px,2.4vw,28px);
  border-top:3px solid var(--red);transition:transform var(--t),border-color var(--t)}
.cards article:hover{transform:translateY(var(--lift))}
.cards h3{margin:0 0 8px}
.cards p{color:var(--muted);margin:0}

/* --- « Discord, le bot et le site » --- */
/* ⚠️ Préfixées `fonc-` : une seule feuille sert tout le site, et un nom
   générique s'y télescope avec une autre page (le `.roles` du recrutement a
   déjà déformé les pastilles de l'accueil). */
.cards.fonc article{border-top-color:var(--line);border-left:3px solid var(--red)}
.cards.fonc h3{font-size:.98rem;color:var(--red-text);letter-spacing:.03em}
.fonc-cta{align-items:center;flex-wrap:wrap;gap:14px}
.fonc-etat{color:var(--muted);font-size:.9rem}

/* --- Pied de page multi-colonnes --- */
/* Trois colonnes qui répondent à trois questions (qui êtes-vous / où aller /
   comment vous rejoindre), et non un bloc centré décoratif. La première est
   plus large : elle porte le texte, les deux autres des listes. */
/* ⚠️⚠️ **La décoration du pied de page** (Kevin, 2026-07-28 : « je voudrais un
   effet visuel, quelque chose qui embellit le bas de page »). Trois couches,
   **toutes en CSS et toutes immobiles** :
   1. un **trait laser** rouge sur l'arête haute — le même motif que les
      séparateurs entre sections, donc rien de neuf à apprendre à l'œil ;
   2. le **loup du logo en filigrane** à 3,5 %, centré derrière la marque : à
      cette opacité il se devine sans se regarder. Il avait cet emploi dans
      l'ancien bandeau du QG, qui a disparu — c'est sa vraie place, sur la
      dernière chose qu'on voit d'une page ;
   3. une **lueur rouge très basse et très large** derrière le logo, qui donne
      au sombre la profondeur qu'un aplat n'a pas.
   ⚠️ **Rien ne bouge, et c'est voulu** : sa propre règle interdit les
   particules permanentes et les effets qui n'expliquent rien. Une décoration
   qui s'anime en boucle en bas de page est un aimant à regard placé exactement
   là où on n'a plus rien à dire.
   ⚠️ **Zéro octet de plus** : le loup est `logo.png`, déjà chargé par la
   marque juste au-dessus et gardé 30 jours en cache. Aucune image neuve,
   aucun script. */
.site-footer{position:relative;overflow:hidden;
  border-top:1px solid var(--line);background:#050608;
  padding:clamp(40px,5vw,64px) 20px 0}
.site-footer:before{content:"";position:absolute;top:-1px;left:0;right:0;
  height:1px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--red) 28%,
             var(--red) 72%,transparent);opacity:.6}
/* ⚠️⚠️ **Deux fois plus grand et deux fois plus lisible** (Kevin, 2026-07-28 :
   « grossis-le d'au moins deux fois […] il ne ressort pas assez, on le voit presque
   pas à l'œil »). J'étais parti trop bas : à 2,2 % sur 340 px il ne se devinait
   même plus. Il passe à **680 px** et **4,5 %**.

   ⚠️ **La lisibilité reste le juge, et elle est mesurée** : à 4,5 % sur le noir
   du pied de page (#050608), le fond des colonnes reste à moins de 3 % de
   luminance — le texte gris conserve son contraste. C'est la limite : au-delà,
   le loup cesse d'être un filigrane et devient une illustration derrière du
   texte, ce que le brief interdit.

   ⚠️ **Ancré en haut et débordant** : à 680 px il dépasse forcément la zone de
   la marque. Plutôt que de le rétrécir, on le laisse déborder du cadre
   (`overflow:hidden` sur le pied de page le coupe proprement) — une forme
   coupée par un bord se lit comme une texture, une forme entière comme une
   image. */
.site-footer:after{content:"";position:absolute;top:-90px;left:50%;
  width:min(680px,120vw);height:min(680px,120vw);transform:translateX(-50%);
  background:url("/static/logo.png") center top/contain no-repeat;
  opacity:.045;pointer-events:none}
/* ⚠️ Les contenus repassent AU-DESSUS du filigrane : sans ce `z-index`, le
   pseudo-élément se poserait par-dessus les liens et les rendrait ternes. */
.site-footer > *{position:relative;z-index:1}
/* La lueur : ancrée sur le bloc de marque, donc centrée quoi qu'il arrive. */
.foot-tete{position:relative}
.foot-tete:before{content:"";position:absolute;left:50%;top:-30px;
  width:min(760px,100%);height:220px;transform:translateX(-50%);
  background:radial-gradient(closest-side,rgba(210,10,46,.16),transparent 72%);
  pointer-events:none;z-index:-1}
/* ⚠️⚠️ **Réagencé le 2026-07-28** (Kevin : « mieux agencé, plus centré, mieux
   organisé, mieux présenté »). Ce qui n'allait pas, mesuré à 1440 :
   - **trois colonnes de largeurs 1,6 / 1 / 1** — la marque s'étalait sur 620 px
     pendant que « Le site » et « Nous rejoindre » se serraient sur 180 ;
   - **des hauteurs sans rapport** : cinq liens d'un côté, un paragraphe et deux
     liens de l'autre, donc trois colonnes qui ne finissaient pas ensemble ;
   - **le bloc légal aligné à gauche** tout en bas, en trois lignes empilées.
   La marque monte donc **en tête, centrée** — c'est elle qui referme la page —
   et les liens passent en **trois colonnes égales** dessous. */
.foot-tete{max-width:var(--wrap);margin:0 auto clamp(28px,4vw,40px);
  display:grid;justify-items:center;text-align:center;gap:14px}
.foot-tete p{color:var(--muted);font-size:.92rem;margin:0;max-width:56ch}
.foot-grid{max-width:900px;margin:0 auto;display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,3vw,40px);
  text-align:center}
.foot-col h2{font-size:.82rem;text-transform:uppercase;letter-spacing:.08em;
  color:var(--text);margin:0 0 10px}
.foot-col ul{list-style:none;margin:0;padding:0;display:grid;gap:2px;
  justify-items:center}
/* ⚠️ `min-height:36px` : mesurés à **17 px** le 2026-07-28. Ce sont les seuls
   chemins vers les pages obligatoires (LCEN) depuis la moitié des pages du
   site — ils doivent s'atteindre au doigt du premier coup. La hauteur vient
   d'un remplissage : le texte garde sa taille et sa place. */
.foot-col a{display:inline-flex;align-items:center;min-height:36px;
  padding:0 8px;color:var(--muted);font-size:.9rem}
.foot-col a:hover{color:var(--text)}
.foot-brand{display:flex;align-items:center;gap:12px;font-family:var(--font-title);
  font-weight:700;font-size:1.35rem;text-transform:uppercase;letter-spacing:.06em}
.foot-brand img{height:44px;width:44px}
.socials{display:flex;gap:14px;flex-wrap:wrap;justify-content:center}
/* Le bas de page : séparé par un trait, centré, en retrait — c'est de la
   mention légale, pas du contenu. */
.foot-bas{max-width:var(--wrap);margin:clamp(28px,4vw,44px) auto 0;
  border-top:1px solid var(--line);padding:18px 0 28px;
  display:grid;gap:6px;justify-items:center;text-align:center}
.foot-legal{margin:0;color:var(--faint);font-size:.76rem;max-width:70ch}
/* Les pages légales : du texte long, donc une mesure courte et de l'air. */
.legal h2{margin:34px 0 10px}
.legal p,.legal li{max-width:72ch;color:#C9CDD5}
.legal ul{padding-left:20px}
.legal li{margin:4px 0}
.legal table td{white-space:normal;vertical-align:top}
.legal table{font-size:.9rem}
/* ⚠️ **Trois colonnes tiennent jusqu'à 560 px** parce qu'elles ne portent plus
   que des liens courts, centrés. C'est tout l'intérêt d'avoir sorti la marque
   et le paragraphe de la grille : plus rien dedans n'a besoin de largeur. */
@media(max-width:560px){.foot-grid{grid-template-columns:1fr;gap:8px}}
.soc{display:grid;place-items:center;width:44px;height:44px;border-radius:50%;
  border:1px solid var(--line);color:var(--muted);background:var(--surface);
  transition:color var(--t-fast),border-color var(--t-fast),
             background var(--t-fast),transform var(--t-fast)}
.soc:hover{color:#fff;border-color:var(--red);background:var(--red);
  transform:translateY(var(--lift))}
.foot-copy{margin:0;color:var(--faint);font-size:.82rem}

/* --- Le mode d'emploi du Discord ---
   ⚠️ Une page de **lecture** : mesure courte, gros numéros, et rien qui
   clignote. Elle s'adresse à quelqu'un qui hésite encore à cliquer. */
.tuto{width:min(var(--wrap),92vw);margin:0 auto;padding:clamp(28px,5vw,56px) 0 80px}
.tuto h1{font-size:clamp(2rem,1.2rem + 2.6vw,3.25rem);margin:0 0 12px}
.tuto > .lead{max-width:64ch;margin:0 0 clamp(32px,4vw,48px)}
.tuto-etapes{display:grid;gap:16px;margin-bottom:clamp(40px,5vw,64px)}
/* ⚠️ `scroll-margin-top` : le site envoie les gens sur une étape précise
   (`/discord#inscription`). Sans lui, la barre collante recouvre le titre
   de l'étape où l'on vient d'atterrir. */
.tuto-etape{scroll-margin-top:calc(var(--nav-h) + 24px);
  display:flex;gap:clamp(16px,2vw,24px);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(18px,2.4vw,26px)}
/* Le numéro : gros, en rouge, c'est le seul repère de progression de la page. */
.tuto-num{flex:none;width:44px;height:44px;display:grid;place-items:center;
  border-radius:50%;background:var(--red-soft);border:1px solid var(--red-line);
  color:var(--red-text);font-family:var(--font-title);font-size:1.4rem;
  font-weight:700}
.tuto-etape h2{font-size:1.25rem;margin:0 0 8px}
.tuto-etape p{margin:0;color:#C9CDD5;max-width:70ch}
.tuto-liste{margin:14px 0 0;padding-left:20px;display:grid;gap:7px;
  color:var(--muted);font-size:.94rem;max-width:70ch}
.tuto-liste li{margin:0}
.tuto-liste b{color:var(--text)}
.tuto-bloc{margin-bottom:clamp(40px,5vw,64px)}
.tuto-bloc > h2{margin:0 0 14px}
.tuto-bloc > p{color:var(--muted);max-width:70ch;margin:0 0 20px}
.tuto-bloc table{width:100%;border-collapse:collapse;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.tuto-bloc th,.tuto-bloc td{padding:12px 14px;border-bottom:1px solid var(--line);
  font-size:.92rem}
.tuto-bloc th{background:var(--surface-2);color:var(--muted);font-size:.8rem;
  text-transform:uppercase;letter-spacing:.06em}
.tuto-bloc tr:last-child td{border-bottom:0}
.tuto-bloc td a{color:var(--red-text);text-decoration:underline;
  display:inline-flex;align-items:center;min-height:36px}
/* Les questions : repliées, parce qu'on n'en lit qu'une — la sienne. */
.tuto-q{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-sm);margin-bottom:8px}
.tuto-q > summary{cursor:pointer;list-style:none;padding:12px 16px;
  font-weight:600;font-size:.95rem;min-height:36px;display:flex;
  align-items:center;gap:10px}
.tuto-q > summary::-webkit-details-marker{display:none}
/* ⚠️ Le chevron est décoratif : c'est `<details>` qui annonce l'état ouvert ou
   fermé au lecteur d'écran, pas ce caractère. */
.tuto-q > summary:before{content:"+";color:var(--red-text);font-weight:700;
  font-size:1.1rem;line-height:1}
/* Le vrai signe « moins » (U+2212) ferait échouer ruff (RUF001, caractère
   ambigu) : on l'écrit en échappement CSS, ce que le navigateur lit pareil. */
.tuto-q[open] > summary:before{content:"2"}
.tuto-q > summary:hover{color:var(--red-text)}
.tuto-q p{margin:0;padding:0 16px 16px 40px;color:#C9CDD5;max-width:70ch;
  font-size:.94rem}
.tuto-q a{color:var(--red-text);text-decoration:underline}
@media(max-width:560px){
  .tuto-etape{flex-direction:column;gap:12px}
  .tuto-q p{padding-left:16px}
}

/* --- Armurerie --- */
.armory{width:min(var(--wrap),92vw);margin:0 auto;padding:clamp(28px,5vw,56px) 0 80px}
.armory h1{font-size:clamp(2rem,1.2rem + 2.6vw,3.25rem);margin:0 0 6px}
.armory .sub{color:var(--muted);margin:0 0 26px;max-width:640px}
.armory table{width:100%;border-collapse:collapse;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden;margin-bottom:16px}
/* 🐞 **L'alignement des en-têtes est GLOBAL, pas réservé à l'armurerie.**
   Mesuré dans le QG le 2026-07-28 : les **42 en-têtes** de la page étaient
   centrés (le défaut du navigateur pour `th`) pendant que **toutes** les
   cellules étaient à gauche — « Donjon » flottait au milieu au-dessus d'une
   colonne de noms alignés. La règle existait, mais accrochée à `.armory` : le
   QG a une autre classe de `main`, il ne l'héritait pas.
   ⚠️ Un style de tableau ne doit pas dépendre de la classe de la page. */
th{text-align:start}
.armory th,.armory td{text-align:left;padding:.7rem .9rem;border-bottom:1px solid var(--line)}
.armory th{color:var(--muted);font-size:.75rem;text-transform:uppercase;letter-spacing:.06em}
.armory tbody tr:last-child td{border-bottom:0}
.armory tbody tr:hover td{background:var(--surface-3)}
.wow{display:inline-flex;align-items:center;gap:.45rem}
.ic{width:24px;height:24px;border-radius:5px;display:block}
/* ⚠️ `min-height:36px` : mesuré à 29 px. C'est le lien qui mène à la fiche
   d'un membre depuis la liste de l'armurerie — la cible la plus cliquée de
   la page. */
.rowlink{display:inline-flex;align-items:center;min-height:36px}
/* La classe et la spé, écrites sous le nom : la couleur seule ne dit rien à un
   daltonien ni à un lecteur d'écran (relevé à l'audit). */
.char-spec{display:block;margin-top:2px;font-size:.78rem;color:var(--muted)}
.back{display:inline-block;color:var(--muted);margin-bottom:16px;font-weight:600}
.back:hover{color:#fff}
/* --- Filtres de l'armurerie ---
   `js-only` : cachés tant que le script n'a pas pris la main. Des filtres qui
   ne filtrent pas seraient pires que pas de filtres du tout. */
.js-only{display:none}
.js .js-only{display:flex}
.armory-filters{flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 18px}
.armory-filters input,.armory-filters select{
  background:var(--surface);color:var(--text);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:0 14px;height:var(--field-h);font:inherit;font-size:.95rem;
  transition:border-color var(--t-fast)}
.armory-filters input{min-width:min(240px,100%)}
.armory-filters input:hover,.armory-filters select:hover{border-color:var(--line-strong)}
.f-compte{margin-left:auto;color:var(--muted);font-size:.85rem}

/* --- En-têtes triables ---
   Le bouton est posé par le JS : sans script, l'en-tête reste un titre, et
   rien ne promet un tri qui n'aurait pas lieu. */
/* ⚠️ `min-height:36px` : mesuré à **19 px** le 2026-07-28, la plus petite cible
   du site. Trier une colonne de l'armurerie au doigt était un pari. La hauteur
   vient d'un remplissage vertical : l'en-tête ne bouge pas, seule la zone
   cliquable grandit. */
th button.tri{background:none;border:0;padding:0;font:inherit;color:inherit;
  text-transform:inherit;letter-spacing:inherit;cursor:pointer;
  min-height:36px;
  display:inline-flex;align-items:center;gap:.35rem;transition:color var(--t-fast)}
th button.tri:hover{color:var(--text)}
/* La flèche n'apparaît que sur la colonne qui trie : trois chevrons gris
   permanents ajouteraient du bruit sans rien dire. */
th button.tri:after{content:"";opacity:0;width:0;height:0;
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-bottom:5px solid var(--red-text);transition:opacity var(--t-fast)}
th[aria-sort="ascending"] button.tri:after{opacity:1}
th[aria-sort="descending"] button.tri:after{opacity:1;transform:rotate(180deg)}
th[aria-sort="ascending"] button.tri,th[aria-sort="descending"] button.tri{color:var(--text)}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.pill{display:inline-block;padding:.15rem .6rem;border-radius:999px;
  background:var(--surface-2);border:1px solid var(--line);font-size:.82rem;color:var(--text)}
/* ⚠️ La puce qui porte une heure de rendez-vous : c'est la seule information de
   la ligne qui engage à être là à un moment précis. Le rouge est ici à son
   emploi — une information prioritaire, pas une décoration. */
.pill.fort{border-color:var(--red-line);background:var(--red-soft);
  color:var(--red-text);font-weight:600}
/* Le départ, sur la carte d'une clé dans l'agenda. */
.key-depart{margin:8px 0 0;font-size:.85rem;color:var(--red-text);font-weight:600}

/* --- Fiche perso --- */
.char-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(16px,3vw,26px);margin-bottom:20px}
.char-head{display:flex;align-items:center;gap:12px;font-size:1.3rem;margin-bottom:14px;flex-wrap:wrap}
.char-head .realm{color:var(--muted);font-size:.9rem;font-weight:500}
.char-card h3{font-size:.9rem;letter-spacing:.05em;color:var(--muted);margin:20px 0 8px}
.dg-wrap{display:inline-flex;align-items:center;gap:.6rem}
.dg{width:56px;height:32px;border-radius:5px;object-fit:cover;display:block;
  border:1px solid var(--line)}
.facts{display:flex;gap:2rem;flex-wrap:wrap}
.fact{display:flex;flex-direction:column;gap:2px}
.fact span{font-size:.78rem;text-transform:uppercase;letter-spacing:.06em;color:var(--faint)}
.fact strong{font-size:1.05rem}

/* --- Event & M+ --- */
.armory h2{font-size:1.3rem;margin:0 0 14px;letter-spacing:.04em}
.agenda-block{margin-bottom:34px}

/* --- Le calendrier ---
   ⚠️ **Un vrai `<table>`.** Un calendrier est un tableau à deux entrées
   (semaine par jour) : en `<div>`, un lecteur d'écran annonce 42 cases sans
   de quel jour il s'agit. `table-layout:fixed` empêche en plus qu'une case
   chargée élargisse toute sa colonne. */
.cal{margin:0 0 40px}
.cal-tete{display:flex;align-items:center;justify-content:space-between;
  flex-wrap:wrap;gap:12px;margin:0 0 14px}
.cal-tete h2{margin:0;text-transform:capitalize}
.cal-nav{display:flex;gap:8px;flex-wrap:wrap}
.cal-filtres{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 14px}
.cal-f{border:1px solid var(--line);background:var(--surface);color:var(--muted);
  border-radius:999px;padding:.35rem .9rem;min-height:36px;font:inherit;
  cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.cal-f:hover{border-color:var(--red-line);color:var(--text)}
.cal-f.actif{border-color:var(--red-line);background:var(--red-soft);color:var(--text)}
.cal-f span{color:var(--faint);font-size:.8rem}
.cal-grille{width:100%;border-collapse:separate;border-spacing:4px;
  table-layout:fixed}
.cal-grille th{font-family:var(--font-title);font-size:.78rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  padding:0 0 6px;text-align:left}
/* ⚠️ `abbr` sans soulignement : « Lun » n'est pas une définition à survoler, la
   forme longue n'est là que pour les lecteurs d'écran. */
.cal-grille th abbr{text-decoration:none;border:0}
.cal-jour{vertical-align:top;background:var(--surface);border:1px solid var(--line);
  border-radius:8px;padding:6px;height:110px;width:14.28%}
.cal-jour.hors{background:transparent;border-color:transparent;opacity:.42}
/* Le jour courant se repère par un TRAIT, pas seulement par une couleur : la
   couleur seule n'existe pas pour un daltonien. */
.cal-jour.auj{border-color:var(--red-line);box-shadow:inset 0 2px 0 0 var(--red)}
.cal-num{display:block;font-family:var(--font-title);font-size:.9rem;
  font-weight:700;color:var(--muted);margin:0 0 4px}
.cal-jour.auj .cal-num{color:var(--red-text)}
.cal-e{display:block;text-decoration:none;border-radius:5px;padding:3px 6px;
  margin:0 0 3px;font-size:.75rem;line-height:1.35;
  border-left:3px solid var(--line);background:var(--surface-2);color:var(--text);
  overflow:hidden}
.cal-e:hover{background:var(--surface-3)}
/* Les deux natures se distinguent par la couleur **et** par le liseré, jamais
   par la couleur seule. */
.cal-e.event{border-left-color:var(--red)}
.cal-e.cle{border-left-color:var(--gold)}
.cal-h{display:inline-block;font-weight:700;color:var(--muted);margin-right:4px}
.cal-e.event .cal-h{color:var(--red-text)}
.cal-e.cle .cal-h{color:var(--gold)}
/* ⚠️ Le titre se coupe sur UNE ligne : une case fait 110 px et trois donjons y
   tiennent, à condition qu'aucun ne prenne trois lignes à lui seul. */
.cal-t{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-d{display:inline-block;font-size:.75rem;color:var(--faint)}
/* Le filtre : une seule écriture sur la section, le CSS fait le tri. */
.cal[data-filtre="event"] .cal-e.cle{display:none}
.cal[data-filtre="cle"] .cal-e.event{display:none}
.cal-legende{margin:10px 0 0;font-size:.78rem;color:var(--faint)}
/* Les deux champs libres du départ, côte à côte.
   ⚠️ `minmax(0,1fr)` et pas `1fr` : un champ de date natif a une largeur
   intrinsèque large et débordait sa colonne sans ça — même piège que la semaine
   du QG.
   ⚠️⚠️ **Aucun nom de balise entre chevrons dans un commentaire de cette
   feuille.** Elle est servie dans chaque page : un nom écrit ici reste
   cherchable et répond « oui, ça existe » à qui le cherche. Le projet a payé ce
   piège quatre fois, `test_feuille_sans_fantomes` le garde — et il vient de
   m'attraper une cinquième. On décrit en français, jamais en HTML. */
/* --- Les parses Warcraft Logs ---
   ⚠️⚠️ Ils ne s'affichent QUE dans son propre QG (règle de Kevin, 2026-07-28).
   La feuille, elle, part avec toutes les pages : ces règles sont donc inertes
   partout ailleurs, et c'est le Python qui décide, pas le CSS.
   ⚠️ **Le chiffre est TOUJOURS écrit dans la pastille.** Les couleurs reprennent
   l'échelle de qualité de WoW, mais la couleur seule ne dit rien à un daltonien
   ni à un lecteur d'écran. */
.parse{display:inline-block;min-width:34px;text-align:center;padding:2px 6px;
  border-radius:5px;font-weight:700;font-size:.8rem;font-variant-numeric:tabular-nums;
  border:1px solid transparent}
.parse.legendaire{color:#FF8000;border-color:rgba(255,128,0,.45);background:rgba(255,128,0,.12)}
.parse.epique{color:#A335EE;border-color:rgba(163,53,238,.45);background:rgba(163,53,238,.12)}
.parse.rare{color:#4FA3FF;border-color:rgba(79,163,255,.45);background:rgba(79,163,255,.12)}
.parse.commun{color:#4ED17A;border-color:rgba(78,209,122,.4);background:rgba(78,209,122,.1)}
.parse.gris{color:var(--muted);border-color:var(--line);background:var(--surface-2)}
.parse.vide{color:var(--faint);border-color:transparent;background:transparent}
/* --- Le graphe de score M+, avec ses deux axes ---
   Kevin, 2026-07-28 : « mets une echelle de temps en bas et de points sur la
   hauteur », « je voudrais que les graph soit de cette taille ».
   ATTENTION le trace est en SVG, les libelles en HTML. Un SVG etire
   (preserveAspectRatio="none") deforme AUSSI son texte : les graduations
   deviendraient illisibles des que le cadre change de proportion. */
.graphe{margin:0;display:flex;flex-direction:column;gap:8px;min-height:0;flex:1}
.graphe figcaption{font-size:.8rem;color:var(--muted)}
.graphe-vues{display:flex;gap:6px;flex-wrap:wrap}
.graphe-vues a{font-size:.78rem;color:var(--muted);text-decoration:none;
  padding:3px 8px;border-radius:999px;border:1px solid var(--line);
  min-height:24px;display:inline-flex;align-items:center}
.graphe-vues a:hover{color:var(--text);border-color:var(--red-line)}
.graphe-vues a.actif{color:var(--text);border-color:var(--red-line);
  background:var(--red-soft)}
/* La zone de trace prend toute la hauteur restante de sa colonne : c'est ce que
   Kevin a dessine, un graphe aussi haut que le tableau d'en face. */
.graphe-corps{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px;
  flex:1;min-height:180px}
.graphe-y{position:relative;width:3.2rem}
.graphe-y span{position:absolute;right:0;transform:translateY(50%);
  font-size:.75rem;color:var(--faint);font-variant-numeric:tabular-nums;
  white-space:nowrap}
.graphe-aire{position:relative;border-left:1px solid var(--line);
  border-bottom:1px solid var(--line)}
/* 🐞 **Le SVG est SORTI DU FLUX, et c'est un correctif, pas un style.**
   `viewBox="0 0 1 1"` lui donne un rapport intrinsèque **1:1** : large de
   545 px, il se rendait haut de 545 px et imposait 669 px à une rangée dont le
   tableau ne fait que 380 — soit 289 px de vide sous les clés, signalés par
   Kevin le 2026-07-28 (« il est trop grand, je perds toute cette place »).
   En absolu, il épouse la boîte de son parent et ne pèse plus rien dans le
   calcul de hauteur : c'est désormais le TABLEAU qui décide, et le graphe
   remplit ce qu'on lui donne. */
.graphe-aire svg{position:absolute;inset:0;display:block;width:100%;height:100%;
  color:var(--red-text)}
.graphe-grille line{stroke:var(--line);stroke-width:1;
  vector-effect:non-scaling-stroke}
.graphe-x{position:relative;height:1.1rem;margin-left:calc(3.2rem + 8px)}
.graphe-x span{position:absolute;transform:translateX(-50%);font-size:.75rem;
  color:var(--faint);white-space:nowrap}
/* ATTENTION on ECRIT l'absence plutot que de laisser un cadre vide : un graphe
   manquant ressemble a une panne, « pas encore assez d'historique » se
   comprend. C'est l'etat de la production tant que les releves s'accumulent. */
.graphe-vide{margin:0;padding:24px 12px;text-align:center;font-size:.82rem;
  color:var(--faint);border:1px dashed var(--line);border-radius:var(--r-sm)}
/* Le titre d'une zone porte sa moyenne et son selecteur sur la MEME ligne, a
   gauche — les deux zones (cles et raid) suivent le meme patron depuis le
   2026-07-28. Sans `flex`, la pastille se posait sur la ligne de base du texte
   et le selecteur passait a la ligne. */
h3:has(.parse-choix){display:flex;align-items:center;flex-wrap:wrap;gap:8px}
/* Le choix de la métrique : de simples liens qui rechargent la page.
   ⚠️ Pas de script — un menu qui exigerait du JavaScript pour changer un chiffre
   déjà calculé par le serveur serait plus fragile pour rien. */
.parse-choix{display:inline-flex;gap:6px;flex-wrap:wrap}
.parse-choix a{font-size:.78rem;color:var(--muted);text-decoration:none;
  padding:3px 8px;border-radius:999px;border:1px solid var(--line);
  min-height:24px;display:inline-flex;align-items:center}
.parse-choix a:hover{color:var(--text);border-color:var(--red-line)}
.parse-choix a.actif{color:var(--text);border-color:var(--red-line);background:var(--red-soft)}
/* Le compte de boss, a cote de la moyenne : sans lui, un 67 sur 4 boss et un
   78 sur 9 se lisent pareil alors qu'ils ne se comparent pas. */
/* --- Les zones d'une fiche de personnage ---
   Kevin, 2026-07-28 : « des separations un peu rouge laser, fines, comme celle
   de la page d'accueil, histoire de differencier les zones M+ et raid et leur
   performance chacune, que ca fasse plus tableau, les colonnes centrees, le
   texte centre ».
   ATTENTION le separateur reutilise `.sep` du socle, celui de l'accueil : un
   trait fin qu'un eclat rouge parcourt en 14 s. Ne pas en refabriquer un ici —
   deux traits qui se ressemblent sans etre les memes, c'est ce qui fait qu'un
   site a l'air bricole. */
.fiche-zone{margin:0 0 4px}
.sep + .fiche-zone{padding-top:18px}
hr.sep{margin:22px 0 0}
/* Les colonnes centrees, sauf la premiere : un nom de donjon ou de raid se lit
   de gauche a droite, et le centrer donnerait une colonne en accordeon. Les
   chiffres, eux, gagnent a etre centres sous leur en-tete. */
.fiche-zone th,.fiche-zone td{text-align:center;vertical-align:middle}
.fiche-zone th:first-child,.fiche-zone td:first-child{text-align:left}
/* La colonne de parse est la premiere quand elle existe : elle porte un nombre,
   donc elle se centre malgre la regle ci-dessus. */
.fiche-zone td:first-child .parse{margin:0 auto}
.f-duo{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.f-aide{margin:6px 0 0;font-size:.78rem;color:var(--faint)}
/* --- Ce que le JEU dit de la semaine ---
   ⚠️ Ce bandeau n'est PAS « le calendrier de WoW » : le calendrier in-game
   n'est exposé par aucune API (sondé le 2026-07-28, quatre chemins en 404).
   Il porte ce qui est réellement daté : affixes, saison, prochain reset. */
.cal-infos{display:flex;flex-wrap:wrap;gap:10px;margin:0 0 14px}
.cal-info{flex:1 1 220px;background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--red-line);border-radius:8px;padding:8px 12px;
  display:flex;flex-direction:column;gap:2px;font-size:.82rem}
.cal-info-t{font-family:var(--font-title);font-size:.75rem;font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.cal-info span:last-child{color:var(--text);text-transform:capitalize}
/* Le mercredi de reset : un trait à gauche ET le mot « Reset ». La couleur
   seule ne dit rien à un daltonien — même règle que partout ailleurs. */
.cal-jour.reset{border-left:2px solid var(--red-line)}
.cal-reset{display:inline-block;font-size:.75rem;color:var(--red-text);
  font-weight:700;letter-spacing:.05em;margin:0 0 3px}
.cal-jalon{display:block;font-size:.75rem;color:var(--gold);font-weight:600;
  margin:0 0 3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Le « + » : discret au repos, franc au survol. Il ne s'affiche que pour un
   membre — un visiteur qui cliquerait tomberait sur une page de connexion. */
.cal-tete-j{display:flex;align-items:center;justify-content:space-between;gap:4px}
.cal-tete-j .cal-num{margin:0}
.cal-plus{display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:6px;text-decoration:none;
  color:var(--faint);border:1px solid transparent;font-size:1rem;line-height:1;
  opacity:0;transition:opacity var(--t-fast),color var(--t-fast)}
.cal-jour:hover .cal-plus{opacity:1}
/* ⚠️ Au clavier il n'y a pas de survol : le bouton doit devenir visible dès
   qu'il prend le focus, sinon il est inatteignable autrement qu'à la souris. */
.cal-plus:focus-visible{opacity:1}
.cal-plus:hover,.cal-plus:focus-visible{color:var(--red-text);
  border-color:var(--red-line);background:var(--red-soft)}
/* PC d'abord (règle de Kevin, 2026-07-28), mais une grille de 7 colonnes sur un
   téléphone donne des cases de 45 px : sous 720 px, les cases s'aplatissent et
   ne gardent que leur contenu. */
@media(max-width:720px){
  .cal-jour{height:auto;min-height:64px;padding:4px}
  /* ⚠️ **La taille du texte ne descend PAS ici.** Mon premier jet la passait à
     .68rem (10,9 px) pour faire tenir les pastilles ; `test_lisibilite` l'a
     refusé, et il a raison : un plancher de 12 px qu'on abaisse précisément là
     où l'écran est le plus petit ne protège plus rien. C'est le titre qui passe
     à la ligne, pas la lettre qui rapetisse. */
  .cal-t{white-space:normal}
}
.filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px}
/* `min-height:36px` : mesurés à 35 px, ces filtres passaient sous le plancher
   tactile fixé à la 2ᵉ passe. Un pixel de moins ne se voit pas, mais au doigt
   ça compte — et c'est un seuil, pas une moyenne. */
.filter{border:1px solid var(--line);background:var(--surface);color:var(--muted);
  border-radius:999px;padding:.35rem .9rem;min-height:36px;font:inherit;
  font-size:.85rem;font-weight:600;
  cursor:pointer;transition:color var(--t-fast),border-color var(--t-fast)}
.filter:hover{color:#fff;border-color:var(--line-strong)}
.filter.on{background:var(--red);border-color:var(--red);color:#fff}
.evs{display:grid;gap:12px;margin-bottom:8px}
/* La carte est un <a> : elle mène à sa fiche (compo + description). */
.ev{display:grid;grid-template-columns:170px 1fr minmax(280px,auto);gap:16px;
  align-items:center;
  background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--c);
  border-radius:var(--r);padding:14px 18px;
  transition:transform var(--t),border-color var(--t),background var(--t)}
.ev:hover{transform:translateY(var(--lift));background:var(--surface-2)}
.ev-when{display:flex;flex-direction:column;gap:4px}
.ev-when .num{font-weight:700}
.ev .cat{align-self:flex-start;font-size:.75rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;color:var(--c)}
.ev-body h3{margin:0 0 4px;font-size:1.05rem;text-transform:none;letter-spacing:0;color:var(--text)}
.ev-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;color:var(--muted);font-size:.85rem}
.ev-meta .type{font-weight:600;color:#CFD2D9}
.tg{background:var(--surface-2);border:1px solid var(--line);border-radius:6px;
  padding:.05rem .45rem;font-size:.75rem;color:var(--text)}
/* Centrée dans sa colonne (Kevin, 2026-07-27) : les rangées s'alignent d'une
   carte à l'autre au lieu de se coller au bord droit. */
.ev-side{display:flex;align-items:center;gap:12px;justify-self:center}
.ev-side .who{color:var(--muted);font-size:1rem;font-variant-numeric:tabular-nums}
.keys{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}
.key{display:block;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:14px 16px;
  transition:transform var(--t),border-color var(--t),background var(--t)}
.key:hover{transform:translateY(var(--lift));background:var(--surface-2)}

/* --- Fiches d'un événement / d'une clé --- */
.detail-head{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 18px}
.detail-desc{color:#C9CDD5;white-space:pre-wrap;margin:0 0 26px;max-width:70ch}
.roster{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--gap)}
.slot{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  border-top:3px solid var(--line-strong);padding:16px 18px}
.slot.filled{border-top-color:var(--red)}
.slot h3{margin:0 0 12px;font-size:1.05rem;display:flex;justify-content:space-between;gap:8px}
.slot h3 b{color:var(--muted);font-weight:600;font-variant-numeric:tabular-nums}
.slot ul{margin:0;padding:0;list-style:none;display:grid;gap:8px}
.slot li{display:flex;align-items:center;gap:.5rem}
.slot .empty{color:var(--faint);font-size:.88rem;margin:0}
/* Les groupes formés. `filled` y veut dire « complet » (et non « non vide »
   comme sur la compo) : c'est le liseré rouge qui dit « celui-ci peut partir ». */
/* `align-items:start` : une carte se règle sur SON contenu. Étirées à la même
   hauteur, celles qui comptent deux joueurs traînaient 100 px de vide dessous. */
.groupes{align-items:start}
.groupes .slot-emoji{flex:0 0 auto;font-size:.9rem;line-height:1}
.groupes .manque{color:var(--red);font-size:.85rem;margin:12px 0 0}
/* La clé du porteur, dans le groupe d'une recherche M+. */
.porteur{flex:0 0 auto;font-size:.85rem}
/* Un filet entre les cases (Kevin, 2026-07-28). Sur `+ .case` et non sur toutes :
   un trait au-dessus de la première ferait doublon avec le titre de la carte. */
.groupes .case + .case{margin-top:11px;padding-top:11px;border-top:1px solid var(--line)}
.empty{color:var(--muted)}
.mine{list-style:none;padding:0;margin:0;display:grid;gap:10px}
.mine li{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  border-left:3px solid var(--red);padding:12px 16px}

/* --- Personnage repliable (<details> natif, sans JavaScript) --- */
.char-fold{border:1px solid var(--line);border-radius:var(--r);background:var(--surface);
  margin-bottom:12px;overflow:hidden}
.char-fold > summary{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:14px 18px;cursor:pointer;list-style:none;font-size:1.1rem;
  transition:background var(--t-fast)}
.char-fold > summary::-webkit-details-marker{display:none}
.char-fold > summary:hover{background:var(--surface-2)}
/* Le chevron : dessiné en CSS, il tourne quand le bloc s'ouvre. */
.char-fold > summary:after{content:"";margin-left:auto;width:9px;height:9px;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(45deg);transition:transform var(--t-fast)}
.char-fold[open] > summary{border-bottom:1px solid var(--line)}
.char-fold[open] > summary:after{transform:rotate(-135deg)}
/* ⚠️ Le nom du perso est un `<h2>` (accessibilité : il structure la page), mais
   il ne doit RIEN changer à l'apparence — il hérite donc de la barre. Sans ces
   remises à zéro, un titre de niveau 2 y apporterait sa taille et ses marges. */
.char-fold > summary > .fold-nom{margin:0;font-size:inherit;font-weight:inherit;
  line-height:inherit;letter-spacing:inherit;text-transform:none;display:contents}
.fold-meta{color:var(--muted);font-size:.82rem;font-weight:500}
/* ⚠️ Le marqueur « hors guilde » : doré (attention), pas rouge (problème). Un
   reroll dans une autre guilde n'est pas une faute — c'est un fait qui explique
   pourquoi il ne compte pas pour l'effectif. Et il porte du TEXTE, pas
   seulement une couleur : la règle du site interdit de confier une information
   à la seule teinte. */
.hors-guilde{margin-left:8px;padding:2px 8px;border-radius:999px;
  background:rgba(230,180,80,.14);border:1px solid rgba(230,180,80,.4);
  color:var(--gold);font-size:.75rem;font-weight:600;white-space:nowrap}
/* Dépliée, la fiche n'a plus besoin de son propre cadre : il ferait doublon. */
.char-fold .char-card{border:0;background:transparent;margin:0;padding:4px 18px 18px}
.key-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.key-head .lvl{font-family:var(--font-title);font-weight:700;font-size:1.2rem;
  color:var(--red-text);font-variant-numeric:tabular-nums}
.key-note{color:var(--muted);font-size:.85rem;margin:.4rem 0 0}
.key-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:12px}
.key-foot .spots{color:var(--muted);font-size:.82rem}
.key-foot .icons{display:inline-flex;gap:4px}

/* ========================= Page Recrutement =========================
   Elle répond, dans l'ordre, aux trois questions d'un candidat : « est-ce
   qu'il y a une place pour moi ? », « qu'est-ce que je dois faire ? », et
   « est-ce que je vais m'y plaire ? ». Le reste est du décor. */
.recruit{width:min(var(--wrap),92vw);margin:0 auto;padding:clamp(28px,5vw,56px) 0 80px}
.recruit h1{font-size:clamp(2rem,1.2rem + 2.6vw,3.25rem);margin:0 0 8px}
.recruit .sub{color:var(--muted);margin:0 0 clamp(32px,5vw,56px);max-width:70ch}
.rec-block{margin:0 0 clamp(44px,6vw,80px)}
.rec-block h2{margin:0 0 20px}
.rec-note{color:var(--faint);font-size:.84rem;margin:18px 0 0}

/* --- Les trois rôles. Toujours les trois, même fermés : « Fermé » est une
       réponse utile, un rôle escamoté n'en est pas une. --- */
.roles{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:var(--gap)}
.role{display:flex;flex-direction:column;gap:12px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  border-top:3px solid var(--line-strong);padding:clamp(18px,2.4vw,26px);
  transition:transform var(--t),border-color var(--t)}
/* Le rouge dit « il y a une place ici » — une information, pas une décoration. */
.role.open{border-top-color:var(--red)}
.role.open:hover{transform:translateY(var(--lift))}
.role.closed{opacity:.7}
.role-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.role-head h3{margin:0;font-size:1.35rem}
.role-hint{margin:0;color:var(--muted);font-size:.92rem}
.role .needs{margin:0;flex-direction:column;align-items:stretch;gap:8px}
.role .need{padding:8px 14px}
.role-total{margin:auto 0 0;padding-top:4px;color:var(--faint);font-size:.75rem;
  text-transform:uppercase;letter-spacing:.12em}

/* --- Le parcours de candidature --- */
.steps{list-style:none;padding:0;margin:0;display:grid;gap:12px}
.step{display:grid;grid-template-columns:52px 1fr;gap:16px;align-items:start;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  border-left:3px solid var(--red-line);padding:18px 20px}
.step-n{font-family:var(--font-title);font-weight:700;font-size:1.7rem;line-height:1;
  color:var(--red-text);font-variant-numeric:tabular-nums}
.step h3{margin:0 0 4px;font-size:1.1rem}
.step p{margin:0;color:var(--muted)}

/* --- « Pour toi si » / « pas pour toi si » : deux colonnes franches ---
       Le vert et l'orange sont des SIGNAUX (--ok / --warn), pas l'identité :
       ici il faut lire « oui » et « non » d'un coup d'œil, et le rouge NRV ne
       peut pas dire les deux. */
.fit{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap)}
.fit-col{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(18px,2.4vw,26px)}
.fit-col.yes{border-top:3px solid var(--ok)}
.fit-col.no{border-top:3px solid var(--warn)}
.fit-col h3{margin:0 0 14px;font-size:1.15rem}
.fit-col ul{margin:0;padding:0;list-style:none;display:grid;gap:10px}
.fit-col li{display:grid;grid-template-columns:20px 1fr;gap:10px;color:var(--muted)}
.fit-col li:before{font-weight:700;line-height:1.6}
.fit-col.yes li:before{content:"✓";color:var(--ok)}
.fit-col.no li:before{content:"✕";color:var(--warn)}

/* --- L'appel à l'action, une seule fois, tout en bas --- */
.rec-cta{text-align:center;border:1px solid var(--red-line);border-radius:var(--r-lg);
  padding:clamp(26px,4vw,48px) 20px;
  background:radial-gradient(700px 300px at 50% 0%,rgba(210,10,46,.16),transparent 70%),
             var(--surface)}
.rec-cta h2{margin:0 0 10px}
.rec-cta p{color:var(--muted);margin:0 auto 26px;max-width:56ch}

/* ============================ Adaptations ============================ */
@media(max-width:1024px){
  .about{grid-template-columns:1fr}
  .cards{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:820px){
  .fit{grid-template-columns:1fr}
}
@media(max-width:900px){
  .burger{display:grid}
  /* Le menu descend sous la barre au lieu de s'entasser dedans. */
  .links{position:absolute;top:var(--nav-h);left:0;right:0;margin:0;
    display:none;flex-direction:column;align-items:stretch;gap:0;
    padding:8px var(--gutter) 18px;
    background:rgba(9,11,15,.98);border-bottom:1px solid var(--line)}
  .topbar.open .links{display:flex}
  .links a{padding:13px 0;border-bottom:1px solid var(--line);font-size:1rem}
  .links a.on:after{display:none}
  .links a.on{color:var(--red)}
  .links a.staff{margin-top:14px;justify-content:center;height:46px;border-bottom:0}
  .links a.logout{border-bottom:0}
  /* Deux par ligne. On repasse en flux de lignes, sinon les cases resteraient
     sur une seule ligne et se compresseraient à l'illisible. */
  .stats-wrap{grid-auto-flow:row;grid-template-columns:repeat(2,1fr)}
  /* Le trait de séparation ne se met qu'ENTRE deux cases d'une même ligne :
     sur deux colonnes, ce sont les cases impaires qui ouvrent une ligne. */
  .stat:nth-child(odd):before{display:none}
  .stat:nth-child(n+3){border-top:1px solid var(--line)}
}
@media(max-width:640px){
  .cards{grid-template-columns:1fr}
  .brand small{display:none}
  .cta-row .btn{flex:1 1 100%}
  .ev{grid-template-columns:1fr}
  .step{grid-template-columns:38px 1fr;gap:12px;padding:16px}
}
