/* ============================================================
   VA-REBIRTH — Design System
   ============================================================ */

/* === 1. TOKENS === */
:root {
  --bg:            #0c0805;
  --bg-card:       #181008;
  --bg-card-alt:   #110b05;
  --bg-nav:        rgba(8, 5, 2, 0.96);
  --bg-section:    #110d07;

  --accent:        #e89610;
  --accent-hover:  #f5aa22;
  --accent-dim:    rgba(232, 150, 16, 0.14);
  --accent-border: rgba(232, 150, 16, 0.35);

  --text:          #f0e6d2;
  --text-muted:    #9a8060;
  --text-dim:      #5a4030;

  --border:        #2a1a08;
  --border-card:   #2e1e0c;
  --ruby:          #cc2233;
  --ruby-dim:      rgba(204, 34, 51, 0.18);

  /* L'améthyste des commandes de jeu : ce que le joueur tient sous le doigt.
     La raquette du casse-briques d'abord, puis le repère du geste du vol. Elle
     est ici parce qu'elle sert à deux jeux — `.bb-scene` la reprend sous le nom
     que `bb.js` lui lit, et le vol s'en sert directement. Elle tranche sur l'or
     du site, qui est la couleur de ce qu'on gagne, pas de ce qu'on manœuvre. */
  --violet-commande: #9d4edd;
  --violet-commande-clair: #c79bff;          /* lisible en texte sur la nuit */
  --violet-commande-lueur: rgba(157, 78, 221, 0.75);

  --r-sm:  4px;
  --r-md:  8px;
  --r-lg:  12px;
  --r-xl:  20px;

  /* Effet verre — consommé uniquement sous `.admin-page`.
     (Le bestiaire a ses propres valeurs, écrites en dur dans `.news-item--glass`
     et consorts : ces jetons ne l'atteignent pas.)

     Le voile était du blanc pur. Posé à 4 % sur un fond brun chaud, il tire le
     tout vers le gris : les pastilles du tableau de bord paraissaient neutres
     au milieu d'une page ambrée, alors que rien autour d'elles ne l'est.

     Le blanc est donc remplacé par un crème tiré de la famille de l'accent
     (#e89610). L'opacité monte d'un cheveu au passage : une teinte chaude peu
     opaque rend moins de contraste sur un fond sombre qu'un blanc de même
     valeur, et à 4 % le verre aurait disparu. */
  --glass-bg:      rgba(255, 206, 148, 0.055);
  --glass-bg-soft: rgba(255, 206, 148, 0.03);
  --glass-border:  rgba(255, 198, 130, 0.14);
  --glass-blur:    blur(14px) saturate(1.2);
  --glass-input:   rgba(255, 206, 148, 0.075);
  --glass-shadow:  0 8px 40px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 224, 180, 0.07);

  --shadow:        0 4px 20px rgba(0, 0, 0, 0.5);
  --shadow-accent: 0 0 24px rgba(232, 150, 16, 0.25);

  --max-w:  1200px;
  --nav-h:  70px;

  --font-display: 'Cinzel', serif;
  --font-body:    'Lato', sans-serif;
}

/* === 2. RESET === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Aucune barre de défilement n'est dessinée, nulle part : ni sur la page, ni
   dans les panneaux qui débordent (modal, tiroir d'inventaire, listes). Le
   défilement lui-même reste entier — molette, geste tactile, flèches et
   `scrollIntoView` fonctionnent comme avant ; c'est le rail gris qui disparaît.
   Contrepartie à connaître : sans lui, rien ne signale qu'il reste du contenu
   plus bas. Là où c'est un risque, c'est à la mise en page de le dire — un
   contenu coupé net en bas de zone, un dégradé de bord — pas à une barre. */
* {
  scrollbar-width: none;      /* Firefox */
  -ms-overflow-style: none;   /* anciens Edge/IE */
}
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

html  { scroll-behavior: smooth; }
/* Le défilement animé vaut pour les ancres comme pour le retour en haut de
   page après un changement d'onglet. Une page qui glisse sur plusieurs
   milliers de pixels donne le vertige à certaines personnes : le système le
   dit, on l'écoute. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
body  {
  font-family: var(--font-body);
  background-color: var(--bg);
  color: var(--text);
  font-size: 16px;
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main { flex: 1; }
img  { max-width: 100%; display: block; }
a    { color: var(--accent); text-decoration: none; transition: color 0.2s; }
a:hover { color: var(--accent-hover); }
ul   { list-style: none; }

/* === 3. TYPOGRAPHY === */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--text);
  line-height: 1.2;
}
h1 { font-size: clamp(1.8rem, 5vw, 3rem); }
h2 { font-size: clamp(1.4rem, 3vw, 2rem); }
h3 { font-size: 1.25rem; }
h4 { font-size: 1.05rem; }

.text-accent { color: var(--accent); }
.text-muted  { color: var(--text-muted); }
.eyebrow {
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  display: block;
  margin-bottom: 0.5rem;
}

/* === 4. LAYOUT === */
.container {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 1.5rem;
}
.section     { padding: 4rem 0; }
.section--alt { background-color: var(--bg-section); }
/* La section d'une page où l'on joue. Les 4 rem sont le rythme d'une page qu'on
   lit ; un plateau, lui, se tient haut, sous les yeux — c'est ce que
   `.game-main` fait pour les jeux servis par le gabarit commun, et cette classe
   le fait pour le pierre-papier-ciseaux, qui n'en dépend pas. */
.section--jeu { padding: 1.5rem 0 2.5rem; }

/* === 5. NAVIGATION — 2 lignes (mobile/tablette) → 1 ligne (desktop ≥ 1024px) === */
#menu {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  background: var(--bg-nav);
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.nav-inner {
  max-width: var(--max-w);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

/* ── Row 1 : Logo | Shortcuts ── */
.nav-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.55rem 1.1rem;
  min-height: 54px;
}

/* ── Row 2 : User info ── */
.nav-user-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.3rem 1.1rem 0.45rem;
  border-top: 1px solid rgba(255,255,255,0.055);
  gap: 0.5rem;
}

/* ── Logo ── */
.nav-logo {
  display: flex; align-items: center; gap: 0.6rem;
  text-decoration: none; flex-shrink: 0;
}
.nav-logo img { width: 36px; height: 36px; object-fit: contain; }
.nav-logo__brand { display: flex; flex-direction: column; line-height: 1.1; }
.nav-logo__name {
  font-family: var(--font-display);
  font-size: 0.95rem; font-weight: 900;
  color: var(--text); letter-spacing: 0.05em;
}
.nav-logo__sub {
  font-size: 0.55rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--text-muted);
}

/* ── Shortcuts (boutique, forge, musique…) ── */
.nav-shortcuts { display: flex; align-items: center; gap: 0.45rem; }

.nav-shortcut-btn {
  width: 2rem; height: 2rem; border-radius: 50%;
  overflow: visible;
  border: 1px solid rgba(245,200,66,0.18);
  background: rgba(255,255,255,0.04);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: transform 0.18s cubic-bezier(.34,1.56,.64,1), border-color 0.15s, box-shadow 0.15s;
}
.nav-shortcut-btn__img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }
.nav-shortcut-btn:hover {
  transform: scale(1.14);
  border-color: rgba(245,200,66,0.6);
  box-shadow: 0 0 14px rgba(245,200,66,0.22);
}
.nav-shortcut-btn--icon {
  overflow: visible; color: var(--text-muted);
  background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12);
}
.nav-shortcut-btn--icon:hover { color: var(--accent); border-color: var(--accent-border); }

/* L'engrenage des réglages est doré comme le reste de l'interface : il avait
   viré au violet le temps d'un essai, ce qui en faisait le seul élément froid
   d'une page ambrée. Il reprend donc l'accent, et la règle de survol ci-dessus
   suffit — plus rien à surcharger ici, sinon l'état par défaut. */
.nav-settings-toggle { color: var(--accent); }
.nav-settings-toggle:hover { color: var(--accent-hover); }

/* ── Music button (compact circle) ── */
.nav-music-btn {
  width: 2rem; height: 2rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
  border-radius: 50%; padding: 0; font-size: 0.88rem; line-height: 1;
  cursor: pointer; color: var(--text); flex-shrink: 0;
  transition: background 0.2s, border-color 0.2s, transform 0.15s;
}
.nav-music-btn:hover { background: rgba(255,255,255,0.13); border-color: rgba(255,255,255,0.3); transform: scale(1.08); }
.nav-music-btn .music-btn__icon-off { display: none; }
.nav-music-btn.is-muted .music-btn__icon-on { display: none; }
.nav-music-btn.is-muted .music-btn__icon-off { display: inline; }

/* ── Auth buttons (non-connecté) ── */
.nav-auth { display: flex; gap: 0.5rem; align-items: center; }

/* ── User identity (home icon + nom) ── */
.nav-user-identity { display: flex; align-items: center; gap: 0.45rem; }
.nav-identity-link {
  display: flex; align-items: center; gap: 0.45rem;
  text-decoration: none;
}
.nav-identity-link:hover .nav-space-btn {
  background: rgba(245,200,66,0.2); color: #f5c842;
  border-color: rgba(245,200,66,0.55); transform: scale(1.1);
}
.nav-identity-link:hover .nav-username { color: var(--text); }
.nav-space-btn {
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(245,200,66,0.08); border: 1px solid rgba(245,200,66,0.22);
  color: var(--text-muted); flex-shrink: 0;
  transition: background 0.15s, color 0.15s, transform 0.12s, border-color 0.15s;
}
.nav-space-btn:hover {
  background: rgba(245,200,66,0.2); color: #f5c842;
  border-color: rgba(245,200,66,0.55); transform: scale(1.1);
}
/* Les pastilles dont l'image touche ses propres bords : le panda et ses oreilles
   pour le tableau de bord, l'emblème et ses quatre épées pointées vers les angles
   pour les jeux. Elles se posent DANS le disque au lieu de le remplir — un
   `cover` découpé en rond leur couperait ces pointes, ce qui se lirait comme un
   défaut de cadrage et non comme un parti pris.

   Les portraits de la boutique et de la forge, eux, n'ont que du décor à perdre
   sur les bords : ils gardent le `cover` bord à bord. Le cercle, de toute façon,
   est déjà dessiné par la bordure du bouton, et le fond ambré se voit autour. */
.nav-space-btn__img,
.nav-shortcut-btn__img--entier {
  width: 100%; height: 100%;
  padding: 2px; box-sizing: border-box;
  object-fit: contain; display: block;
  border-radius: 0;
}
.nav-username {
  font-size: 0.78rem; font-weight: 700; color: var(--text-muted); white-space: nowrap;
}

/* ── User actions (ruby, inventaire, déco) ── */
.nav-user-actions { display: flex; align-items: center; gap: 0.45rem; }
.nav-user__ruby {
  display: flex; align-items: center; gap: 0.22rem;
  color: var(--accent); font-size: 0.8rem; font-weight: 700; white-space: nowrap;
}
.gem-icon { color: var(--ruby); }
.nav-inv-btn {
  width: 1.85rem; height: 1.85rem;
  background: rgba(245,200,66,.1); border: 1px solid rgba(245,200,66,.28);
  border-radius: 50%; color: #f5c842; font-size: .85rem; cursor: pointer; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s, transform .12s;
}
.nav-inv-btn:hover { background: rgba(245,200,66,.22); transform: scale(1.09); }
.nav-logout {
  font-size: 0.72rem !important; color: var(--text-muted) !important;
  padding: 0.2rem 0.5rem !important; opacity: 0.8;
}
.nav-logout:hover { opacity: 1; color: var(--text) !important; }

/* ── Tooltips (tous éléments [data-tooltip]) ── */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%);
  background: rgba(8,8,18,0.78);
  border: none;
  color: var(--text-muted);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 0.15rem 0.4rem;
  border-radius: 4px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
  z-index: 200;
}
[data-tooltip]:hover::after { opacity: 0.72; }

/* ── Settings dropdown ── */
.nav-settings-wrap { position: relative; }
.nav-settings-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  background: rgba(8,8,18,0.97);
  border: 1px solid rgba(245,200,66,0.2);
  border-radius: 10px;
  padding: 0.35rem 0;
  min-width: 148px;
  z-index: 300;
  box-shadow: 0 10px 36px rgba(0,0,0,0.55);
  animation: menu-in 0.14s ease;
}
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nav-settings-menu[hidden] { display: none; }
.nav-settings-toggle[aria-expanded="true"]::after { opacity: 0 !important; }
.nav-settings-menu__item {
  display: block;
  padding: 0.55rem 1rem;
  color: var(--text-muted);
  font-size: 0.78rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.14s, color 0.14s;
}
/* `:focus-visible` à côté de `:hover` : au clavier, l'anneau du navigateur dit
   où l'on est, mais pas que l'entrée réagit comme sous la souris. */
.nav-settings-menu__item:hover,
.nav-settings-menu__item:focus-visible {
  background: rgba(255,255,255,0.055);
  color: var(--text);
}
/* L'admin se signale en violet, la couleur que porte déjà l'en-tête du
   panneau : l'entrée annonce la teinte de la page où elle mène. Le jeton reste
   local, comme celui de `.admin-board-header` — rien d'autre ne s'en sert. */
.nav-settings-menu__item--admin { --violet-admin: #a78bfa; }
.nav-settings-menu__item--admin:hover,
.nav-settings-menu__item--admin:focus-visible {
  color: var(--violet-admin);
  background: rgba(167, 139, 250, 0.12);
}
.nav-settings-menu__item--danger { border-top: 1px solid rgba(255,255,255,0.06); margin-top: 0.2rem; }
.nav-settings-menu__item--danger:hover,
.nav-settings-menu__item--danger:focus-visible { color: #f08080; background: rgba(255,80,80,0.08); }

/* ── Réglages du son ─────────────────────────────────────────────────────
   Le voile ne sert qu'en dessous de 768 px, où le panneau devient une boîte
   centrée : sur grand écran il se pose sous son bouton et le clic dehors
   suffit à le refermer. */
.reglages-son-voile {
  position: fixed;
  inset: 0;
  z-index: 1099;
  background: rgba(0,0,0,0.55);
  opacity: 0;
  transition: opacity 0.18s ease;
}
.reglages-son-voile.est-ouvert { opacity: 1; }
@media (min-width: 769px) {
  .reglages-son-voile { display: none; }
}

.reglages-son {
  position: fixed;
  z-index: 1100;
  width: 15rem;
  padding: 0.85rem 0.95rem 0.7rem;
  background: rgba(12, 8, 5, 0.97);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: var(--r-md);
  box-shadow: 0 8px 28px rgba(0,0,0,0.5);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.reglages-son.est-ouvert {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Le son coupé se lit sur les curseurs, et pas seulement sur l'icône : un
   curseur qui reste vif alors que rien ne sort donne le sentiment d'un réglage
   cassé. Seul le curseur s'efface — les libellés et les lignes d'aide gardent
   leur contraste, sinon couper le son rendrait le panneau illisible au moment
   précis où il faut le relire pour le rétablir. */
.reglages-son.est-coupe .volume-slider { opacity: 0.4; }

.reglages-son__entete {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}
.reglages-son__titre {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.reglages-son__couper {
  margin-left: auto;
  width: 1.85rem; height: 1.85rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 50%;
  padding: 0;
  font-size: 0.9rem; line-height: 1;
  color: var(--text);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.reglages-son__couper:hover {
  background: rgba(255,255,255,0.13);
  border-color: rgba(255,255,255,0.3);
}
.reglages-son__couper .music-btn__icon-off { display: none; }
.reglages-son__couper.is-muted { border-color: rgba(255,80,80,0.4); }
.reglages-son__couper.is-muted .music-btn__icon-on { display: none; }
.reglages-son__couper.is-muted .music-btn__icon-off { display: inline; }

.reglages-son__ligne { margin-bottom: 0.7rem; }
.volume-slider { transition: opacity 0.2s; }
.reglages-son__ligne:last-of-type { margin-bottom: 0.5rem; }
.reglages-son__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.3rem;
  font-size: 0.78rem;
  color: var(--text);
  cursor: pointer;
}
.reglages-son__valeur {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
/* `--text-dim` (#5a4030) tombe sous 3:1 sur le fond du panneau : c'est la
   couleur des mentions qu'on ne lit pas, et ces deux lignes-ci disent ce que
   chaque curseur règle. Elles prennent donc le muted du site, qui passe. */
.reglages-son__aide {
  margin: 0.3rem 0 0;
  font-size: 0.65rem;
  line-height: 1.35;
  color: var(--text-muted);
}
.reglages-son__note {
  margin: 0;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(255,255,255,0.07);
  font-size: 0.62rem;
  color: var(--text-muted);
}

/* L'infobulle « Son » du bouton se posait par-dessus le panneau qu'il vient
   d'ouvrir : elle ne sert qu'à annoncer ce qui est maintenant à l'écran. */
[data-reglages-son][aria-expanded="true"]::after { content: none; }
.reglages-son__fermer {
  position: absolute;
  top: 0.4rem; right: 0.4rem;
  display: none;
  width: 1.7rem; height: 1.7rem;
  align-items: center; justify-content: center;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 50%;
  color: var(--text-muted);
  font-size: 0.75rem;
  cursor: pointer;
}

/* En boîte centrée, la croix devient nécessaire : le doigt n'a pas de « clic
   à côté » aussi évident que la souris, et le voile seul ne le dit pas. */
@media (max-width: 768px) {
  .reglages-son {
    top: 50% !important;
    left: 50% !important;
    width: min(20rem, calc(100vw - 2rem));
    padding: 1.1rem 1.25rem 0.9rem;
    border-radius: var(--r-lg);
    transform: translate(-50%, -44%);
  }
  .reglages-son.est-ouvert { transform: translate(-50%, -50%); }
  .reglages-son__fermer { display: inline-flex; }
  .reglages-son__entete { padding-right: 1.6rem; }
}

/* Une fenêtre qui surgit sans glisser reste une fenêtre qui s'ouvre. */
@media (prefers-reduced-motion: reduce) {
  .reglages-son,
  .reglages-son.est-ouvert,
  .reglages-son-voile { transition: none; transform: none; }
  @media (max-width: 768px) {
    .reglages-son,
    .reglages-son.est-ouvert { transform: translate(-50%, -50%); }
  }
}

/* Range slider */
.volume-slider {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 4px;
  border-radius: 2px;
  cursor: pointer;
  min-width: 0;
}
/* L'`outline: none` posé ici rendait le curseur invisible au clavier : c'est
   pourtant le seul moyen de régler le volume sans souris. Le contour revient,
   et seulement quand le clavier l'a demandé. */
.volume-slider:focus { outline: none; }
.volume-slider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 2px;
}
.volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
  box-shadow: 0 0 6px rgba(232,150,16,0.5);
  transition: transform 0.15s;
}
.volume-slider::-webkit-slider-thumb:hover { transform: scale(1.2); }
.volume-slider::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
  border: none;
  box-shadow: 0 0 6px rgba(232,150,16,0.5);
}


/* === 6. BUTTONS === */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 1.25rem;
  border-radius: var(--r-md);
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.2s;
  border: 1px solid transparent;
  text-decoration: none;
  white-space: nowrap;
}
.btn-primary {
  background: var(--accent);
  color: #0c0805;
  border-color: var(--accent);
}
.btn-primary:hover {
  background: var(--accent-hover);
  color: #0c0805;
  box-shadow: var(--shadow-accent);
}
.btn-outline {
  background: transparent;
  color: var(--text);
  border-color: var(--border-card);
}
.btn-outline:hover { border-color: var(--accent-border); color: var(--accent); }
.btn-danger  { background: var(--ruby); color: #fff; border-color: var(--ruby); }
.btn-danger:hover { background: #e02535; color: #fff; }
.btn-sm { padding: 0.3rem 0.7rem; font-size: 0.8rem; }

/* === 7. HERO === */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding-top: var(--nav-h);
}
.hero__bg {
  position: absolute;
  inset: 0;
  background-image: url('/images/hero-bg.png');
  background-size: cover;
  background-position: center top;
  filter: brightness(0.5) saturate(0.9);
}
.hero__gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(12,8,5,0.1) 0%, rgba(12,8,5,0.5) 60%, var(--bg) 100%);
}
.hero__content {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 3rem;
  padding-top: 3rem;
  padding-bottom: 6rem;
}
.hero__mascot { display: flex; justify-content: center; align-items: flex-end; }
.hero__mascot img {
  height: 62vh;
  width: auto;
  max-width: none;
  filter: drop-shadow(0 12px 40px rgba(232,150,16,0.22));
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-14px); }
}
.hero__fg {
  position: absolute;
  inset: 0;
  background-image: url('/images/hero-bg.png');
  background-size: cover;
  background-position: center top;
  -webkit-mask-image: linear-gradient(to top right, black 0%, black 12%, rgba(0,0,0,0.6) 22%, transparent 38%);
  mask-image: linear-gradient(to top right, black 0%, black 12%, rgba(0,0,0,0.6) 22%, transparent 38%);
  z-index: 2;
  pointer-events: none;
}
.hero__text { max-width: 520px; }
.hero__title { margin-bottom: 1rem; }
.hero__title .text-accent { display: block; font-size: 1.2em; }
.hero__desc {
  color: var(--text-muted);
  font-size: 1rem;
  margin-bottom: 2rem;
  line-height: 1.75;
}
.hero__ctas { display: flex; gap: 0.75rem; flex-wrap: wrap; }

/* === 8. SECTION HEADERS === */
.section-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 2rem;
}
.section-header h2 { margin: 0; }
.section-link { font-size: 0.875rem; color: var(--accent); font-weight: 700; }
.section-link:hover { color: var(--accent-hover); }

/* === 9. CREATURE CARDS === */
.creatures-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 1rem;
}
.creature-card {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
}
.creature-card:hover {
  border-color: var(--accent-border);
  box-shadow: var(--shadow-accent);
  transform: translateY(-4px);
}
.creature-card__img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--bg-card-alt);
  padding: 0.5rem;
}
.creature-card__body { padding: 0.75rem; }
.creature-card__name {
  font-family: var(--font-display);
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.25rem;
}
.creature-card__desc {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-bottom: 0.6rem;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.creature-card__price {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-muted);
  background: var(--ruby-dim);
  border: 1px solid rgba(204,34,51,0.25);
  border-radius: 50px;
  padding: 0.15rem 0.55rem;
  white-space: nowrap;
  flex-shrink: 0;
}
.creature-card__price .gem-icon { font-size: 0.85em; }

/* === 10. HOMEPAGE WIDGETS === */
.home-widgets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
}
.widget {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  padding: 1.25rem;
}
.widget__title {
  font-size: 0.67rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 700;
  margin-bottom: 1rem;
}
.widget-game { display: flex; align-items: center; gap: 0.75rem; }
.widget-game__name { font-family: var(--font-display); font-size: 0.9rem; font-weight: 700; margin-bottom: 0.15rem; }
.widget-game__desc { font-size: 0.78rem; color: var(--text-muted); }
.widget-account__name { font-family: var(--font-display); font-size: 1rem; font-weight: 700; margin-bottom: 0.2rem; }
.widget-account__date { font-size: 0.78rem; color: var(--text-muted); margin-bottom: 1rem; }
.widget-stats { display: flex; gap: 2rem; }
.widget-stat { text-align: center; }
.widget-stat__value {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--accent);
}
.widget-stat__label { font-size: 0.68rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }
.widget-link {
  display: block;
  text-align: right;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--accent);
  margin-top: 1rem;
}

/* === 11. FEATURES === */
.features-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  text-align: center;
}
.feature-card {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  padding: 2rem 1.25rem;
  transition: border-color 0.2s, transform 0.2s;
}
.feature-card:hover { border-color: var(--accent-border); transform: translateY(-2px); }
.feature-card__icon { font-size: 2rem; margin-bottom: 1rem; display: block; }
.feature-card__title { font-family: var(--font-display); font-size: 0.95rem; font-weight: 700; margin-bottom: 0.5rem; }
.feature-card__desc  { font-size: 0.82rem; color: var(--text-muted); }

/* === 12. CTA BANNER === */
.cta-banner {
  background: linear-gradient(135deg, #1a0e04 0%, #0e1525 100%);
  border: 1px solid var(--border-card);
  border-radius: var(--r-xl);
  padding: 3rem 2.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  overflow: hidden;
  position: relative;
}
.cta-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 20% 50%, var(--accent-dim) 0%, transparent 60%);
  pointer-events: none;
}
.cta-banner__text { position: relative; }
.cta-banner__title { font-size: clamp(1.1rem, 2.5vw, 1.7rem); margin-bottom: 0.5rem; }
.cta-banner__desc  { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem; }
.cta-banner__mascot { max-width: 160px; flex-shrink: 0; position: relative; }

/* === 13. NEWS === */
.news-list { display: flex; flex-direction: column; gap: 0.75rem; }
.news-item {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  padding: 1.1rem 1.4rem;
  transition: border-color 0.2s;
}
.news-item:hover { border-color: var(--accent-border); }
.news-item__title {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 0.3rem;
}
.news-item__content { color: var(--text-muted); font-size: 0.875rem; margin-bottom: 0.4rem; }
.news-item__meta    { font-size: 0.75rem; color: var(--text-dim); }

/* === 13b. NEWS GLASSMORPHISM (hero placement) === */
.news-glass-section {
  position: relative;
  z-index: 1;
}
.news-glass-section::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 0%, rgba(232,150,16,0.06) 0%, transparent 70%);
  pointer-events: none;
}
.news-list--glass {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1rem;
}
.news-item--glass {
  background: rgba(24, 16, 8, 0.45);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(232, 150, 16, 0.18);
  border-radius: var(--r-xl);
  padding: 1.4rem 1.6rem;
  box-shadow: 0 4px 24px rgba(0,0,0,0.45), inset 0 1px 0 rgba(232,150,16,0.08);
  transition: border-color 0.25s, box-shadow 0.25s, transform 0.25s;
}
.news-item--glass:hover {
  border-color: rgba(232, 150, 16, 0.45);
  box-shadow: 0 8px 32px rgba(0,0,0,0.55), 0 0 20px rgba(232,150,16,0.12);
  transform: translateY(-2px);
}

/* === 14. FOOTER === */
footer {
  background: var(--bg-card-alt);
  border-top: 1px solid var(--border);
  padding: 3rem 0 1.5rem;
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
  gap: 2rem;
  margin-bottom: 2.5rem;
}
.footer-brand__logo { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.9rem; }
.footer-brand__logo img { width: 34px; }
.footer-brand__name {
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 900;
  color: var(--text);
}
.footer-brand__desc { font-size: 0.82rem; color: var(--text-muted); line-height: 1.6; margin-bottom: 1.25rem; }
.footer-socials { display: flex; gap: 0.75rem; }
.footer-socials a { display: flex; align-items: center; opacity: 0.65; transition: opacity 0.2s; }
.footer-socials a:hover { opacity: 1; }
.footer-socials img { width: 26px; height: 26px; filter: brightness(0) invert(1); }

/* ── Liens externes en bouton serti ──────────────────────────
   Le relief tient en deux ombres opposées et rien d'autre : une arête claire
   posée à l'intérieur du bord haut, là où tomberait la lumière, et une ombre
   portée dessous. C'est ce qui sépare ces deux-là des liens plats de la
   colonne voisine — on doit voir qu'ils sortent de la page avant de lire ce
   qu'ils disent.

   La pastille, elle, fait l'inverse : son ombre est interne, pour que le
   portrait paraisse enfoncé dans le bouton plutôt que posé dessus. Serti au
   sens propre — le creux d'abord, la pierre dedans. */
.footer-ext {
  display: flex; flex-wrap: wrap;
  justify-content: center; gap: 0.7rem;
  margin-bottom: 1.25rem;
}

.footer-ext-btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.35rem 0.9rem 0.35rem 0.35rem;
  border-radius: 999px;
  border: 1px solid var(--accent-border);
  background: linear-gradient(160deg, rgba(255,206,148,0.10), rgba(255,206,148,0.02));
  box-shadow:
    inset 0 1px 0 rgba(255,224,180,0.18),
    0 3px 10px rgba(0,0,0,0.45);
  text-decoration: none;
  transition: transform 0.18s cubic-bezier(.34,1.56,.64,1),
              border-color 0.15s, box-shadow 0.15s;
}
.footer-ext-btn:hover,
.footer-ext-btn:focus-visible {
  transform: translateY(-2px);
  border-color: rgba(232,150,16,0.65);
  box-shadow:
    inset 0 1px 0 rgba(255,224,180,0.26),
    0 7px 18px rgba(0,0,0,0.5),
    0 0 16px rgba(232,150,16,0.22);
}
/* L'anneau du navigateur reste par-dessus : le relief seul ne dit pas où est
   le focus quand on navigue au clavier. */
.footer-ext-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Le bouton s'enfonce sous le doigt : ombre portée coupée, ombre interne mise. */
.footer-ext-btn:active {
  transform: translateY(0);
  box-shadow: inset 0 2px 7px rgba(0,0,0,0.55);
}

.footer-ext-btn__badge {
  width: 2.5rem; height: 2.5rem; flex-shrink: 0;
  border-radius: 50%; overflow: hidden;
  border: 1px solid rgba(232,150,16,0.4);
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.6), 0 0 10px rgba(232,150,16,0.12);
}
/* Les deux planches sont des carrés, et la pastille aussi : `object-fit` n'a
   donc rien à recadrer — il ferait rentrer le dessin entier dans 40 px, où le
   visage tiendrait dans un huitième du cercle. On agrandit donc l'image, et on
   la fait glisser jusqu'à ce que la tête vienne au centre.

   130 % et pas davantage : les deux personnages ont une chevelure large et la
   tête haute dans le cadre, et au-delà le cercle mordait dessus — la couronne
   d'Astralumen et le chignon de Lumenforge s'en allaient par le haut. Le
   glissement vertical est nul pour la même raison : rien à récupérer au-dessus
   du bord de l'image. Reste l'horizontale, où les deux sont décalées vers la
   droite, chacune à sa mesure. */
.footer-ext-btn__badge img {
  width: 130%; height: 130%; display: block;
  object-fit: cover;
}
.footer-ext-btn__badge--astra img { transform: translate(-15%, 0); }
.footer-ext-btn__badge--caro  img { transform: translate(-13%, 0); }

.footer-ext-btn__text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.footer-ext-btn__name {
  font-family: var(--font-display);
  font-size: 0.8rem; font-weight: 900;
  color: var(--text); letter-spacing: 0.03em;
}
.footer-ext-btn__sub {
  font-size: 0.58rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.footer-ext-btn:hover .footer-ext-btn__sub,
.footer-ext-btn:focus-visible .footer-ext-btn__sub { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .footer-ext-btn { transition: border-color 0.15s, box-shadow 0.15s; }
  .footer-ext-btn:hover,
  .footer-ext-btn:focus-visible,
  .footer-ext-btn:active { transform: none; }
}

.footer-col__title {
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 1rem;
}
.footer-col ul { display: flex; flex-direction: column; gap: 0.5rem; }
.footer-col ul li a { font-size: 0.85rem; color: var(--text-muted); transition: color 0.2s; }
.footer-col ul li a:hover { color: var(--text); }
.footer-bottom {
  border-top: 1px solid var(--border);
  padding-top: 1.25rem;
  text-align: center;
  font-size: 0.78rem;
  color: var(--text-dim);
}

/* === 15. FLASH MESSAGES === */
.flash-stack { padding: 1rem 0; display: flex; flex-direction: column; gap: 0.5rem; }
.flash {
  padding: 0.75rem 1.25rem;
  border-radius: var(--r-md);
  font-size: 0.875rem;
  font-weight: 700;
  border: 1px solid transparent;
}
.flash--success { background: rgba(34,197,94,0.12); border-color: rgba(34,197,94,0.3); color: #86efac; }
.flash--danger  { background: var(--ruby-dim); border-color: rgba(204,34,51,0.3); color: #fca5a5; }
.flash--info    { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); }

/* === 16. ADMIN === */
.admin-wrapper {
  display: flex;
  min-height: 100vh;
  padding-top: var(--nav-h);
}
.admin-sidebar {
  width: 220px;
  flex-shrink: 0;
  background: var(--bg-card-alt);
  border-right: 1px solid var(--border);
  padding: 1.5rem 0;
  position: sticky;
  top: var(--nav-h);
  height: calc(100vh - var(--nav-h));
  overflow-y: auto;
}
.admin-sidebar__title {
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0 1.25rem 0.9rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.5rem;
}
.admin-sidebar__nav li a {
  display: block;
  padding: 0.4rem 1.25rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  transition: background 0.15s, color 0.15s;
}
.admin-sidebar__nav li a:hover { background: var(--accent-dim); color: var(--text); }
.admin-sidebar__nav li a.admin-sidebar__sub {
  padding-left: 2rem;
  font-size: 0.78rem;
  color: var(--text-dim);
  opacity: .8;
}
.admin-sidebar__nav li a.admin-sidebar__sub:hover { opacity: 1; color: var(--text); }
.admin-sidebar__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.4rem 1.25rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s, color 0.15s;
  font-family: inherit;
}
.admin-sidebar__toggle:hover { background: var(--accent-dim); color: var(--text); }
.admin-sidebar__arrow {
  font-size: 0.7rem;
  transition: transform 0.2s;
  display: inline-block;
}
.admin-sidebar__toggle[aria-expanded="true"] .admin-sidebar__arrow {
  transform: rotate(180deg);
}
.admin-sidebar__submenu {
  list-style: none;
  padding: 0;
  margin: 0;
}
.admin-sidebar__section {
  padding: 1rem 1.25rem 0.3rem;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 700;
}
.admin-sidebar__search {
  padding: 0 1.25rem .4rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: .5rem;
}
.admin-sidebar__search-input {
  display: block;
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 1px solid transparent;
  padding: .4rem 0;
  font-size: .85rem;
  color: var(--text-muted);
  outline: none;
  transition: color .15s, border-color .15s;
  box-sizing: border-box;
}
.admin-sidebar__search-input:focus {
  color: var(--text);
  border-bottom-color: var(--accent);
}
.admin-sidebar__search-input::placeholder {
  color: var(--text-dim);
}
/* supprime la croix native du champ search sur webkit */
.admin-sidebar__search-input::-webkit-search-cancel-button { display: none; }
.admin-content {
  flex: 1;
  padding: 2rem 2.5rem;
  min-width: 0;
}
.admin-sidebar-overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(0,0,0,.55);
  backdrop-filter: blur(2px);
  z-index: 599;
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease;
}
.admin-sidebar-overlay.is-open { opacity: 1; pointer-events: all; }
/* La pastille des commandes secondaires de l'admin : le bouton du tiroir et
   « Schéma BDD » se tenaient côte à côte dans l'en-tête sans se ressembler —
   l'un en pastille, l'autre en `.btn` sans variante, c'est-à-dire du texte gras
   sans cadre. Une seule déclaration pour les deux.

   Elle ne dit rien de l'affichage : `.admin-sidebar-trigger` garde son
   `display: none` jusqu'à 960 px, et c'est pour ça que les deux classes
   cohabitent sur le bouton du tiroir au lieu d'être fondues en une. */
.admin-btn {
  display: inline-flex;
  align-items: center; gap: .5rem;
  background: var(--bg-card-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: .4rem .8rem;
  font-size: .85rem;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  transition: color .15s, border-color .15s;
}
.admin-btn:hover { color: var(--text); border-color: var(--accent); }
/* Les pictogrammes de ces boutons sont des dessins au trait, jamais des
   émoticônes ni des caractères de la police : ils prennent la couleur du texte,
   s'alignent sur sa hauteur, et changent de teinte avec lui au survol — ce
   qu'un « ☰ » ou un « ℹ️ » ne savent pas faire. Le retrait interdit est le seul
   réglage nécessaire : une icône ne se comprime pas pour laisser de la place au
   libellé. */
.admin-btn svg { flex-shrink: 0; }

.admin-sidebar-trigger {
  display: none;
  margin-bottom: 1.25rem;
}
.admin-sidebar__mobile-close {
  display: none;
  align-items: center; justify-content: space-between;
  padding: .75rem 1.25rem;
  border-bottom: 1px solid var(--border);
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-muted);
  flex-shrink: 0;
}
.admin-sidebar__mobile-close button {
  background: none; border: none;
  color: var(--text-muted); font-size: 1.1rem;
  cursor: pointer; padding: .2rem .4rem;
  line-height: 1;
}
.admin-sidebar__mobile-close button:hover { color: var(--text); }
.admin-content > h1 { font-size: 1.4rem; margin-bottom: 1rem; }

/* ── Dashboard header ── */
.admin-board-header {
  /* Le violet ne sert qu'ici : c'est la couleur des onglets du schéma BDD, et
     l'en-tête est la seule zone de l'admin qui ne soit pas ambrée. Un jeton
     local plutôt qu'un jeton global — rien d'autre ne s'en sert. */
  --violet-admin: #a78bfa;

  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Resserré : l'en-tête ne porte plus que trois blocs sur une seule rangée —
     la mascotte, le titre et les deux boutons. Ce qu'il prend en hauteur, les
     compteurs et les tables ne l'ont pas. */
  gap: 1rem;
  margin-bottom: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--violet-admin);
}
/* Les mascottes, seules rescapées du bandeau : `.admin-page` peint déjà son
   décor sur toute la page, un second fond sous le titre n'ajoutait qu'une
   couture. Elles, elles restent — ce sont elles qui donnaient sa voix à
   l'en-tête. Caro mène au dossier, Charles aux fiches de révision. */
.admin-board-header__mascot {
  height: 76px;
  width: auto;
  flex-shrink: 0;
  filter: drop-shadow(0 8px 24px rgba(232, 150, 16, 0.18));
  animation: float 4s ease-in-out infinite;
}
/* ── Caro, porte d'entrée du dossier ──
   Une pastille dorée sertie autour de la mascotte, et aucun mot : le lien mène
   toujours au même endroit, un libellé n'aurait redit qu'une fois ce qu'on
   apprend au premier clic.

   L'anneau est creusé plutôt que posé — une ombre portée à l'intérieur en
   haut, un liseré clair en bas : la lumière vient d'en haut comme partout sur
   le site, et c'est ce qui fait la différence entre un médaillon serti et un
   cercle dessiné par-dessus.

   Le nom du lien, lui, est dans un `.visually-hidden` : l'or ne s'entend pas. */
.admin-board-header__dossier {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 7px;
  border-radius: 50%;
  text-decoration: none;
  border: 2px solid var(--accent-border);
  background: radial-gradient(
    circle at 50% 18%,
    rgba(232, 150, 16, 0.14),
    rgba(232, 150, 16, 0.03) 70%
  );
  box-shadow:
    inset 0 3px 7px rgba(0, 0, 0, 0.5),
    inset 0 -1px 0 rgba(255, 206, 148, 0.22),
    0 3px 14px rgba(232, 150, 16, 0.16);
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
}
.admin-board-header__dossier:hover,
.admin-board-header__dossier:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
  box-shadow:
    inset 0 3px 7px rgba(0, 0, 0, 0.45),
    inset 0 -1px 0 rgba(255, 206, 148, 0.3),
    0 6px 20px rgba(232, 150, 16, 0.3);
}
/* Le contour de focus épouse la pastille : un rectangle autour d'un médaillon
   rond se lit comme un défaut d'affichage plutôt que comme un repère. */
.admin-board-header__dossier:focus-visible {
  outline: 2px solid var(--accent-hover);
  outline-offset: 3px;
}

/* ── Charles, porte d'entrée des fiches de révision ──
   Le second médaillon, à droite du premier : même sertissure, même geste, un
   autre document. Deux portes côte à côte, et aucune n'est plus importante que
   l'autre — rien ne les distingue que la mascotte.

   Le seul écart est le décalage de la respiration. Les deux mascottes flottent
   sur le même cycle de quatre secondes ; côte à côte et en phase, elles montent
   et descendent comme un seul bloc, ce qui se remarque bien plus qu'un
   mouvement. Une demi-seconde suffit à les désolidariser.

   `prefers-reduced-motion` éteint l'animation des deux : la règle est posée sur
   `.admin-board-header__mascot`, plus bas, et les couvre ensemble. */
.admin-board-header__revisions .admin-board-header__mascot {
  animation-delay: -0.5s;
}

.admin-board-header__title {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--text);
  margin: 0 0 0.2rem;
}
.admin-board-header__sub {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin: 0;
  letter-spacing: 0.04em;
}
/* Les boutons restent dans le flux, centrés avec le reste : rien ne les pousse
   à l'autre bout, l'en-tête est un bloc centré et non une barre.

   Ils sont deux depuis que le bouton du tiroir les a rejoints — il occupait une
   ligne à lui seul en bas du cadre, et c'est une rangée entière de hauteur
   gagnée sur les écrans qui en ont le moins. Le repli est là pour le cas où les
   deux libellés ne tiendraient plus côte à côte. */
.admin-board-header__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
/* La marge sous le bouton du tiroir sépare celui-ci du contenu quand il est
   posé en haut de la colonne, sur les autres pages de l'admin. Ici il est dans
   une rangée, et c'est la gouttière qui espace. */
.admin-board-header__actions .admin-sidebar-trigger { margin-bottom: 0; }

/* Dans le cadre violet, les pastilles s'éclairent en violet et non en ambre :
   c'est la seule zone de l'admin qui ne soit pas ambrée. */
.admin-board-header .admin-btn:hover {
  color: var(--text);
  border-color: var(--violet-admin);
}

@media (max-width: 640px) {
  .admin-board-header__mascot { height: 56px; }
}

/* Caro flottait déjà, et personne ne l'avait éteinte. Tant qu'elle était un
   décor on pouvait en discuter ; maintenant qu'elle est un lien, la question
   ne se pose plus — on ne demande pas de viser une cible qui bouge, et un
   mouvement perpétuel dans le coin de l'œil donne la nausée à qui y est
   sensible. Le survol garde son léger relèvement : il répond à un geste, il ne
   s'impose pas. */
@media (prefers-reduced-motion: reduce) {
  .admin-board-header__mascot { animation: none; }
  .admin-board-header__dossier { transition: border-color 0.2s, box-shadow 0.2s; }
  .admin-board-header__dossier:hover,
  .admin-board-header__dossier:focus-visible { transform: none; }
}

/* ── Dashboard stat cards ── */
.admin-board-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 0.75rem;
  margin-bottom: 2.5rem;
}
.admin-board-stat {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-md);
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  transition: border-color 0.2s;
}
.admin-board-stat:hover { border-color: var(--accent-border); }
.admin-board-stat__val {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--accent);
  line-height: 1;
}
.admin-board-stat__label {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ── Section count badge ── */
.admin-section__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-dim);
  color: var(--accent);
  border-radius: 20px;
  font-size: 0.65rem;
  font-family: var(--font-body);
  font-weight: 700;
  padding: 0.05rem 0.5rem;
  margin-left: 0.5rem;
  vertical-align: middle;
}

.admin-section { margin-bottom: 2.75rem; }
.admin-section__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.9rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--border);
}
.admin-section__header h2 { font-size: 1rem; margin: 0; display: flex; align-items: center; }
.admin-empty { color: var(--text-dim); font-style: italic; font-size: 0.875rem; padding: 0.5rem 0; }
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.admin-table th {
  text-align: left;
  padding: 0.55rem 0.75rem;
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.admin-table td {
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  vertical-align: middle;
}
.admin-table tr:hover td { background: rgba(232,150,16,0.04); }
.admin-table__actions { display: flex; gap: 0.4rem; }
.admin-table code {
  background: var(--bg-card-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 0.1rem 0.4rem;
  font-size: 0.8em;
  color: var(--accent);
}

/* ── Les tableaux d'administration sur téléphone ──
   Huit colonnes ne rentrent pas dans 360 px : le tableau débordait du panneau
   de verre, et la colonne d'actions se dessinait par-dessus le fond de page.
   Chaque ligne devient donc une carte, où chaque valeur est précédée du nom de
   sa colonne — repris dans `data-label` par les gabarits.

   Les rôles ARIA portés par le balisage ne sont pas décoratifs : changer le
   `display` d'un `table`, d'un `tr` ou d'un `td` retire à l'élément sa
   sémantique de tableau dans plusieurs navigateurs. Sans eux, un lecteur
   d'écran n'annoncerait plus ni lignes ni cellules — il lirait une suite de
   blocs. Les rôles rendent explicitement ce que le `display` a enlevé. */
@media (max-width: 640px) {
  .admin-table,
  .admin-table tbody,
  .admin-table tr,
  .admin-table td { display: block; }

  /* L'en-tête est masqué et non supprimé : ses libellés sont repris un à un
     devant chaque valeur. */
  .admin-table thead { display: none; }

  .admin-table tr {
    background: var(--glass-input, var(--bg-card-alt));
    border: 1px solid var(--glass-border, var(--border));
    border-radius: var(--r-md);
    padding: 0.5rem 0.85rem;
    margin-bottom: 0.65rem;
  }
  /* Le sélecteur est aligné sur `.admin-page .admin-table tr:hover td`, plus
     spécifique et défini plus bas : sans cela, le survol d'une carte y reposait
     un voile blanc par-dessus son propre fond. */
  .admin-page .admin-table tr:hover td,
  .admin-table tr:hover td { background: none; }

  .admin-table td {
    padding: 0.3rem 0;
    border-bottom: none;
    display: grid;
    grid-template-columns: 6.5rem minmax(0, 1fr);
    gap: 0.75rem;
    align-items: baseline;
    overflow-wrap: anywhere;
  }
  .admin-table td::before {
    content: attr(data-label);
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
  }

  /* La cellule d'actions se passe d'étiquette — deux boutons s'annoncent
     d'eux-mêmes — et prend toute la largeur de la carte, détachée des valeurs
     par un filet. */
  .admin-table td.admin-table__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.4rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--glass-border, var(--border));
  }
  .admin-table td.admin-table__actions::before { content: none; }
  .admin-table td.admin-table__actions > * { flex: 1 1 auto; }
  .admin-table td.admin-table__actions .btn {
    width: 100%;
    justify-content: center;
  }
}

.admin-form-page { max-width: 640px; }
.admin-form-page__header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-bottom: 2rem;
}
/* Le titre reprend un nom saisi en base — « Modifier Artefact — Couronne des
   Cendres ». Sans `min-width: 0`, un élément flex refuse de passer sous la
   largeur de son plus long mot : le titre poussait alors le panneau hors de
   l'écran au lieu de se replier sur deux lignes. */
.admin-form-page__header h1 {
  margin: 0;
  font-size: 1.3rem;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* Action rejetée à droite de l'en-tête (« Monstres (4) », « + Ajouter »). */
.admin-form-page__header-action { margin-left: auto; }
@media (max-width: 480px) {
  /* En pile, une marge automatique n'écarte plus rien : elle ne fait que
     décaler le bouton contre le bord droit, sous un titre calé à gauche. */
  .admin-form-page__header-action { margin-left: 0; }
}
.btn-back { font-size: 0.875rem; color: var(--text-muted); }
.btn-back:hover { color: var(--text); }
.admin-confirm { max-width: 520px; }
.admin-confirm p { margin-bottom: 0.75rem; color: var(--text-muted); }
.admin-confirm__warning {
  color: #fca5a5;
  background: var(--ruby-dim);
  border: 1px solid rgba(204,34,51,0.25);
  border-radius: var(--r-md);
  padding: 0.6rem 0.9rem;
  font-size: 0.85rem;
  margin-bottom: 0.75rem !important;
}
.admin-form__stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
  gap: 1rem;
}

/* ── Grilles de champs ──
   Les gabarits posaient ces grilles en attribut `style=`, avec un nombre de
   colonnes écrit en dur. Sur un téléphone, les quatre champs de bonus
   (PV / ATQ / DEF / VIT) tombaient à une cinquantaine de pixels de large, sous
   des étiquettes en capitales qui, elles, ne rétrécissaient pas.

   Le nombre de colonnes se déduit maintenant de la place disponible : la grille
   se replie d'elle-même, sans point de rupture à tenir à jour lorsque le
   rembourrage de `.admin-content` change d'un palier à l'autre.

   `min(…, 100%)` n'est pas décoratif : sans lui, une piste de 105 px reste de
   105 px dans un conteneur plus étroit, et la grille déborde au lieu de se
   replier — le défaut classique d'`auto-fit` sur les écrans les plus étroits. */
.admin-form-grid {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(var(--col-min, 190px), 100%), 1fr));
}
.admin-form-grid--3 { --col-min: 165px; }
.admin-form-grid--4 { --col-min: 105px; }

/* Cadre de champs apparentés (l'effet élémentaire d'une rune, par exemple). */
.admin-form-fieldset {
  border: 1px solid var(--glass-border, var(--border));
  border-radius: var(--r-md);
  padding: 0.75rem 1rem 1rem;
  margin: 1rem 0;
  min-width: 0;
}
.admin-form-fieldset > legend {
  padding: 0 0.5rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}
/* Intertitre discret au-dessus d'une grille, sans le filet de
   `.admin-form-section__subtitle`. */
.admin-form-sublabel {
  margin: 0.5rem 0 0.25rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* ── Barre d'actions ──
   `.admin-form__actions` était employée par sept gabarits sans avoir jamais été
   définie : la barre restait un bloc, les boutons s'empilaient bord à bord et
   le `margin-left: auto` porté par la suppression ne s'appliquait à rien. */
.admin-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 2rem;
}
/* Écarte l'action destructrice des deux autres, pour qu'elle ne se clique pas
   dans l'élan du bouton « Enregistrer ». */
.admin-form__delete { margin-left: auto; }

/* Sous 480 px, trois boutons ne tiennent plus sur une ligne. Ils passent pleine
   largeur, et la suppression garde son écart en hauteur plutôt qu'en largeur —
   poussée à droite sur sa propre ligne, elle ne serait plus écartée de rien. */
@media (max-width: 480px) {
  .admin-page .form-actions,
  .admin-form__actions {
    flex-direction: column;
    align-items: stretch;
  }
  .admin-page .form-actions > .btn,
  .admin-form__actions > .btn { justify-content: center; }
  .admin-form__delete { margin-left: 0; margin-top: 0.5rem; }
}


/* === 16 bis. ADMIN — fond bestiaire + surfaces en verre === */
.admin-page {
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  background-repeat: no-repeat;
  position: relative;
}
/* Voile sombre pour garder le texte lisible par-dessus l'image */
.admin-page::before {
  content: '';
  position: fixed;
  inset: 0;
  background: linear-gradient(to bottom, rgba(6, 4, 2, 0.55) 0%, rgba(6, 4, 2, 0.72) 100%);
  pointer-events: none;
  z-index: 0;
}
.admin-page > .admin-sidebar,
.admin-page > .admin-content { position: relative; z-index: 1; }
.admin-page > .admin-sidebar { position: sticky; }

/* ── Sidebar en verre ── */
.admin-page .admin-sidebar {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-right: 1px solid var(--glass-border);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.admin-page .admin-sidebar__title,
.admin-page .admin-sidebar__search,
.admin-page .admin-sidebar__mobile-close { border-bottom-color: var(--glass-border); }
.admin-page .admin-sidebar__nav li a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border-left: 2px solid transparent;
}
.admin-page .admin-sidebar__nav li a:hover {
  background: rgba(255, 255, 255, 0.07);
  border-left-color: var(--accent);
  color: var(--text);
}
.admin-sidebar__gift {
  font-size: 0.95rem;
  line-height: 1;
  flex-shrink: 0;
  filter: drop-shadow(0 0 5px rgba(232, 150, 16, 0.45));
  transition: transform 0.2s ease;
}
.admin-sidebar__link--gift:hover .admin-sidebar__gift { transform: scale(1.18) rotate(-8deg); }

/* ── Panneaux de contenu en verre ── */
.admin-page .admin-board-header,
.admin-page .admin-board-group,
.admin-page .admin-section--visible,
.admin-page .admin-form-page,
.admin-page .admin-confirm,
.admin-page .admin-offer__catalog {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-xl);
  box-shadow: var(--glass-shadow);
  padding: 1.25rem 1.5rem;
}
/* Le panneau en verre passe au violet : la règle groupée juste au-dessus lui
   pose `border: 1px solid var(--glass-border)`, ambré comme le reste de
   l'admin. Seule la couleur change ici, l'épaisseur et le rayon restent ceux
   des autres panneaux. */
/* L'en-tête est le panneau de verre le plus court de l'admin : il ne porte
   qu'un titre, une mascotte et deux boutons, et tout ce qu'il prend en hauteur
   est retiré aux compteurs qui suivent. Il reprend donc le rembourrage
   horizontal des autres panneaux, mais pas leur rembourrage vertical. */
.admin-page .admin-board-header {
  margin-bottom: 0.75rem;
  padding: 0.9rem 1.5rem 0.75rem;
  border-color: var(--violet-admin);
  box-shadow: var(--glass-shadow), 0 0 24px rgba(167, 139, 250, 0.12);
}
.admin-page .admin-board-group {
  padding: 0.75rem 1rem;
  border-radius: var(--r-lg);
}
/* le margin-bottom global des stats n'a pas de sens dans un panneau */
.admin-page .admin-board-group .admin-board-stats { margin-bottom: 0; }
.admin-page .admin-section--visible { margin-bottom: 1.5rem; }
.admin-page .admin-offer__catalog { margin-bottom: 1.5rem; }
.admin-page .admin-form-page,
.admin-page .admin-confirm { padding: 1.75rem; }

/* ── Cartes de stats en verre ── */
.admin-page .admin-board-stat {
  background: var(--glass-input);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.admin-page .admin-board-stat:hover {
  background: rgba(255, 255, 255, 0.09);
  border-color: var(--accent-border);
}

/* ── Tables ── */
.admin-page .admin-table th,
.admin-page .admin-table td,
.admin-page .admin-section__header,
.admin-page .admin-form-page__header,
.admin-page .admin-form-section { border-color: var(--glass-border); }
.admin-page .admin-table tr:hover td { background: rgba(255, 255, 255, 0.05); }
.admin-page .admin-table code {
  background: var(--glass-input);
  border-color: var(--glass-border);
}

/* ── Champs de formulaire ── */
.admin-page .form-input,
.admin-page input[type="text"],
.admin-page input[type="email"],
.admin-page input[type="password"],
.admin-page input[type="number"],
.admin-page select,
.admin-page textarea {
  background: var(--glass-input);
  border-color: var(--glass-border);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.admin-page .form-input:focus,
.admin-page input:focus,
.admin-page select:focus,
.admin-page textarea:focus { border-color: var(--accent-border); }
.admin-page select option { background: #140d06; }

/* ── Divers ── */
/* Le bouton du menu burger est volontairement absent de cette liste : il ne
   s'affiche qu'en dessous de 960 px, posé par-dessus les tableaux de
   l'administration, et un remplissage à 5 % de blanc y laissait passer ce qu'il
   y avait derrière. Il garde le fond opaque de sa règle de base. */
.admin-page .admin-competence-block,
.admin-page .admin-form-section--combat {
  background: var(--glass-input);
  border-color: var(--glass-border);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.admin-page .flash {
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

@media (max-width: 900px) {
  .admin-page .admin-board-header,
  .admin-page .admin-section--visible,
  .admin-page .admin-form-page,
  .admin-page .admin-confirm { padding: 1rem; }
}

/* Sans support du backdrop-filter : surfaces opaques de repli */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .admin-page .admin-sidebar,
  .admin-page .admin-board-header,
  .admin-page .admin-board-group,
  .admin-page .admin-section--visible,
  .admin-page .admin-form-page,
  .admin-page .admin-confirm,
  .admin-page .admin-offer__catalog { background: rgba(24, 16, 8, 0.92); }
}

/* === 17. FORMS === */
.form-group { margin-bottom: 1.25rem; }
.form-group > label,
.form-group label:first-child {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.4rem;
}
.form-input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
select,
textarea {
  width: 100%;
  background: var(--bg-card-alt);
  border: 1px solid var(--border-card);
  border-radius: var(--r-md);
  padding: 0.6rem 0.85rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--text);
  transition: border-color 0.2s, box-shadow 0.2s;
  appearance: none;
}
.form-input:focus,
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-dim);
}
select option { background: #1a1008; color: var(--text); }
.form-actions { display: flex; gap: 0.75rem; margin-top: 2rem; flex-wrap: wrap; }
.form-help { font-size: 0.78rem; color: var(--text-dim); margin-top: 0.3rem; display: block; }
.form-errors { color: #fca5a5; font-size: 0.8rem; margin-top: 0.25rem; display: block; }
.form-errors ul { list-style: none; }
.form-errors ul li::before { content: '↳ '; }

/* Auth pages */
.auth-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--nav-h) + 2rem) 1rem 2rem;
}
.auth-box {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-xl);
  padding: 2.5rem;
  width: 100%;
  max-width: 440px;
}
.auth-box h1  { font-size: 1.5rem; margin-bottom: 0.4rem; }
.auth-sub     { color: var(--text-muted); margin-bottom: 2rem; font-size: 0.9rem; }
.auth-box .form-actions { flex-direction: column; margin-top: 1.5rem; }
.auth-box .form-actions .btn { justify-content: center; width: 100%; }

input[type="checkbox"] { width: auto; accent-color: var(--accent); margin-right: 0.4rem; }
.checkbox-group { display: flex; align-items: flex-start; gap: 0.4rem; }
.checkbox-group label { text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 0.875rem; }

/* === 18. SHOP === */
.shop-wrapper { padding-top: var(--nav-h); }

/* === 18 bis. ONGLETS DE LA BOUTIQUE ===
   Des chevrons qui s'emboîtent : chaque onglet pointe vers le suivant, et la
   lecture va de gauche à droite. Ce sont des liens — le serveur rend chaque
   onglet — mais un module les échange sans recharger la page, pour que la
   musique de la boutique ne reparte pas de zéro à chaque clic.

   Les couleurs ne sont pas choisies à l'œil. Le libellé fait 0.86rem gras,
   soit ~13.8 px : c'est du texte courant, et il lui faut 4.5:1. L'ambre du
   site (#e89610) ne monte qu'à 3.46:1 sur ce violet — c'est l'or plus clair,
   celui des bordures de navigation, qui passe. */
.shop-onglets {
  /* La pointe : sa largeur sert deux fois, au découpage et à l'emboîtement. */
  --chevron: 16px;
  /* De combien le bandeau mord sur le bas de la bannière.
     Il ne se déduit surtout pas de la marge de celle-ci : elle vaut 2rem au
     repos mais 1.25rem sous 768 px, et son padding bas tombe à 1rem sous
     400 px. Un chevauchement calculé à partir d'elle valait 17 px sur grand
     écran et 29 px sur mobile — assez pour manger le sous-titre. La marge est
     donc neutralisée plus bas, et cette valeur ne dépend plus que d'elle-même. */
  --chevauchement: 17px;
  /* Le jour entre deux pièces. Sans lui, les formes s'emboîteraient au pixel
     près et le bandeau ne serait qu'un rectangle plein. Il vaut 4 px parce que
     chaque chevron déborde d'un pixel de chaque côté avec sa bordure : il
     reste donc 2 px de nuit entre deux liserés dorés. */
  --couture: 4px;

  --onglet-fond:          #3a2266;  /* inactif */
  --onglet-fond-survol:   #4a2d80;
  --onglet-fond-actif:    #5b3a9e;
  /* Les rapports sont pris contre le fond de la même ligne. Une bordure borde
     deux couleurs — son propre fond et la nuit autour du bandeau — et c'est la
     moins contrastée qui compte, donc l'intérieur. */
  --onglet-bordure:       #a8862f;  /* or éteint — 3.82:1 sur --onglet-fond */
  --onglet-bordure-actif: #f5c842;  /* or vif    — 5.20:1 sur --onglet-fond-actif */
  --onglet-texte:         #c4b0e0;  /* 6.64:1 */
  --onglet-texte-survol:  #f0e6d2;  /* 8.57:1 */
  --onglet-texte-actif:   #f5c842;  /* 5.20:1 */

  position: sticky;
  top: var(--nav-h);
  z-index: 20;                       /* sous la navigation, qui est à 100 */

  margin-top: calc(var(--chevauchement) * -1);
  /* La section qui suit apporte déjà 4rem de respiration en haut : ce reliquat
     ne sert qu'à décoller les messages flash, qui s'intercalent ici. */
  margin-bottom: 1rem;

  display: flex;
  /* Toute la largeur du contenu, comme la bannière au-dessus : les quatre
     chevrons se partagent l'espace au lieu de s'arrêter en chemin. */
  width: 100%;
  align-items: stretch;
  /* Ce fond ne se voit que dans les coutures et au liseré d'un pixel qui
     entoure la barre : les chevrons le recouvrent partout ailleurs. */
  background: var(--bg);
  padding: 1px 0;
}
/* Sans cela, la marge de la bannière s'ajouterait au chevauchement — et elle
   change de valeur à deux paliers. C'est ce qui faisait mordre le bandeau
   jusque dans le sous-titre sur mobile. */
.shop-wrapper .page-hero { margin-bottom: 0; }

.shop-onglet {
  position: relative;
  isolation: isolate;                /* garde le fond découpé derrière le texte */
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* `auto` en base : l'espace libre se partage également, mais « Élevage
     monstrueux » reste plus large que « Runes ». */
  flex: 1 1 auto;
  justify-content: center;
  padding: 0.85rem 1.7rem 0.85rem calc(1.7rem + var(--chevron));
  /* La pointe entre dans l'encoche suivante, en laissant le jour de la couture. */
  margin-right: calc(var(--couture) - var(--chevron));
  font-family: var(--font-display);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--onglet-texte);
  text-decoration: none;
  white-space: nowrap;
  /* Sans `min-width: 0`, un élément flex refuse de rétrécir sous la largeur de
     son contenu : le libellé débordait du chevron, et celui d'à côté — peint
     après lui — passait par-dessus. Il se tronque désormais, quelle que soit
     la police ou la langue. */
  min-width: 0;
  transition: color 0.18s;
}
.shop-onglet__texte {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Le découpage vit sur un calque de fond, jamais sur le lien : `clip-path`
   rognerait l'anneau de focus avec le reste, et la tabulation deviendrait
   invisible. */
.shop-onglet__fond {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--onglet-fond);
  clip-path: polygon(
    0 0,
    calc(100% - var(--chevron)) 0,
    100% 50%,
    calc(100% - var(--chevron)) 100%,
    0 100%,
    var(--chevron) 50%
  );
  /* La bordure dorée ne peut pas être une `border` : `clip-path` la découperait
     avec la forme. Quatre ombres portées sans flou, décalées d'un pixel dans
     chaque direction, tracent le contour *après* le découpage — donc le long
     du chevron, pointe et encoche comprises. */
  filter:
    drop-shadow( 1px 0 0 var(--onglet-bordure))
    drop-shadow(-1px 0 0 var(--onglet-bordure))
    drop-shadow(0  1px 0 var(--onglet-bordure))
    drop-shadow(0 -1px 0 var(--onglet-bordure));
  transition: background 0.18s;
}
/* Le premier n'a pas d'encoche à gauche, le dernier pas de pointe à droite :
   le bandeau commence et finit droit. */
.shop-onglet:first-child { padding-left: 1.7rem; }
.shop-onglet:first-child .shop-onglet__fond {
  clip-path: polygon(0 0, calc(100% - var(--chevron)) 0, 100% 50%, calc(100% - var(--chevron)) 100%, 0 100%);
}
.shop-onglet:last-child { margin-right: 0; }
.shop-onglet:last-child .shop-onglet__fond {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--chevron) 50%);
}
.shop-onglet:hover { color: var(--onglet-texte-survol); }
.shop-onglet:hover .shop-onglet__fond { background: var(--onglet-fond-survol); }

/* L'état courant ne peut pas reposer sur la couleur du texte : l'or et la
   lavande ne se distinguent qu'à 1.24:1 l'un de l'autre. C'est le liseré doré
   du bas — 5.20:1 sur son propre fond — qui porte l'information, doublé du
   violet plus clair et de `aria-current`. */
.shop-onglet--actif { color: var(--onglet-texte-actif); z-index: 1; }
/* Ce liseré est peint *dans* le fond, en dégradé : une bordure serait découpée
   par le `clip-path` en même temps que la forme. */
.shop-onglet--actif .shop-onglet__fond {
  --onglet-bordure: var(--onglet-bordure-actif);
  background: linear-gradient(
    to bottom,
    var(--onglet-fond-actif) 0 calc(100% - 4px),
    var(--onglet-texte-actif) calc(100% - 4px) 100%
  );
}
.shop-onglet:focus-visible {
  outline: 2px solid var(--onglet-texte-actif);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.shop-onglet__icone { font-size: 1rem; line-height: 1; }

/* Les quatre libellés à pleine taille réclament ~792 px de fenêtre. En dessous,
   ils se resserrent — et sous 680 px ils cèdent la place aux seules icônes.
   Le premier palier manquait : entre 640 et 792 px les libellés restaient
   affichés sans tenir, et le chevron voisin les recouvrait. */
@media (max-width: 1023px) {
  .shop-onglets { --chevron: 12px; }
  .shop-onglet {
    font-size: 0.8rem;
    padding: 0.8rem 0.9rem 0.8rem calc(0.9rem + var(--chevron));
  }
  .shop-onglet:first-child { padding-left: 0.9rem; }
}

/* Sous 680 px, seules les icônes restent. Le libellé part hors écran — jamais
   `display:none`, qui le retirerait aussi de l'arbre d'accessibilité et
   laisserait le lien sans aucun nom. */
@media (max-width: 680px) {
  .shop-onglets { --chevron: 9px; }
  .shop-onglet {
    /* Base nulle : quatre icônes de largeur égale, la longueur du libellé
       masqué ne devant plus peser sur le partage. */
    flex: 1 1 0;
    padding: 0.75rem 0.3rem 0.75rem calc(0.3rem + var(--chevron));
  }
  .shop-onglet:first-child { padding-left: 0.3rem; }
  .shop-onglet__icone { font-size: 1.15rem; }
  .shop-onglet__texte {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* Hors de l'écran, mais dans l'arbre d'accessibilité. Pour tout ce qu'un
   lecteur d'écran doit annoncer sans que personne ne le lise : le libellé d'un
   onglet réduit à son icône, le changement de panneau. `display:none` les
   retirerait des deux à la fois. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* La bannière se resserre sous 768 px — padding bas de 1.25rem, puis 1rem sous
   400 px. Le bandeau mord d'autant moins, pour laisser le sous-titre entier. */
@media (max-width: 768px) {
  .shop-onglets { --chevauchement: 12px; }
}
@media (max-width: 400px) {
  .shop-onglets { --chevauchement: 8px; }
}

/* État vide d'un onglet : la catégorie existe, son rayon est épuisé. */
.shop-vide {
  text-align: center;
  padding: 4rem 1.5rem;
  background: var(--bg-card-alt);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
}
.shop-vide__icone { font-size: 2.6rem; line-height: 1; margin-bottom: 0.75rem; }
.shop-vide h3 { font-family: var(--font-display); margin-bottom: 0.4rem; }
.shop-vide .btn { margin-top: 1.5rem; }
.shop-vide--fete {
  padding: 2.5rem 1.5rem;
  margin-bottom: 2rem;
  border-color: var(--accent-border);
  box-shadow: var(--shadow-accent);
}
.shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1.25rem;
}
.shop-card {
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color 0.2s, transform 0.2s;
}
.shop-card:hover { border-color: var(--accent-border); transform: translateY(-3px); }
.shop-card__img { width: 100%; aspect-ratio: 1; object-fit: contain; background: var(--bg-card-alt); padding: 0.5rem; }
.shop-card__body { padding: 1rem; }
.shop-card__name { font-family: var(--font-display); font-size: 0.92rem; font-weight: 700; margin-bottom: 0.25rem; }
.shop-card__desc { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.75rem; }
.shop-card__footer { display: flex; align-items: center; justify-content: space-between; }

/* === 19. PRIVATE SPACE === */
.private-wrapper { padding-top: var(--nav-h); overflow-x: hidden; }
.page-hero {
  background: linear-gradient(to bottom, var(--bg-card-alt), var(--bg));
  padding: 2rem 0 1.25rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}

/* Le premier bloc sous un bandeau — le titre de section, la barre de filtres,
   la grille de choix d'un jeu. `.section` pose 4 rem en haut, pensés pour deux
   sections qui se suivent au milieu d'une page ; sous un bandeau qui vient
   d'annoncer où l'on est, ce vide n'apprend rien et repousse le contenu sous la
   ligne de flottaison.

   Deux sélecteurs pour deux gabarits : les pages de jeux posent la section en
   frère direct du bandeau, la forge, la boutique et le tableau de bord la
   rangent dans un conteneur partagé avec les messages flash — d'où
   `:first-of-type`, qui trouve la première `<section>` même précédée de div. */
.page-hero + .section,
.page-hero + .container > .section:first-of-type {
  padding-top: 1.25rem;
}
.page-hero__inner {
  display: flex;
  align-items: center;
  gap: 2rem;
}
.page-hero__mascot {
  height: 150px;
  width: auto;
  flex-shrink: 0;
  filter: drop-shadow(0 8px 32px rgba(232,150,16,0.18));
  animation: float 4s ease-in-out infinite;
}
.page-hero h1 { margin-bottom: 0.25rem; }
.page-hero .subtitle { color: var(--text-muted); font-size: 0.9rem; }

/* Le décor des bandeaux de jeu — celui du hub « Mes Jeux », que les cinq pages
   reprennent : on entre dans un jeu sans changer de monde. Une seule
   déclaration, le hub compris, donc aucun risque qu'une page dérive.

   La forge et la boutique posent le leur en style en ligne, avec un div en
   position absolue pour le voile. Ici le voile est simplement empilé sur l'image
   dans `background-image` : un calque de moins, et rien à faire passer par la
   CSP. Les deux rendent la même chose. */
.page-hero--jeu {
  background-image:
    linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.3) 100%),
    url('/images/background/heroGame.png');
  background-size: cover;
  background-position: center;
}

/* Le bandeau du tableau de bord porte tout le décor de la page : `.dashboard-page`
   peignait `bgGame.png` en dessous, et les deux images se disputaient le regard
   sans que le bandeau ressorte. Le fond de page est parti, le dessin du bandeau
   reste.

   Le voile est plus dense que sur les bandeaux de jeu : le nom du joueur passe
   par-dessus, et il est en haut du bandeau, là où l'image est la plus claire. */
.page-hero--tableau-de-bord {
  background-image:
    linear-gradient(to bottom, rgba(0, 0, 0, 0.65) 0%, rgba(0, 0, 0, 0.35) 100%),
    url('/images/background/hero_tableau_bord.png');
  background-size: cover;
  background-position: center;
}

/* Le compteur de créatures, remonté de l'en-tête de section dans le titre du
   bandeau : même pastille qu'avant, juste mise à l'échelle du `h1` qui la
   porte, et qui peut passer à la ligne sur un écran étroit. */
.page-hero h1 .section-count {
  font-size: 0.8rem;
  padding: 0.2rem 0.7rem;
  white-space: nowrap;
}

/* La forme commune des commandes du bandeau : un cercle de 36 px, pour rester
   une cible atteignable au doigt alors que l'icône n'en fait que 16.

   C'était le dessin du seul bouton de réglages. Le journal des quêtes l'a
   rejoint, et ce qu'ils ont en commun a pris un nom plutôt que d'être recopié —
   deux pastilles rondes dans le même bandeau doivent l'être exactement, ou pas
   du tout.

   Dorées comme les raccourcis de la barre de navigation : même famille, même
   couleur, et la même que le reste de l'application. Le bord aussi reste sur les
   jetons de l'accent. */
.page-hero__pastille {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--border-card);
  border-radius: 50%;
  color: var(--accent);
  background: rgba(0, 0, 0, 0.25);
  font-size: 1rem;
  line-height: 1;
  transition: color 0.2s, border-color 0.2s;
}
.page-hero__pastille:hover {
  color: var(--accent-hover);
  border-color: var(--accent-border);
}
/* Le contour du focus se pose à l'extérieur : sur un cercle, un contour interne
   se confondrait avec la bordure. */
.page-hero__pastille:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* La rotation porte sur l'engrenage seul, pas sur le lien : la bulle d'aide est
   un `::after` du lien, et elle penchait avec lui. */
.page-hero__reglages svg { transition: transform 0.2s; }
.page-hero__reglages:hover svg { transform: rotate(35deg); }
@media (prefers-reduced-motion: reduce) {
  .page-hero__pastille,
  .page-hero__reglages svg { transition: none; }
  .page-hero__reglages:hover svg { transform: none; }
}

/* === 20. PAGINATION === */
.menu-paginator { margin-top: 1.5rem; display: flex; justify-content: center; }
.pagination { display: flex; gap: 0.25rem; }
.pagination li a,
.pagination li span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--border-card);
  border-radius: var(--r-md);
  font-size: 0.875rem;
  color: var(--text-muted);
  transition: all 0.2s;
}
.pagination li a:hover { border-color: var(--accent-border); color: var(--accent); }
.pagination li.active span {
  background: var(--accent);
  border-color: var(--accent);
  color: #0c0805;
  font-weight: 700;
}

/* === 21. CONFIRM MODAL & FLASH === */
.confirm-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(12,8,5,0.75);
  z-index: 500;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(3px);
}
.confirm-overlay.active { display: flex; animation: fadeIn 0.2s ease; }
.confirm-box {
  background: var(--bg-card);
  border: 1px solid var(--accent-border);
  border-radius: var(--radius);
  padding: 2rem 2.5rem;
  max-width: 380px;
  width: 90%;
  text-align: center;
  animation: slideUp 0.25s ease;
}
.confirm-box__title {
  font-family: var(--font-display);
  font-size: 1.2rem;
  margin-bottom: 0.5rem;
}
.confirm-box__name { color: var(--text); font-size: 1rem; margin-bottom: 0.4rem; }
.confirm-box__price { font-size: 1.3rem; color: var(--accent); margin-bottom: 1.75rem; }
.confirm-box__actions { display: flex; gap: 1rem; justify-content: center; }

/* Bouton animé Acheter → J'achète ! */
.btn-buy {
  overflow: hidden;
  position: relative;
}
.btn-buy span {
  display: block;
  transition: transform 0.3s ease;
}
.btn-buy span::after {
  content: attr(data-hover);
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
}
.btn-buy:hover span { transform: translateY(-100%); }

/* Flash messages */
.flash {
  padding: 1rem 1.5rem;
  border-radius: var(--radius);
  margin-bottom: 1.5rem;
  font-weight: 600;
  animation: slideUp 0.3s ease;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.flash--success {
  background: rgba(40,180,100,0.12);
  border: 1px solid rgba(40,180,100,0.4);
  color: #4ade80;
}
.flash--danger {
  background: rgba(204,34,51,0.12);
  border: 1px solid rgba(204,34,51,0.4);
  color: #f87171;
}
@keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { transform: translateY(16px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

/* === 22. JEUX === */

/* La carte de jeu, désormais celle des pages d'intro seules — `--single` et
   `--fiche`, plus bas. Le hub, qui l'employait en grand dans son carrousel,
   montre maintenant ses jeux en pastilles ; leur dessin est plus bas, à la fin
   de cette section. */
/* La carte de jeu porte l'or en permanence, et non plus seulement au survol :
   c'est une vitrine, pas une ligne de liste. Le halo est posé au repos, discret,
   et se réveille au survol — une carte qui n'existe qu'une fois touchée n'attire
   personne.

   `position: relative` et `overflow: hidden` pour le filet doré du haut : il est
   tracé en `::before` sur toute la largeur, et c'est la découpe de la carte qui
   lui donne ses angles arrondis. */
.game-card {
  position: relative;
  overflow: hidden;
  /* Un fond chaud qui s'éteint vers le bas plutôt qu'un aplat : la lumière vient
     du haut, là où sont l'icône et le titre. */
  background:
    radial-gradient(120% 80% at 50% -20%, rgba(232, 150, 16, 0.10), transparent 60%),
    linear-gradient(170deg, #1b1209 0%, var(--bg-card) 55%);
  border: 1px solid var(--accent-border);
  border-radius: var(--r-xl);
  padding: 2rem 1.75rem;
  display: flex;
  flex-direction: column;
  /* Tout est centré sur l'axe de la carte : le médaillon, le titre, la
     description et le bouton. Alignés à gauche, ils dessinaient trois bords
     différents — le cercle de l'icône, la première lettre du titre, celle de la
     description — dans un cadre pourtant symétrique. `align-items` place les
     blocs, `text-align` les lignes de texte à l'intérieur. */
  align-items: center;
  text-align: center;
  gap: 0.75rem;
  box-shadow: 0 0 22px rgba(232, 150, 16, 0.08), 0 6px 20px rgba(0, 0, 0, 0.45);
  transition: border-color 0.25s, transform 0.25s, box-shadow 0.25s;
}
/* Le filet doré, plein au centre et fondu aux extrémités : il souligne la carte
   sans lui dessiner une seconde bordure. */
.game-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: 0.6;
  transition: opacity 0.25s;
}
.game-card:hover {
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 0 38px rgba(232, 150, 16, 0.26), 0 14px 34px rgba(0, 0, 0, 0.55);
}
.game-card:hover::before { opacity: 1; }
/* Le survol soulève la carte : sans cette exception, une tabulation ferait
   sauter la carte focalisée sans rien annoncer, et le focus resterait invisible
   à la souris comme au clavier. */
.game-card:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 38px rgba(232, 150, 16, 0.26), 0 14px 34px rgba(0, 0, 0, 0.55);
}
/* ── Carrousel de cartes ─────────────────────────────────────────────────────
   Le défilement est celui du navigateur, pas une position calculée en
   JavaScript : la piste se parcourt au doigt, à la molette et au clavier même
   si le script ne se charge pas, et `scroll-snap` aligne les cartes seul.
   `carrousel.js` n'ajoute que les flèches et les points. */
.carrousel {
  position: relative;
}
/* Le nombre de cartes visibles est déclaré en un seul endroit, et la largeur
   s'en déduit : les paliers n'ont qu'à changer ce compte, sans retoucher aucun
   calcul. Une carte et un débord sur téléphone, deux sur tablette, trois au
   plus sur grand écran — au-delà, elles maigriraient sans rien gagner. */
.carrousel__piste {
  --cartes: 1.18;
  --ecart: 1.25rem;

  display: flex;
  gap: var(--ecart);
  margin: 0;
  /* De la place en haut et en bas pour le halo et le soulèvement au survol,
     qu'un conteneur à défilement rognerait. */
  padding: 0.75rem 0.25rem 1.25rem;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;   /* pas de navigation arrière au bout de la piste */
  scroll-snap-type: x mandatory;
  scrollbar-width: none;            /* les flèches et les points la remplacent */
}
.carrousel__piste::-webkit-scrollbar { display: none; }

/* La piste s'attrape et se tire, comme une carte posée sur une table : le
   curseur le dit avant qu'on essaie. Seulement quand elle boucle, c'est-à-dire
   quand elle déborde — promettre un geste qui ne déplacerait rien serait pire
   que de ne rien promettre. */
.carrousel__piste--boucle { cursor: grab; }

/* Le temps du glissé, l'accroche lâche prise : elle ramène la piste au point le
   plus proche à chaque pixel parcouru, et la main qui tire se bat alors contre
   elle. `carrousel.js` la rend dès que tout s'est arrêté. */
.carrousel__piste--libre { scroll-snap-type: none; }
/* Main fermée sur la piste : le curseur change, et la sélection de texte se
   tait — sans quoi tirer une carte surlignerait son titre en bleu. */
.carrousel__piste--saisi {
  cursor: grabbing;
  user-select: none;
  -webkit-user-select: none;
}

/* La piste est focalisable pour être parcourue au clavier : il lui faut donc un
   indicateur de focus visible, et posé en `outline-offset` négatif pour qu'il
   reste dans le conteneur plutôt que de mordre sur la section voisine.
   `:focus-visible` et non `:focus` : un clic sur la piste ne doit pas dessiner
   un cadre dont la souris n'a que faire. */
.carrousel__piste:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--r-md);
}

@media (min-width: 641px)  { .carrousel__piste { --cartes: 2; --ecart: 1.35rem; } }
@media (min-width: 1024px) { .carrousel__piste { --cartes: 3; --ecart: 1.5rem; } }

/* Les écarts occupent (n − 1) intervalles : ce qui reste se partage en n.
   Le compte n'est pas entier sur téléphone : à 1,18 carte, la suivante dépasse
   d'un bon centimètre au bord de l'écran. C'est ce débord qui dit qu'il y a une
   suite — une carte pleine largeur, seule et bien cadrée, ne ressemble pas à un
   carrousel, et personne ne pense à glisser.

   L'accroche se fait au centre et non plus au bord gauche. La carte courante a
   alors ses deux voisines de part et d'autre, au lieu d'ouvrir une liste vers
   la droite avec du vide derrière elle. */
.carrousel__element {
  --ecart-centre: 0;                /* 0 au centre, 1 à un pas ou plus ; posé par le script */

  flex: 0 0 calc((100% - (var(--cartes) - 1) * var(--ecart)) / var(--cartes));
  scroll-snap-align: center;
  display: flex;
  /* Le relief : la carte du centre est pleine, les autres reculent d'autant
     qu'elles s'en éloignent. Sans transition, volontairement — la valeur est
     réécrite à chaque image du défilement, et une transition la ferait traîner
     derrière le geste au lieu de le suivre.

     La transparence reste faible : ces cartes portent du texte et un bouton, et
     une voisine à demi effacée est une voisine à demi lisible. Le relief vient
     surtout de la taille, qui ne coûte aucun contraste. */
  transform: scale(calc(1 - 0.09 * var(--ecart-centre)));
  opacity: calc(1 - 0.25 * var(--ecart-centre));
}
/* Qui s'intéresse à une carte de côté la reçoit entière, sans avoir à l'amener
   au centre d'abord. `:focus-within` surtout : au clavier, on ne peut pas lire
   en diagonale ce sur quoi on a tabulé. */
.carrousel__element:hover,
.carrousel__element:focus-within {
  transform: none;
  opacity: 1;
}

/* Les flèches sont à cheval sur le bord de la piste, moitié dehors : posées
   franchement dessus, elles masqueraient le liseré doré des cartes qu'elles
   servent ; entièrement dehors, elles sortiraient des 1,5 rem que le conteneur
   laisse de marge. À cheval, elles ne mordent que sur le rembourrage des cartes,
   jamais sur leur texte. */
.carrousel__fleche {
  position: absolute;
  top: calc(50% - 1.75rem);         /* centré sur les cartes, au-dessus des points */
  z-index: 2;
  width: 2.75rem;
  height: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--accent);
  cursor: pointer;
  border: 1px solid var(--accent-border);
  border-radius: 50%;
  background: rgba(12, 8, 5, 0.9);
  box-shadow: 0 0 18px rgba(232, 150, 16, 0.12), 0 4px 14px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: border-color 0.18s, color 0.18s, opacity 0.18s, transform 0.18s, box-shadow 0.18s;
}
.carrousel__fleche--precedent { left: -1.375rem; }
.carrousel__fleche--suivant   { right: -1.375rem; }
/* `display: flex` bat le [hidden] du navigateur : sans ce rappel, les flèches
   restaient affichées sur une piste qui ne déborde pas — visibles, cliquables,
   et sans rien à faire défiler. */
.carrousel__fleche[hidden], .carrousel__points[hidden] { display: none; }
.carrousel__fleche:hover {
  border-color: var(--accent);
  color: var(--accent-hover);
  transform: scale(1.1);
  box-shadow: 0 0 26px rgba(232, 150, 16, 0.32), 0 4px 14px rgba(0, 0, 0, 0.5);
}

.carrousel__points {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
/* Le point courant s'allonge en barreau plutôt que de simplement grossir : la
   position dans la série se lit alors d'un coup d'œil, sans compter. */
.carrousel__point {
  width: 0.45rem;
  height: 0.45rem;
  padding: 0;
  cursor: pointer;
  border: 0;
  border-radius: 999px;
  background: rgba(232, 150, 16, 0.25);
  transition: background 0.22s, width 0.22s, box-shadow 0.22s;
}
.carrousel__point:hover { background: rgba(232, 150, 16, 0.55); }
.carrousel__point--actif {
  width: 1.5rem;
  background: var(--accent);
  box-shadow: 0 0 10px rgba(232, 150, 16, 0.5);
}

@media (max-width: 640px) {
  /* Les flèches passent la main au doigt : elles mordraient sur des cartes déjà
     étroites, alors que le glissé est là et fait le même travail. Les points
     restent — ils disent où l'on en est, ce que le glissé ne dit pas. */
  .carrousel__fleche { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .carrousel__fleche:hover { transform: none; }
  .carrousel__point { transition: background 0.22s; }
  .game-card:hover { transform: none; }
  /* Le nuancier suit le défilement image par image : c'est un mouvement continu
     dans la moitié de l'écran, précisément ce dont ce réglage protège. Les
     cartes gardent leur taille ; il reste la transparence, qui distingue le
     centre sans rien faire bouger. */
  .carrousel__element { transform: none; }
}

/* Ce que rapporte un jeu : une pastille par monnaie, sur une rangée qui se
   replie. C'était `.game-card__reward`, une ligne de texte doré — une seule
   monnaie tenait dedans, et le diamant de la V3 en demande une seconde. Le
   dessin des pastilles est plus bas, avec le reste du composant.

   La hauteur minimale tient la rangée même vide : les tuiles du tableau de bord
   n'annoncent pas toutes quelque chose, et sans elle leurs boutons ne
   s'aligneraient pas d'une tuile à l'autre. */
.game-card__gains {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.3rem;
  min-height: 1.35rem;
  margin-bottom: 0.25rem;
}

/* ── Le hub en pastilles ─────────────────────────────────────────────────────
   La vitrine des jeux. C'était une piste de carrousel : les six jeux y
   défilaient de côté, une à trois cartes à la fois. Elle est devenue une
   grille, comme celle du bestiaire et pour la même raison — une collection
   qu'on peut montrer entière n'a pas à être montrée par bouts.

   Elle ne demande RIEN à JavaScript : c'est une liste, le CSS la met en
   colonnes. Pas de piste, pas de rôle de carrousel, pas d'arrêt de tabulation,
   pas de copies de cartes de part et d'autre — rien de ce qu'il fallait pour
   qu'une piste cesse d'en être une.

   Le dessin du carrousel reste dans cette feuille, plus haut : les nouvelles de
   l'accueil s'en servent toujours. */

/* Trois colonnes sur téléphone, quatre sur tablette, six au large : les six
   jeux du site tiennent alors sur une seule ligne. `minmax(0, 1fr)` et non
   `1fr` : la largeur minimale par défaut d'une piste de grille est son contenu,
   et un nom de jeu insécable ferait déborder la rangée entière. */
.hub-pastilles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 641px)  { .hub-pastilles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; } }
@media (min-width: 1024px) { .hub-pastilles { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

/* La case s'étire à la hauteur de sa rangée et la pastille la remplit : sans
   cela, un nom sur deux lignes donnerait une carte plus haute que ses voisines
   et la rangée flotterait. */
.hub-pastilles > li { display: flex; }

/* Les cases sont des cibles d'ancre : le bouton « Quitter » de chaque jeu vise
   la sienne, pour qu'on revienne sur la pastille d'où l'on est parti. Le
   navigateur y saute seul ; cette marge l'empêche de la poser sous la
   navigation, qui est fixe et haute de 92 px sur téléphone — sans quoi l'on
   arriverait sur une pastille coupée par la barre.

   Sur l'attribut et non sur la classe : une case sans identifiant n'est la
   cible de rien. */
.hub-pastilles > li[id] { scroll-margin-top: calc(var(--nav-h) + 0.75rem); }

/* La pastille entière est le lien. À cette taille, un bouton posé dedans serait
   une cible de quelques dizaines de pixels au milieu d'une case inerte — alors
   que la case ne veut dire qu'une chose : jouer à ce jeu-là.

   Le fond, le liseré et le soulèvement sont ceux de `.game-card`, en plus
   sobre : les deux vues montrent la même collection, elles n'ont pas à se
   ressembler de loin seulement. */
.hub-pastille {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.45rem;
  padding: 0.9rem 0.45rem 0.8rem;
  text-align: center;
  text-decoration: none;
  color: inherit;
  background:
    radial-gradient(120% 80% at 50% -20%, rgba(232, 150, 16, 0.10), transparent 60%),
    linear-gradient(170deg, #1b1209 0%, var(--bg-card) 55%);
  border: 1px solid var(--accent-border);
  border-radius: var(--r-lg);
  box-shadow: 0 0 14px rgba(232, 150, 16, 0.06), 0 4px 12px rgba(0, 0, 0, 0.4);
  transition: border-color 0.25s, transform 0.25s, box-shadow 0.25s;
}
/* Sur `a.` et non sur la classe : la pastille d'un jeu sans route est un
   `<span>`, et elle ne doit ni s'éclairer ni se soulever — rien ne s'ouvrira
   sous le clic. */
a.hub-pastille:hover,
a.hub-pastille:focus-visible {
  border-color: var(--accent);
  transform: translateY(-3px);
  box-shadow: 0 0 26px rgba(232, 150, 16, 0.22), 0 10px 24px rgba(0, 0, 0, 0.5);
}
a.hub-pastille:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* L'émoticône passe dans un médaillon cerclé d'or, au lieu de flotter seule :
   elle tient la même place d'une pastille à l'autre quelle que soit sa largeur,
   et répond aux pastilles rondes de la navigation.

   C'est le dernier endroit du site qui porte l'emblème d'un jeu, et le seul où
   il serve : dans une grille de six cases autrement identiques, c'est lui qu'on
   reconnaît. Sur la page d'un jeu, le titre du bandeau le nomme déjà. */
.hub-pastille__icone {
  width: 2.35rem;
  height: 2.35rem;
  display: grid;
  place-items: center;
  font-size: 1.15rem;
  line-height: 1;
  border-radius: 50%;
  border: 1px solid var(--accent-border);
  background: radial-gradient(circle at 50% 35%, rgba(232, 150, 16, 0.22), rgba(232, 150, 16, 0.04));
  box-shadow: inset 0 0 10px rgba(232, 150, 16, 0.12);
}

/* Trois colonnes sur un téléphone étroit laissent une centaine de pixels par
   nom : « Pierre-papier-ciseaux » doit pouvoir se couper, y compris au milieu
   d'un mot, plutôt que de déborder de sa case. */
.hub-pastille__nom {
  font-family: var(--font-display);
  font-size: 0.74rem;
  font-weight: 700;
  line-height: 1.25;
  overflow-wrap: anywhere;
  hyphens: auto;
}
/* La phrase du jeu, coupée à trois lignes. Une case de cette largeur ne peut
   pas la rendre entière sans s'étirer deux fois plus haut que ses voisines, et
   la grille se mettrait à ressembler à un mur irrégulier. Trois lignes suffisent
   à dire de quoi il s'agit ; le reste se lit sur la page du jeu.

   `-webkit-line-clamp` est préfixé mais c'est la seule forme que les navigateurs
   implémentent — la carte de créature s'en sert déjà, plus haut. */
.hub-pastille__desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  font-size: 0.64rem;
  line-height: 1.35;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
.hub-pastille__gains {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem;
  margin-top: auto;   /* les gains restent collés au bas, phrase courte ou longue */
  padding-top: 0.3rem;
}

/* Le jeu qu'on ne peut pas lancer : il reste offert, et le refus reste côté
   serveur, qui explique. La pastille pâlit seulement — et parce qu'une pastille
   pâle n'est pas une information, le manque est aussi dit par un signe, par un
   liseré discontinu, et par une phrase hors de l'œil. */
.hub-pastille--manquante { opacity: 0.62; }
.hub-pastille--manquante:hover,
.hub-pastille--manquante:focus-visible { opacity: 1; }
.pastille--manque {
  color: var(--text-muted);
  border-style: dashed;
  border-color: var(--text-muted);
}

/* Un jeu sans route : la pastille se voit mais ne se survole pas et ne se
   soulève pas — elle ne promet aucun geste, puisqu'il n'y en a pas. */
.hub-pastille--inerte { cursor: default; opacity: 0.7; }

@media (prefers-reduced-motion: reduce) {
  a.hub-pastille:hover,
  a.hub-pastille:focus-visible { transform: none; }
}

/* Pages intro (home_ppc, home_ttt, home_bb) */
.game-intro { max-width: 560px; margin: 0 auto; }
/* La fiche qui COIFFE un jeu au lieu de le précéder : le pierre-papier-ciseaux
   et le duel la posent au-dessus de leur plateau, sur la même page. Sur les
   quatre autres, elle est seule dans sa section et n'a rien après quoi
   respirer ; ici, « Votre champion » et « L'adversaire » se collaient au bas de
   la carte, et l'on ne voyait plus où finissait la consigne. */
.game-intro--consigne { margin-bottom: 3rem; }
@media (max-width: 768px) {
  .game-intro--consigne { margin-bottom: 2rem; }
}

/* ── La carte du menu d'un jeu ───────────────────────────────────────────────
   Une carte de jeu qui dit ce qu'il rapporte, en plus de ce qu'il est. Le vol
   est le premier à en avoir besoin : il paie à la manche, et ce tarif-là tenait
   sur le panneau qui recouvre la scène — là où c'est le plus mal placé pour
   être lu.

   Elle a porté un moment le geste en dessin animé et la liste des manches. Tout
   cela répétait la description du jeu et le sous-titre ; la consigne est
   redescendue sur le panneau de départ, au moment où elle sert.

   La carte garde son cadre et son médaillon ; ce qui change est le contenu, qui
   s'aligne à gauche. Des paragraphes centrés dessinent un bord flottant à
   chaque ligne, illisible dès qu'il y en a plus de deux. Le médaillon, la
   description et le bouton restent sur l'axe. */
/* Tout est sur l'axe de la carte : le panda et sa bulle, l'intertitre, les
   gains, la créature exigée. Elle avait été alignée à gauche du temps où elle
   portait des paragraphes de plusieurs lignes ; depuis que la description tient
   dans une bulle et que le reste est fait de pastilles, le centre redevient le
   bon axe — et c'est celui de la carte du hub, du bandeau et du bouton. */
.game-card--fiche {
  max-width: 520px;
  margin: 0 auto;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
}

/* La rangée des gains sur une fiche : centrée comme le reste, et sans la marge
   basse de `.game-card__gains`, qui sert à aligner les boutons des tuiles du
   tableau de bord — une fiche n'a qu'un bouton, tout en bas. */
.fiche__gains { margin-bottom: 0; }

.fiche__titre {
  margin: 1rem 0 0.15rem;
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Le premier intertitre suit le chapeau de près : c'est un changement de ton,
   pas un changement de sujet. */
.fiche__titre:first-of-type { margin-top: 0.25rem; }

.fiche__gain {
  max-width: 100%;
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--accent);
}
/* Le tarif à la manche, en retrait derrière le total : il dit d'où le total
   vient, et c'est la seule des deux valeurs que la carte du hub donne déjà. */
.fiche__gain-detail { font-weight: 400; color: var(--text-muted); }

/* La règle du jeu, sur le vol et l'échappée. C'est la seule chose de la fiche
   qui reste alignée à gauche : elle fait quatre à cinq lignes, et un paragraphe
   de cette longueur centré dessine un bord flottant à chaque retour. Le bloc,
   lui, est bien sur l'axe de la carte — c'est son texte qui ne l'est pas. */
.fiche__note {
  max-width: 100%;
  margin: 0.4rem 0 0;
  font-size: 0.8rem;
  line-height: 1.5;
  text-align: left;
  color: var(--text-dim);
}

/* ── Le cycle des éléments, sur la fiche du duel ─────────────────────────────
   Le cercle est détouré sur fond transparent : il n'a ni cadre ni fond à lui, et
   se pose directement sur la carte. Une ombre portée le décolle, comme les
   portraits de créature du reste du site.

   Sa taille est bornée en `min()` plutôt qu'en pourcentage seul : à 170 px les
   trois sceaux et le sens des flèches se distinguent encore, et le dessin reste
   ce qu'il est — une illustration au-dessus de la règle, pas la moitié de la
   fiche. Il a d'abord été posé à 260 px : joli, mais il repoussait le bouton
   « Lancer le duel » hors de l'écran sur un portable. */
.fiche__cycle {
  width: min(100%, 170px);
  height: auto;
  margin: 0.35rem auto 0.15rem;
  filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.55));
}

/* La règle en toutes lettres, sous le dessin : c'est elle qui fait foi, et elle
   vient de la constante que le simulateur applique. Une rangée de pastilles
   plutôt qu'une liste à puces — trois relations courtes se lisent d'un coup
   d'œil, empilées elles font un paragraphe. */
.fiche__cycle-regles {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem;
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
}
.fiche__cycle-regles li {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  background: var(--accent-dim);
  font-size: 0.75rem;
  color: var(--text);
}
.fiche__cycle-regles strong { color: var(--accent); }

.fiche__lancer { align-self: center; margin-top: 1.5rem; }

/* ── Le panda féroce et sa consigne ──────────────────────────────────────────
   Le portrait qui s'adresse déjà au joueur partout ailleurs — les bulles de
   l'entrée et de la sortie, le mot de la fin du vol et du casse-briques — passe
   devant la partie plutôt qu'après : c'est avant de jouer qu'une consigne sert.

   Le dessin est celui de `.vol-fin__mot`, à qui il emprunte sa pointe en deux
   triangles superposés. Il n'est pas partagé avec lui : la bulle du vol entre
   en scène derrière un panneau de fin de partie, avec ses retards
   d'animation, et une fiche qu'on lit n'a rien à faire de cette mise en scène. */
.fiche__mot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  margin: 0.25rem 0 0.5rem;
}
/* Le portrait, nu. Il a porté un médaillon cerclé d'or, comme celui des bulles
   de l'entrée et de la sortie — essayé, puis retiré : le cadre enfermait une
   tête déjà détourée, et il alourdissait une carte qui ne porte que le panda et
   sa bulle. L'ombre portée suffit à le décoller du fond. */
.fiche__panda {
  flex: none;
  width: clamp(3.25rem, 14vw, 4.5rem);
  height: auto;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.5));
  /* Il respire, il ne gesticule pas : un degré et demi, trois pixels. C'est ce
     qui sépare une image collée d'une bête qui attend qu'on se décide. */
  animation: fiche-panda-souffle 4.2s ease-in-out infinite;
}
@keyframes fiche-panda-souffle {
  0%, 100% { transform: translateY(0) rotate(-1.2deg); }
  50%      { transform: translateY(-3px) rotate(1.2deg); }
}

/* L'encadré doré. Le liseré sourd de `--border-card` rangeait la bulle avec les
   panneaux de service du site ; l'or la range avec ce qui parle au joueur — la
   carte du hub, les pastilles de gain, le bouton qui lance la partie. C'est le
   même `--accent-border` que la carte qui la contient, doublé d'une lueur
   discrète pour qu'elle se détache du fond de cette carte-là. */
.fiche__bulle {
  position: relative;
  margin: 0;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--accent-border);
  border-radius: var(--r-lg);
  background: rgba(232, 150, 16, 0.06);
  box-shadow: 0 0 14px rgba(232, 150, 16, 0.08);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text);
}
/* La pointe, en deux triangles superposés : le grand fait la bordure — doré
   comme elle —, le petit le fond. Un seul les aurait laissés sans liseré. Sa
   couleur est celle de la carte sous le voile ambré de la bulle : un triangle
   ne prend pas de fond translucide sans laisser voir la bordure au travers. */
.fiche__bulle::before,
.fiche__bulle::after {
  content: '';
  position: absolute;
  top: 1.15rem;
  right: 100%;
  border: 0.45rem solid transparent;
}
.fiche__bulle::before { border-right-color: var(--accent-border); }
.fiche__bulle::after  { border-right-color: #261a09; margin-right: -1px; }

/* Sur un écran étroit, le portrait passe au-dessus et la pointe pivote pour
   continuer de le viser : une bulle dont la pointe montre le vide ne se lit
   plus comme une parole. */
@media (max-width: 560px) {
  .fiche__mot { flex-direction: column; }
  .fiche__bulle::before,
  .fiche__bulle::after {
    top: auto;
    right: auto;
    bottom: 100%;
    left: 50%;
    margin-left: -0.45rem;
  }
  .fiche__bulle::before { border-right-color: transparent; border-bottom-color: var(--accent-border); }
  .fiche__bulle::after  { border-right-color: transparent; border-bottom-color: #261a09; margin-right: 0; margin-bottom: -1px; }
}

@media (prefers-reduced-motion: reduce) {
  .fiche__panda { animation: none; }
}

/* PPC */
.ppc-form { width: 100%; }
/* Les trois choix tiennent sur une ligne, à toutes les largeurs. C'était une
   rangée flex qui repliait : sur un téléphone, « Ciseaux » passait seul en
   dessous et le geste n'était plus un choix entre trois images mais entre deux
   puis une.

   Trois colonnes égales, donc, et `minmax(0, 1fr)` plutôt que `1fr` : la taille
   minimale par défaut d'une piste de grille est le contenu, et l'image de 80 px
   aurait débordé au lieu de rétrécir. C'est ce `0` qui autorise la case à
   devenir plus étroite que son image. */
.ppc-choices {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.5rem, 2.5vw, 1.25rem);
  margin-top: 1rem;
}
.ppc-choice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  padding: 1.25rem clamp(0.5rem, 3vw, 1.5rem);
  border-radius: var(--r-xl);
  border: 2px solid var(--border-card);
  background: var(--bg-card);
  transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
  user-select: none;
}
.ppc-choice:hover { border-color: var(--accent-border); transform: translateY(-3px); box-shadow: var(--shadow-accent); }
.ppc-choice input[type="radio"] { display: none; }
.ppc-choice:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-dim);
  box-shadow: 0 0 0 3px rgba(232,150,16,0.2);
}
/* L'image suit la case au lieu de la contraindre : elle plafonne à 80 px sur un
   écran large et se resserre en dessous, ce qui est la condition pour que la
   ligne tienne sans repli. `aspect-ratio` remplace la hauteur fixe, sans quoi
   une image rétrécie s'écraserait. */
.ppc-choice img {
  width: 100%;
  max-width: 80px;
  height: auto;
  aspect-ratio: 1;
  object-fit: contain;
}
/* Le libellé est le dernier à pouvoir faire déborder la case : « Ciseaux » est
   plus large que l'image aux petites tailles. Il rétrécit avec elle et ne se
   coupe jamais en deux lignes. */
.ppc-choice span {
  font-weight: 700;
  font-size: clamp(0.72rem, 2.8vw, 0.9rem);
  color: var(--text-muted);
  white-space: nowrap;
}
.ppc-choice:has(input:checked) span { color: var(--accent); }

/* Flash résultat jeu */
.flash--game {
  justify-content: center;
  font-size: 1.1rem;
  font-family: var(--font-display);
  letter-spacing: 0.05em;
  max-width: 360px;
  margin: 0 auto 1.5rem;
}
.flash--info {
  background: rgba(232,150,16,0.1);
  border: 1px solid rgba(232,150,16,0.3);
  color: var(--accent);
}

/* Layout plein écran (parties) */
body.game-fullscreen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}
.game-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1.5rem;
  height: var(--nav-h);
  background: var(--bg-nav);
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 100;
}
.game-header__back {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text-muted);
  transition: color 0.2s;
}
.game-header__back:hover { color: var(--accent); }
/* Le logo du site dans l'en-tête de jeu. Tout son dessin vient de `.nav-logo`,
   qu'il porte aussi : il ne reste ici que sa place dans la barre — poussé à
   droite, contre le compteur de rubis, là où se tenait le nom écrit qu'il
   remplace.

   C'était `.game-header__title`, un simple mot qui ne menait nulle part. La
   règle a disparu avec lui : elle recopiait la moitié de `.nav-logo__name` sans
   rien lui devoir. */
.game-header__logo { margin-left: auto; }

/* Sur un écran étroit la marque rétrécit, mais elle reste écrite : c'est la
   règle que la navigation du site s'est déjà donnée (« on garde la marque
   visible mais en plus petit plutôt que de la masquer », plus bas dans la
   feuille), et l'en-tête de jeu n'a aucune raison d'en décider autrement.

   Elle s'effaçait ici sous 420 px, pour que la barre tienne sur une ligne avec
   la sortie et le compteur de rubis. Mais un logo amputé de son nom n'est plus
   le logo du site : c'est un panda, et le joueur n'a plus rien à reconnaître.
   Resserrée, la marque tient dans la même place. */
@media (max-width: 480px) {
  .game-header__logo { gap: 0.4rem; }
  .game-header__logo img { width: 28px; height: 28px; }
  .game-header__logo .nav-logo__name { font-size: 0.72rem; letter-spacing: 0.02em; }
  .game-header__logo .nav-logo__sub  { font-size: 0.44rem; letter-spacing: 0.1em; }
}
.game-header__user {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent);
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: 1rem;
  padding-left: 1rem;
  border-left: 1px solid var(--border-card);
}
/* Le plateau se tient en haut de la place disponible, plus au milieu.

   Centré, il descendait avec la hauteur de l'écran, et la sortie posée dessous
   descendait avec lui : sur un grand écran elle finissait au ras du bas de
   page, collée au plateau et loin du regard. En haut, le jeu est là où on le
   cherche, et ce qui reste de hauteur devient de l'air sous lui — de la place
   pour la sortie, qui n'a plus à se serrer contre le plateau. */
.game-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 1.25rem 1rem 2rem;
}

/* La sortie sous le plateau, commune aux cinq jeux — `games/_quitter.html.twig`.
   C'étaient `.bb-actions` et `.vol-actions`, deux règles pour la même chose et
   qui n'étaient déjà pas d'accord sur la marge ; le morpion et
   pierre-papier-ciseaux, eux, n'avaient rien.

   La hauteur totale du bloc est déclarée en jeton plutôt que laissée à deviner :
   les scènes se taillent sur la hauteur de l'écran MOINS cette place-là, et les
   deux valeurs ne peuvent pas diverger en silence. C'est ce qui manquait quand
   la marge est passée d'un gabarit à l'autre. */
:root { --sortie-jeu: 4.5rem; }

/* La place de la sortie dans la hauteur de la page.

   Les deux marges automatiques se partagent à parts égales tout le vide que le
   plateau laisse sous lui — moitié au-dessus du bouton, moitié en dessous. Le
   plateau, lui, n'a aucune marge automatique : il reste donc en haut, contre son
   bandeau, et c'est l'intervalle entre les deux qui absorbe le vide.

   Trois dispositions ont été essayées, et chacune disait quelque chose de faux.
   Une marge fixe seule laissait la sortie coller au plateau et remonter avec lui
   sur les jeux courts, très loin du bas de page. `margin-top: auto` seul la
   chassait contre le bord de la fenêtre, à un écran du jeu qu'elle quitte — de
   si loin, un bouton n'a plus l'air d'être la sortie de quoi que ce soit. Et
   centrer le groupe entier ouvrait un vide entre le bandeau et le plateau, qui
   donnait l'air d'une page mal posée.

   Le rembourrage tient le cas où il n'y a plus rien à partager : sur un écran
   court, les marges automatiques valent zéro et ces 2 rem sont tout ce qui
   sépare encore la sortie du plateau. Un rembourrage et non une marge, parce
   qu'une marge fixe et une marge automatique ne cohabitent pas — l'automatique
   remplace l'autre — là où le rembourrage compte dans la hauteur du bloc, celle
   que `--sortie-jeu` annonce aux scènes. */
.jeu-actions {
  margin: auto 0;
  padding-top: 2rem;
  width: 100%;
  text-align: center;
}

/* Le bouton de sortie lui-même.

   Il était en `btn-outline` : un contour d'un brun sombre sur un fond brun
   sombre, à côté d'un bouton d'action doré et plein. On ne le voyait pas — et
   c'était le seul moyen de quitter la page, puisque le lien du bandeau est tout
   en haut, hors du regard de qui joue.

   Le rubis plutôt que l'or : l'or est la couleur de ce qu'on vient faire ici —
   jouer, valider, rejouer. Quitter n'est pas cela, et deux boutons dorés côte à
   côte se disputeraient le geste. Le rubis est la seconde couleur du site, déjà
   celle de la monnaie et des refus : elle se voit sans rien promettre. */
.btn-quitter {
  padding: 0.6rem 1.4rem;
  font-size: 0.95rem;
  color: #ffb3bb;
  background: var(--ruby-dim);
  border-color: rgba(204, 34, 51, 0.55);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.btn-quitter:hover,
.btn-quitter:focus-visible {
  color: #fff;
  background: var(--ruby);
  border-color: var(--ruby);
  box-shadow: 0 0 22px rgba(204, 34, 51, 0.4), 0 2px 12px rgba(0, 0, 0, 0.4);
}
/* La flèche s'écarte du texte au survol : le bouton dit où il mène avant même
   qu'on le lise. */
.btn-quitter span { transition: transform 0.18s; }
.btn-quitter:hover span { transform: translateX(-3px); }

/* Le pendant : la flèche de « Rejouer » fait son tour. Le mot dit déjà ce que
   fait le bouton ; le dessin le dit plus vite, et le mouvement au survol le
   confirme. */
.btn-rejouer .btn__icone { transition: transform 0.35s; }
.btn-rejouer:hover .btn__icone,
.btn-rejouer:focus-visible .btn__icone { transform: rotate(-180deg); }

@media (prefers-reduced-motion: reduce) {
  .btn-quitter:hover span { transform: none; }
  .btn-rejouer:hover .btn__icone,
  .btn-rejouer:focus-visible .btn__icone { transform: none; }
}

/* ── Le bandeau des pages de jeu ─────────────────────────────────────────────
   Le bandeau d'un jeu servi par `games/layout.html.twig`, où la page tient dans
   l'écran : le casse-briques, le morpion et le vol d'orage le partagent.

   Il est plus court que sur les pages qui défilent, et c'est la scène qui le
   décide : elle se taille sur la hauteur qui reste, et tout ce que le bandeau
   prend lui est retiré. D'où une hauteur fixe plutôt qu'un bandeau qui grandit
   avec son contenu — c'est elle que `--hauteur-bandeau` retranche plus bas, et
   les deux valeurs ne peuvent pas diverger.

   C'était le bandeau du vol seul (`.page-hero--vol`), les deux autres gardant
   les 150 px de mascotte des pages qui défilent : trois pages de la même
   famille, trois hauteurs de bandeau, et sur le casse-briques la moitié de
   l'écran partait dans le titre. Une seule déclaration désormais, et le jeu
   ajouté demain l'aura sans y penser.

   Le jeton se pose sur `body` et non sur le bandeau : la scène est dans une
   autre branche du document, une variable déclarée sur le bandeau ne
   l'atteindrait pas. */
body:has(.page-hero--plein-ecran) { --hauteur-bandeau: 4.5rem; }

/* Double classe, et ce n'est pas du zèle : les paliers responsives plus bas
   dans la feuille redéclarent `padding` et `margin-bottom` sur `.page-hero`
   seul. À égalité de spécificité, c'est le dernier écrit qui gagne — le bandeau
   retrouverait sa garde sur mobile et dépasserait de sa hauteur fixe, là
   précisément où l'écran est le plus court. */
.page-hero.page-hero--plein-ecran {
  height: var(--hauteur-bandeau);
  padding: 0;
  /* `.page-hero` pose une marge sous lui pour les pages qui enchaînent des
     sections ; ici c'est `.game-main` qui espace le plateau, et les deux
     s'additionneraient. */
  margin-bottom: 0;
  display: flex;
  align-items: center;
}
/* Le bandeau reste sur une ligne à toutes les largeurs : l'empilement vertical
   des autres pages (≤ 768 px) le rendrait deux fois plus haut que la hauteur
   qu'on vient de lui fixer, et son contenu déborderait. */
.page-hero--plein-ecran .page-hero__inner {
  flex-direction: row;
  text-align: left;
  gap: 0.85rem;
  width: 100%;
}
/* Le titre et son sous-titre peuvent rétrécir : sans `min-width: 0`, un élément
   flex refuse de passer sous la largeur de son texte, et une description un peu
   longue pousserait le bandeau hors de l'écran au lieu de se couper. */
.page-hero--plein-ecran .page-hero__inner > div { min-width: 0; }
.page-hero--plein-ecran .page-hero__mascot {
  height: 48px;
  animation: none;   /* l'emblème ne flotte pas dans un bandeau de cette hauteur */
}
.page-hero--plein-ecran h1 { font-size: 1.1rem; margin: 0; }
/* Une ligne, et pas deux : les descriptions des jeux sont des phrases entières,
   et le bandeau a désormais une hauteur fixe qu'une seconde ligne ferait
   déborder. La fiche du jeu, elle, la donne en entier. */
.page-hero--plein-ecran .subtitle {
  font-size: 0.78rem;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Sous 480 px, le sous-titre s'efface : le panneau de départ dit déjà l'essentiel,
   et le titre seul tient mieux à côté de l'emblème. */
@media (max-width: 480px) {
  .page-hero--plein-ecran .subtitle { display: none; }
}

/* ── Casse-briques : scène, fin de partie et toast du panda ──────────────────
   La fin de partie tenait dans un `alert()` suivi d'un `location.reload()` :
   la partie repartait toute seule, sans que le joueur ait rien demandé. Ce
   bloc lui rend la main — un toast annonce l'issue, un panneau posé sur le
   canvas propose de rejouer ou de quitter. */

.bb-scene {
  position: relative;

  /* ── La taille de la fenêtre de jeu ───────────────────────────────────────
     Le plateau garde ses 460 × 320 : c'est la taille dans laquelle `bb.js`
     dessine, et la seule où rien n'est étiré. Un canvas tiré au-delà déforme
     tout ce qu'il contient — la balle y devient un œuf, les briques des
     rectangles mous.

     Centrée plutôt qu'agrandie, donc. Le bloc prend toute la largeur pour que
     `margin-inline: auto` ait de quoi centrer, et `min()` le ramène aux 460 px
     du plateau — ou à la largeur de l'écran, quand celui-ci est plus étroit.

     La troisième borne descend la fenêtre avec la hauteur disponible : ce qui
     est retranché est la somme de ce qui entoure le plateau — l'air au-dessus
     (`--air-plateau`, posé par `.game-main`) et le rembourrage sous lui, la
     place de la sortie (`--sortie-jeu`) et le bandeau de titre
     (`--hauteur-bandeau`, nul quand il s'efface en paysage) —, reconverti en
     largeur (× 460 / 320) pour que le rapport de côté reste exact. C'est ce qui
     garde le bouton « Quitter » atteignable sans défiler sur un écran court, un
     téléphone tenu en paysage compris. La déclaration simple au-dessus sert de
     repli à un navigateur qui ne saurait pas lire celle-ci.

     `100dvh` et non `100vh` : la barre d'URL d'iOS se rétracte au défilement, et
     `vh` ferait sauter la mise en page en pleine partie. */
  display: block;
  margin-inline: auto;
  width: min(100%, 460px);
  width: min(
    100%,
    460px,
    (100dvh - var(--nav-h) - var(--air-plateau, 0.75rem) - 1rem - var(--sortie-jeu) - var(--hauteur-bandeau, 0px)) * 460 / 320
  );

  border-radius: var(--r-lg);
  overflow: hidden;

  /* Le même cadre que la scène du vol : or plein sur un fil d'un pixel, et le
     halo qui l'assoit sans l'épaissir. Le plateau portait un fil brun qui se
     perdait sur le fond de page — passable pour une vignette dans une liste, pas
     pour la seule chose de l'écran. */
  border: 1px solid var(--accent);
  box-shadow: var(--shadow), 0 0 60px rgba(90, 70, 190, 0.12), 0 0 24px rgba(232, 150, 16, 0.14);

  /* Sur la scène et non sur le canvas seul : bb.js écoute le glissé ici, et le
     doigt commence volontiers sa course sur le repère de départ posé par-dessus.
     Sans ça, ce geste-là ferait défiler la page en pleine partie. */
  touch-action: none;

  /* Améthyste, contre les briques orange et le fond brun — la raquette était
     d'un bleu translucide qui s'effaçait sur le plateau sombre.

     Le nom reste porté par la scène, et pas seulement par habitude : le repère
     de départ le lit par héritage, et bb.js le lit par `getComputedStyle` sur
     la scène pour le passer au canvas. La valeur, elle, est remontée à la
     racine le jour où le vol a eu besoin du même violet : deux hexadécimaux
     recopiés n'auraient rien eu pour les tenir d'accord. */
  --bb-raquette: var(--violet-commande);
}
/* Le canvas suit la scène en largeur et garde son rapport de côté : ses
   attributs `width` et `height` (460 × 320) le portent, et `height: auto` le
   laisse faire. Rien n'est jamais étiré, la fenêtre se réduit d'un bloc. */
.bb-scene canvas {
  display: block;
  width: 100%;
  height: auto;
  background: var(--bg-card-alt);
}

/* ── Repère de départ ───────────────────────────────────────────────────────
   Deux flèches dorées et une raquette qui fait l'aller-retour entre elles :
   le geste montré plutôt qu'une commande affichée à l'écran. Le voile couvre
   le plateau quelques secondes, puis s'efface — bb.js le retire plus tôt si le
   joueur a déjà posé le doigt ou pressé une flèche. */
.bb-depart {
  position: absolute;
  inset: 0;
  z-index: 3;                  /* au-dessus du panneau de fin, jamais visible en même temps */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 1.25rem;
  background: rgba(12, 8, 5, 0.82);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 1;
  transition: opacity 0.3s ease;   /* durée tenue d'accord avec FONDU_REPERE, dans bb.js */
}
/* Même rappel que pour .bb-fin : `display: flex` bat le [hidden] du navigateur. */
.bb-depart[hidden] { display: none; }
/* Le fondu de sortie ; pointer-events coupés pour que le glissé qui vient de
   lancer la partie continue de passer pendant qu'il s'efface. */
.bb-depart--sortie { opacity: 0; pointer-events: none; }

.bb-depart__piste {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  color: var(--accent);
}
.bb-depart__fleche {
  display: block;
  flex: none;
  animation: bb-fleche 1.8s ease-in-out infinite;
}
/* Décalée d'une demi-période : chaque flèche s'allume quand la raquette part
   de son côté. */
.bb-depart__fleche--droite { animation-delay: 0.9s; }

/* La raquette du repère reprend la forme ET la couleur de celle du jeu : le
   joueur doit reconnaître sur le plateau la barre qu'on vient de lui montrer.
   Seules les flèches restent dorées. */
.bb-depart__curseur {
  flex: none;
  width: 58px;
  height: 12px;
  border-radius: 999px;
  background: var(--bb-raquette);
  box-shadow: 0 0 14px rgba(157, 78, 221, 0.55);   /* la lueur de --bb-raquette */
  animation: bb-glisser 1.8s ease-in-out infinite;
}

@keyframes bb-fleche {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.2; }
}
@keyframes bb-glisser {
  0%, 100% { transform: translateX(-28px); }
  50%      { transform: translateX(28px); }
}

.bb-depart__texte {
  margin: 0;
  max-width: 17rem;
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.45;
  text-align: center;
  color: var(--text-muted);
}

/* Le panneau couvre la scène : la partie est finie, le canvas n'a plus rien à
   montrer derrière lui qu'un décor. */
.bb-fin {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 1.25rem;
  text-align: center;
  line-height: 1.4;
  background: rgba(12, 8, 5, 0.88);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: bb-fin-in 0.25s ease both;
}
/* `display: flex` ci-dessus bat la règle `display: none` que le navigateur
   attache à [hidden] : sans ce rappel, le panneau s'afficherait dès l'ouverture
   de la page, attribut ou pas. */
.bb-fin[hidden] { display: none; }
@keyframes bb-fin-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.bb-fin__titre {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ruby);
}
.bb-fin--victoire .bb-fin__titre { color: var(--accent); }
.bb-fin__score {
  margin: 0 0 0.9rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text-muted);
}
.bb-fin__score .gem-icon { color: var(--accent); }
.bb-fin__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
}

/* ── La partie suspendue, le temps d'un réglage ──────────────────────────────
   Le gabarit du panneau de fin — `.bb-fin` porte la mise en page et le
   `[hidden]` qui bat le `display: flex` — mais pas son ton : rien n'est fini
   ici, on a seulement ouvert les réglages du son. Le titre reprend donc la
   couleur du texte courant plutôt que le rouge du « Game over », et le voile
   est un cran plus léger : le plateau figé derrière est ce qu'on va retrouver,
   et il n'y a aucune raison de le cacher.

   Même arbitrage, mêmes valeurs que `.vol-suspendu` : les deux jeux
   s'interrompent de la même façon, ils doivent en avoir l'air. */
.bb-suspendu { background: rgba(12, 8, 5, 0.7); }
.bb-suspendu[hidden] { display: none; }
.bb-suspendu__titre {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
}
.bb-suspendu__clavier {
  margin: 0.9rem 0 0;
  font-size: 0.74rem;
  color: var(--text-dim);
}
.bb-suspendu__clavier kbd {
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-sm);
  background: var(--bg-card);
  font-family: inherit;
  font-size: 0.72rem;
  color: var(--text-muted);
}

/* Le toast reprend la mise en page de celui du pierre-feuille-ciseaux —
   avatar rond, nom, réplique — mais sur les jetons du site plutôt que sur les
   couleurs écrites en dur là-bas. */
.bb-toast {
  position: fixed;
  top: calc(var(--nav-h) + 1rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  width: max-content;
  max-width: min(92vw, 420px);
  padding: 0.6rem 1rem 0.6rem 0.6rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-xl);
  background: var(--bg-card);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  animation: toast-in 0.25s ease both;
  transition: opacity 0.3s;
}
.bb-toast__avatar {
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid var(--border-card);
  border-radius: 50%;
  background: var(--bg-card-alt);
}
.bb-toast__avatar img { width: 100%; height: 100%; object-fit: contain; }
.bb-toast__bulle { min-width: 0; text-align: left; }
.bb-toast__nom {
  display: block;
  margin-bottom: 0.1rem;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.bb-toast__texte {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
}
.bb-toast--perte { border-color: rgba(204, 34, 51, 0.5); }
.bb-toast--perte .bb-toast__avatar { border-color: rgba(204, 34, 51, 0.55); }
.bb-toast--perte .bb-toast__texte { color: #ff8a8a; }
.bb-toast--victoire { border-color: var(--accent-border); }
.bb-toast--victoire .bb-toast__avatar { border-color: var(--accent-border); }
.bb-toast--victoire .bb-toast__texte { color: var(--accent); }

@media (max-width: 560px) {
  .bb-toast {
    top: auto;
    bottom: 1rem;
    left: 1rem;
    right: 1rem;
    width: auto;
    max-width: none;
    transform: none;
    animation: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .bb-fin, .bb-toast { animation: none; }

  /* Le repère perd son va-et-vient mais garde ses flèches : c'est le mouvement
     qui gêne, pas la consigne. Les deux restent allumées, la raquette au
     milieu — l'image dit encore de quel côté ça glisse. */
  .bb-depart__fleche, .bb-depart__curseur { animation: none; }
  .bb-depart { transition: none; }
}

/* ── Vol d'Orage : scène, compteur, panneaux ─────────────────────────────────

   La scène du casse-briques est un canvas de 460 × 320 étiré par CSS : le
   plateau est fixe, et le décor avec lui. Ici la fenêtre de jeu s'adapte, et
   c'est voulu — `vol/regles.js` exprime la vitesse en écrans par seconde et
   l'espacement des obstacles en secondes, de sorte que le temps de réaction
   reste le même quel que soit le format. Aucun rapport de côté n'est donc
   imposé : celui que la page obtient est le bon, la scène s'y coule.

   Le rapport ci-dessous n'est qu'une préférence, et le format portrait en prend
   une autre. Sur une tablette, `max-height` gagne contre `aspect-ratio` et rend
   une scène un peu plus carrée — c'est sans conséquence. */

/* Le gabarit de jeu marge et centre son contenu. Les scènes veulent toute la
   hauteur qu'elles peuvent prendre : deux rem de moins en haut et en bas, c'est
   du décor en plus, et sur un téléphone en paysage ce n'est pas un détail.

   Les deux jeux ensemble, et pas le vol seul : c'est ce 1,75 rem que leurs deux
   calculs de taille retranchent, et un rembourrage différent d'une scène à
   l'autre ferait mentir l'un des deux.

   Le plateau se tient en haut de la place disponible : c'est là qu'on le
   cherche, et un grand vide ouvert entre le bandeau et lui se lit comme un
   défaut de mise en page. Le vide qu'il laisse sous lui n'est pas perdu pour
   autant — il passe entre le plateau et la sortie (`.jeu-actions`, plus bas dans
   la feuille). */
.game-main:has(.vol-scene),
.game-main:has(.echappee-scene) { padding: 0.75rem 0.75rem 1rem; }

/* ── L'air gardé SOUS le bouton de sortie, sur le vol ─────────────────────────
   Le vol seul, et il faut dire pourquoi.

   La sortie se place d'elle-même sur les jeux dont le plateau laisse du vide :
   `.jeu-actions` le partage à parts égales, moitié au-dessus du bouton, moitié
   en dessous, et le bouton se pose à mi-hauteur de ce qui reste. C'est le cas du
   casse-briques, dont le plateau est plafonné à 460 × 320.

   La scène du vol, elle, prend toute la hauteur disponible : il ne reste rien à
   partager, et la sortie tombait au ras du bord de la fenêtre, bien plus bas que
   sur l'autre jeu. Cette réserve lui rend ce vide-là. Elle est retranchée par la
   scène de la hauteur dont elle dispose — la scène perd donc exactement ce que
   la sortie gagne, et rien ne déborde de l'écran.

   11 % de la fenêtre plutôt qu'un nombre fixe, plafonnés à 5,5 rem : c'est ce
   qui place le bouton à la hauteur qu'il a sur le casse-briques pour un écran
   d'ordinateur portable. Sur un écran très haut les deux s'écartent un peu — le
   vol garde sa réserve pendant que le vide du casse-briques grandit avec l'écran
   — mais les rendre identiques partout demanderait à la page du vol de calculer
   la place du plateau de l'autre jeu, et de rapetisser sa scène d'autant. Un
   bouton aligné ne vaut pas un jeu amputé.

   Sur un écran court, la réserve se retire d'elle-même au lieu de chasser le jeu
   hors de l'écran. */
.game-main:has(.vol-scene) { --sortie-bas: clamp(0.5rem, 11dvh, 5.5rem); }

/* La sortie du vol ne partage plus : la réserve ci-dessus tient seule le
   dessous, et `margin-top` reste automatique — il absorbe ce qui pourrait rester
   ailleurs, et vaut zéro quand il n'y a rien. Le casse-briques, lui, garde le
   partage par moitiés : c'est ce qui pose sa sortie là où elle est. */
.game-main:has(.vol-scene) .jeu-actions { margin-bottom: var(--sortie-bas); }

/* Le casse-briques prend un peu d'air au-dessus de son plateau, que la scène du
   vol n'a pas : celle-ci remplit toute la hauteur disponible, l'en écarter ne
   ferait que la rapetisser d'autant. Le plateau du casse-briques, lui, fait
   460 × 320 et laisse du vide derrière lui ; collé sous le bandeau il se tenait
   trop haut.

   Une hauteur qui suit l'écran plutôt qu'un nombre fixe : 9 % de la fenêtre,
   sans descendre sous le rembourrage ordinaire ni dépasser 5,5 rem. Sur un
   téléphone tenu en paysage, où il ne reste presque rien, cet air se retire
   pratiquement tout seul et la place revient au jeu.

   Le plafond compte autant que la part : sans lui, un grand écran ouvrirait un
   vide de plus en plus grand entre le bandeau et le plateau, et finirait par
   repousser celui-ci en bas de page. Passé 1000 px de haut, l'air cesse donc de
   croître et c'est l'écart avec la sortie qui absorbe le reste.

   Le jeton est déclaré ici et relu par `.bb-scene`, qui retranche cette réserve
   de la hauteur dont elle dispose : les deux ne peuvent pas diverger, et le
   bouton « Quitter » reste atteignable sans défiler. */
.game-main:has(.bb-scene) {
  --air-plateau: clamp(0.75rem, 9dvh, 5.5rem);
  padding: var(--air-plateau) 0.75rem 1rem;
}

/* Le téléphone tenu en paysage — 390 px de haut, dont 92 pour l'en-tête de jeu.
   C'est le format où le vol se joue le mieux, et le seul où il n'y a la place
   pour rien d'autre : le bandeau s'efface, et le jeton repasse à zéro pour que
   la scène récupère exactement ce qu'il lui rendait. Le casse-briques, dont la
   fenêtre se taille maintenant sur la même hauteur, y gagne autant. */
@media (orientation: landscape) and (max-height: 540px) {
  .page-hero--plein-ecran { display: none; }
  body:has(.page-hero--plein-ecran) { --hauteur-bandeau: 0rem; }

  /* L'air sous la sortie du vol s'efface avec le bandeau, et pour la même
     raison : dans ce format il ne reste que 390 px de haut, et chaque rem gardé
     en réserve est un rem retiré au jeu. La sortie descend alors au bas de
     l'écran, là où le casse-briques met déjà la sienne faute de vide à
     partager. */
  .game-main:has(.vol-scene) { --sortie-bas: 0.25rem; }

  /* Le repère du geste s'efface au même seuil, et pour la même raison : dans ce
     format, le panneau de départ n'a pas la hauteur de tout montrer, et ce qui
     doit rester tenir est le bouton. La phrase dit toujours le geste ; c'est le
     dessin qui se retire, pas la consigne.

     Le sélecteur vise le panneau du jeu et lui seul : la fiche, elle, défile,
     et rien ne l'oblige à tenir dans une hauteur d'écran. */
  .vol-depart .vol-geste,
  .echappee-depart .echappee-geste { display: none; }
}

.vol-scene,
.echappee-scene {
  position: relative;
  width: 100%;
  max-width: 1100px;
  aspect-ratio: 16 / 9;
  max-height: calc(100dvh - var(--nav-h) - 1.75rem - var(--sortie-jeu) - var(--sortie-bas, 0px) - var(--hauteur-bandeau, 0px));

  /* Ce qui est retranché est la somme de ce qui entoure la scène : les 1,75 rem
     de rembourrage que `.game-main:has(.vol-scene)` pose au-dessus et au-dessous
     d'elle, et `--sortie-jeu`, la place du bouton « Quitter » et de la marge qui
     l'en sépare. Ce qui reste tient dans l'écran sans le faire défiler.

     La réserve était un seul nombre écrit à la main — 5,5 rem, puis 4,5 avant
     qu'il n'y manque treize pixels. Elle n'a pas suivi quand le bouton a grandi,
     et la page s'est remise à défiler pendant la partie, ce qui est le pire
     moment. Le jeton la tient maintenant d'accord avec `.jeu-actions`, qui le
     déclare.

     `--hauteur-bandeau` ajoute celle du bandeau de titre, qui est au-dessus et
     qui disparaît en paysage — la valeur de repli à zéro couvre ce cas comme
     elle couvrirait un gabarit sans bandeau.

     Le plancher est à 200 px et non à 260 : sur un téléphone tenu en paysage —
     390 px de haut, moins 70 d'en-tête et 72 de marges — il reste 248 px. À
     260, le plancher l'emportait sur le plafond et la page se mettait à
     défiler pendant la partie, ce qui est le pire moment pour ça.

     `100dvh` et non `100vh` : la barre d'URL d'iOS se rétracte au défilement,
     et `vh` ferait sauter la mise en page en plein vol. */
  min-height: 200px;
  overflow: hidden;
  /* La scène est encadrée d'or, et elle l'est toujours : partie non commencée,
     en cours, gagnée ou perdue, le cadre est le même.

     Il était brun, et l'or qu'on voyait par moments n'était pas une bordure mais
     l'anneau de focus posé plus bas. Il arrivait donc quand le focus entrait
     dans la scène — au premier envol, qui le donne au canvas, et à la chute, qui
     le donne à « Rejouer » — et repartait au premier clic ailleurs dans la page.
     Un cadre qui va et vient au gré du focus se lit comme un défaut d'affichage,
     d'autant qu'il tenait justement le rôle inverse : signaler ce qui est
     désigné au clavier.

     Or plein sur un fil d'un pixel, et non l'or au tiers de `--accent-border`
     que portent les cartes au repos : la scène n'est pas une vignette dans une
     liste, c'est la seule chose de la page, et le cadre doit se voir de loin. Le
     halo doré du `box-shadow` l'assoit sans l'épaissir. */
  border: 1px solid var(--accent);
  border-radius: var(--r-lg);
  background: #0a0718;
  box-shadow: var(--shadow), 0 0 60px rgba(90, 70, 190, 0.12), 0 0 24px rgba(232, 150, 16, 0.14);

  /* Les trois lignes qui rendent le jeu praticable au doigt. Sans la première,
     un glissé fait défiler la page pendant la partie ; sans la deuxième, le
     premier appui déclenche le « tirer pour rafraîchir » ; sans la troisième,
     chaque tap laisse un aplat bleu sur la scène. */
  touch-action: none;
  overscroll-behavior: contain;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

/* La scène se redresse en portrait : un vol qui se joue sur l'altitude a besoin
   de hauteur, pas de largeur. C'est le format de référence — le jeu est pensé
   pour le téléphone tenu droit, et adapté ensuite au reste. */
@media (orientation: portrait) {
  .vol-scene { aspect-ratio: 3 / 4; }
}

.vol-scene canvas,
.echappee-scene canvas {
  display: block;
  width: 100%;
  height: 100%;
}
/* Aucun anneau de focus sur la scène : il n'y a rien à y focaliser.

   Il y en avait un, posé en `:focus-within`, du temps où le canvas était un
   arrêt de tabulation. Il s'allumait dès que le focus entrait n'importe où dans
   la scène — donc aussi sur les boutons des panneaux, qui ont déjà le leur : le
   cadre entier s'éclairait pour dire qu'un bouton de dix caractères était
   désigné. C'était aussi lui, et non une bordure, que l'on voyait apparaître et
   disparaître au fil de la partie.

   Le canvas ne se tabule plus (`tabindex="-1"` dans le gabarit) et ne reçoit le
   focus que de la main de `vol.js`, pour le retirer d'un bouton. Un focus que
   personne n'a demandé ne s'annonce pas : la règle ci-dessous éteint l'anneau
   que le navigateur dessinerait alors. Les boutons, eux, gardent le leur — ce
   sont les seules choses que l'on atteint au clavier, et les seules à devoir
   dire qu'on les a atteintes. */
.vol-scene canvas:focus,
.echappee-scene canvas:focus { outline: none; }

/* ── Le compteur ─────────────────────────────────────────────────────────────
   Minimal, en haut à gauche, sur un fond à peine posé : il doit se lire d'un
   coup d'œil sans rien cacher du décor. */
.vol-hud,
.echappee-hud {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  pointer-events: none;
}
.vol-hud__temps,
.echappee-hud__score {
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 900;
  letter-spacing: 0.08em;
  color: var(--text);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9);
  font-variant-numeric: tabular-nums;
}
.vol-hud__jauge,
.echappee-hud__jauge {
  flex: 1;
  max-width: 220px;
  height: 3px;
  border-radius: 2px;
  background: rgba(240, 230, 210, 0.16);
  overflow: hidden;
}
.vol-hud__jauge-remplie,
.echappee-hud__jauge-remplie {
  display: block;
  width: 0;
  height: 100%;
  background: linear-gradient(90deg, #8a6cff, var(--accent));
}
/* La manche en cours, à droite de la jauge : le chronomètre dit l'effort du
   moment, celle-ci dit le chemin déjà fait. */
.vol-hud__manche,
.echappee-hud__palier {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}


/* ── Panneaux posés sur la scène ─────────────────────────────────────────── */
.vol-depart,
.vol-pause,
.vol-fin,
.vol-sans-js,
.echappee-depart,
.echappee-suspendu,
.echappee-fin,
.echappee-sans-js {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.5rem 1.25rem;
  text-align: center;
  background: rgba(8, 5, 18, 0.82);
  backdrop-filter: blur(3px);
}
/* `display: flex` bat le [hidden] du navigateur : sans ce rappel, le panneau
   caché resterait affiché. Même piège que `.bb-fin`. */
.vol-depart[hidden], .vol-pause[hidden], .vol-fin[hidden],
.echappee-depart[hidden], .echappee-suspendu[hidden], .echappee-fin[hidden] { display: none; }

.vol-fin__titre {
  margin: 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Le titre et le surtitre du panneau de départ sont partis avec le texte long :
   ils répétaient le bandeau de la page, et le `h1` du panneau en faisait un
   second sur le document. Ce qui reste — une phrase, le repère, le bouton — est
   ce qu'il faut pour partir ; le parcours et les gains sont sur la fiche. */
/* `.vol-fin__texte` n'est plus de la liste : le texte de fin du vol est passé
   dans la bulle du panda (`.vol-fin__bulle`), qui a sa propre mise en forme. */
.vol-depart__texte,
.vol-pause__texte,
.echappee-depart__texte,
.echappee-fin__texte {
  margin: 0 0 1rem;
  max-width: 34ch;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text-muted);
}
/* Un cran au-dessus du reste : c'est la seule consigne de l'écran, et elle doit
   se prendre d'un coup d'œil. */
.vol-depart__texte,
.echappee-depart__texte { font-size: 1rem; font-weight: 700; color: var(--text); }

/* ── La pause entre deux manches ─────────────────────────────────────────────
   Le même gabarit que l'écran de départ, et c'est voulu : le joueur y retrouve
   une phrase et un bouton à la même place. Ce qui change est le ton — la pause
   annonce une réussite, elle ne présente pas le jeu.

   L'encart vert porte cette réussite. Les valeurs sont celles des messages de
   succès du site (`.flash--success`) plutôt qu'un vert inventé pour l'occasion :
   c'est la même promesse, elle doit avoir la même couleur. */
.vol-pause__succes {
  margin: 0 0 1rem;
  padding: 0.9rem 1.4rem;
  border: 1px solid rgba(34, 197, 94, 0.3);
  border-radius: var(--r-lg);
  background: rgba(34, 197, 94, 0.12);
  box-shadow: 0 0 34px rgba(34, 197, 94, 0.18);
}
.vol-pause__titre {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 4.5vw, 1.75rem);
  font-weight: 900;
  color: #86efac;
}
/* Le coche redit en forme ce que le vert dit en couleur : l'encart reste lisible
   pour qui ne distingue pas le vert du reste. */
.vol-pause__coche {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 1.5em;
  height: 1.5em;
  border-radius: 50%;
  background: rgba(34, 197, 94, 0.25);
  font-size: 0.7em;
  line-height: 1;
}
/* Ce que la manche a mis à l'abri : c'est tout l'intérêt du découpage, et la
   seule chose de la page de pause qui ne se devine pas. Le rubis garde sa
   couleur à lui, même dans l'encart vert — c'est celle qu'il a partout. */
.vol-pause__acquis {
  margin: 0.5rem 0 0;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--accent);
}
.vol-pause__acquis[hidden] { display: none; }
/* Le montant est l'information, le moment du versement la précision : elle est
   posée sous le chiffre, plus petite et sans l'or du rubis, pour qu'on lise
   d'abord combien et ensuite quand. */
.vol-pause__mention {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-muted);
}

/* ── Le vol suspendu, le temps d'un réglage ──────────────────────────────────
   Le gabarit de la pause — `.vol-pause` porte la mise en page et le `[hidden]`
   qui bat le `display: flex` — mais pas son encart vert : rien n'est gagné ici,
   on a seulement ouvert les réglages du son. Le titre reprend donc le ton neutre
   du panneau de fin, et le voile est un cran plus léger : la scène figée derrière
   est ce qu'on va retrouver, et il n'y a aucune raison de la cacher. */
.vol-suspendu,
.echappee-suspendu { background: rgba(8, 5, 18, 0.7); }
.vol-suspendu[hidden] { display: none; }
.vol-suspendu__titre,
.echappee-suspendu__titre {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
}
/* ── Le geste du départ ──────────────────────────────────────────────────────
   Un doigt qui tape, l'onde de l'appui, un dragon qui monte. Les trois
   animations partagent la même période de 1,8 s et se décalent à l'intérieur :
   l'onde part quand le doigt touche, le dragon monte dans la foulée. Des durées
   indépendantes auraient dérivé l'une par rapport à l'autre en quelques
   boucles, et le geste n'aurait plus rien expliqué.

   Le violet est celui des commandes de jeu (`--violet-commande`) : c'est
   déjà la couleur de ce que le joueur tient sous le doigt, elle dit la même
   chose ici. */
.vol-geste {
  --vol-geste-periode: 1.8s;
  /* De combien le dragon du repère s'élève. La hauteur du bloc réserve cette
     course au-dessus de lui : sans elle, la pastille sortait par le haut et
     passait sur le titre. */
  --vol-geste-envol: 2.1rem;

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Tout est calé en bas, et la place libre est donc au-dessus : c'est là que
     le dragon monte. Caler en haut l'aurait fait sortir du bloc. */
  justify-content: flex-end;
  gap: 0.45rem;
  margin: 0.5rem 0 1rem;
  height: calc(2.2rem + 0.45rem + 1.05rem + var(--vol-geste-envol));
  color: var(--violet-commande-clair);
}

/* Le dragon du repère : une pastille, pas une silhouette. Ce qu'il faut
   comprendre est « ça monte quand je tape », et un dessin détaillé à cette
   taille ne dirait que « il y a un dragon ». */
.vol-geste__dragon {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  background: var(--violet-commande);
  box-shadow: 0 0 16px var(--violet-commande-lueur);
  animation: vol-geste-monte var(--vol-geste-periode) ease-in-out infinite;
}

.vol-geste__appui {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
}
.vol-geste__doigt {
  display: block;
  position: relative;
  z-index: 1;
  animation: vol-geste-tape var(--vol-geste-periode) ease-in-out infinite;
}
/* L'onde part du bout de l'index, pas du centre du dessin : c'est là que le
   doigt touche, et une onde centrée sur la paume se lirait comme un halo. Le
   bout de l'index est à 8/24 et 2/24 du `viewBox`, soit 33 % et 8 % des 34 px
   du dessin. */
.vol-geste__onde {
  position: absolute;
  top: 0.18rem;
  left: 33%;
  width: 2.5rem;
  height: 2.5rem;
  margin: -1.25rem 0 0 -1.25rem;
  border: 2px solid var(--violet-commande);
  border-radius: 50%;
  opacity: 0;
  animation: vol-geste-onde var(--vol-geste-periode) ease-out infinite;
}

@keyframes vol-geste-tape {
  0%, 6%    { transform: translateY(0); }
  18%       { transform: translateY(7px); }
  34%, 100% { transform: translateY(0); }
}
@keyframes vol-geste-onde {
  0%, 14%   { transform: scale(0.35); opacity: 0; }
  22%       { opacity: 0.8; }
  58%, 100% { transform: scale(1.9); opacity: 0; }
}
@keyframes vol-geste-monte {
  0%, 16%  { transform: translateY(0); }
  46%      { transform: translateY(calc(-1 * var(--vol-geste-envol))); }
  100%     { transform: translateY(0); }
}

.vol-depart__clavier,
.echappee-depart__clavier {
  margin: 0.9rem 0 0;
  font-size: 0.74rem;
  color: var(--text-dim);
}
.vol-depart__clavier kbd,
.echappee-depart__clavier kbd {
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-sm);
  background: var(--bg-card);
  font-family: inherit;
  font-size: 0.72rem;
  color: var(--text-muted);
}

.vol-fin__titre,
.echappee-fin__titre {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 4.5vw, 1.75rem);
  font-weight: 900;
  letter-spacing: 0.04em;
  text-transform: none;
  color: #ff8a8a;
}
.vol-fin--victoire .vol-fin__titre {
  color: var(--accent);
  text-shadow: 0 0 30px rgba(232, 150, 16, 0.5);
}
.vol-fin__temps,
.echappee-fin__mesure {
  margin: 0 0 0.35rem;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.vol-fin__gain,
.echappee-fin__gain {
  margin: 0 0 0.9rem;
  font-size: 1rem;
  font-weight: 700;
  color: var(--accent);
}
.vol-fin__gain[hidden], .echappee-fin__gain[hidden] { display: none; }
.vol-fin__actions,
.echappee-fin__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
}

/* ── La carte de fin du Vol d'Orage ──────────────────────────────────────────
   Tout ce qui suit est au vol seul, et posé après les règles partagées avec
   l'échappée plutôt qu'à l'intérieur : les deux jeux avaient le même panneau
   plat, celui-ci prend une carte et une mascotte, et l'autre n'a pas à changer
   parce que le premier a changé.

   Le panneau était un aplat sombre sur toute la scène — le seul des quatre à
   n'avoir rien à quoi s'accrocher, là où la pause a son encart vert et le
   départ son geste animé. Il prend donc la carte du site, aux valeurs des
   autres fenêtres, et la victoire l'allume en ambre : la couleur du portail
   qu'on vient de franchir, et celle que le rubis a partout ailleurs. */
.vol-fin__carte {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  width: min(30rem, 100%);
  max-height: 100%;
  overflow-y: auto;
  padding: 1.25rem 1.5rem 1.4rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--bg-card) 0%, var(--bg-card-alt) 100%);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
  animation: vol-fin-entree 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.vol-fin--victoire .vol-fin__carte {
  border-color: rgba(232, 150, 16, 0.42);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55), 0 0 46px rgba(232, 150, 16, 0.22);
}
@keyframes vol-fin-entree {
  from { opacity: 0; transform: translateY(14px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

/* Le mot qu'on attend au bout des trois manches, et qui n'existait pas : « Le
   portail s'ouvre » raconte la scène, il ne dit pas qu'on a gagné. Il entre
   après la carte — l'ordre se lit comme une annonce. */
.vol-fin__bravo {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 6vw, 2.15rem);
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  text-shadow: 0 0 34px rgba(232, 150, 16, 0.55);
  animation: vol-fin-bravo 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.14s both;
}
.vol-fin__bravo[hidden] { display: none; }
.vol-fin__eclat {
  font-size: 0.7em;
  animation: vol-fin-eclat 2.4s ease-in-out 0.7s infinite;
}
@keyframes vol-fin-bravo {
  from { opacity: 0; transform: scale(0.72); }
  to   { opacity: 1; transform: none; }
}
@keyframes vol-fin-eclat {
  0%, 100% { opacity: 0.55; transform: rotate(0) scale(1); }
  50%      { opacity: 1;    transform: rotate(90deg) scale(1.25); }
}

/* Sous « Victoire », le titre n'est plus l'annonce mais la scène : il redescend
   d'un cran pour que les deux ne se disputent pas le regard. Sur une défaite il
   reste ce qu'il était — c'est là le seul titre de la carte. */
.vol-fin--victoire .vol-fin__titre {
  font-size: clamp(1rem, 3.4vw, 1.2rem);
  font-weight: 700;
  opacity: 0.85;
  text-shadow: none;
}

/* ── La mascotte et sa bulle ─────────────────────────────────────────────────
   Le panda du site commente la partie. C'est le portrait du bandeau, du tableau
   de bord et du casse-briques : il est déjà celui qui s'adresse au joueur
   (`_toast_panda.html.twig`), il n'apprend rien de nouveau ici.

   Portrait et bulle côte à côte, repliés l'un sous l'autre quand la scène est
   étroite — la pointe de la bulle pivote alors vers le haut, pour continuer de
   viser le panda. */
.vol-fin__mot {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 0.75rem;
  text-align: left;
}
.vol-fin__panda {
  flex: none;
  width: clamp(3.75rem, 16vw, 5.25rem);
  height: auto;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.5));
  animation:
    vol-fin-panda-entree 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.24s both,
    vol-fin-panda-souffle 3.6s ease-in-out 0.9s infinite;
}
@keyframes vol-fin-panda-entree {
  from { opacity: 0; transform: translateY(16px) scale(0.8); }
  to   { opacity: 1; transform: none; }
}
/* Il respire, il ne gesticule pas : un degré et demi, quatre pixels. C'est ce
   qui sépare une image collée d'une bête qui attend qu'on rejoue. */
@keyframes vol-fin-panda-souffle {
  0%, 100% { transform: translateY(0) rotate(-1.4deg); }
  50%      { transform: translateY(-4px) rotate(1.4deg); }
}

.vol-fin__bulle {
  position: relative;
  margin: 0;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, 0.04);
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--text);
  animation: vol-fin-bulle 0.4s ease-out 0.38s both;
}
/* La pointe, en deux triangles superposés : le grand fait la bordure, le petit
   le fond. Un seul les aurait laissés sans liseré. Sa couleur est celle de la
   carte sous le voile clair de la bulle — un triangle ne prend pas de fond
   translucide sans laisser voir la bordure au travers. */
.vol-fin__bulle::before,
.vol-fin__bulle::after {
  content: '';
  position: absolute;
  top: 1.15rem;
  right: 100%;
  border: 0.45rem solid transparent;
}
.vol-fin__bulle::before { border-right-color: var(--border-card); }
.vol-fin__bulle::after  { border-right-color: #1d1710; margin-right: -1px; }
@keyframes vol-fin-bulle {
  from { opacity: 0; transform: translateX(-8px); }
  to   { opacity: 1; transform: none; }
}

/* ── Le gain ─────────────────────────────────────────────────────────────────
   Une ligne d'ambre parmi d'autres lignes : il fallait la chercher, et une
   joueuse a fini un parcours sans savoir si elle avait touché ses rubis. C'est
   devenu une pastille, aux couleurs du rubis, et la seule chose colorée de la
   carte en dehors de l'annonce. */
.vol-fin__gain {
  margin: 0 0 1rem;
  padding: 0.5rem 1.1rem;
  border: 1px solid rgba(232, 150, 16, 0.38);
  border-radius: 999px;
  background: rgba(232, 150, 16, 0.12);
  font-size: 1.15rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  animation: vol-fin-gain 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* Pendant que le serveur répond. Discrète, et déjà à la place de ce qui la
   remplacera : la carte ne doit pas sauter au moment où le montant arrive. */
.vol-fin__gain--attente {
  border-color: var(--border-card);
  background: none;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  animation: none;
}
@keyframes vol-fin-gain {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: none; }
}

/* Sur une scène étroite, le portrait passe au-dessus de la bulle, et la pointe
   avec lui. */
@media (max-width: 26rem) {
  .vol-fin__carte { padding: 1rem 1rem 1.15rem; }
  .vol-fin__mot { flex-direction: column; gap: 0.5rem; text-align: center; }
  .vol-fin__bulle::before,
  .vol-fin__bulle::after {
    top: auto;
    right: auto;
    bottom: 100%;
    left: 50%;
    margin: 0 0 0 -0.45rem;
    border-right-color: transparent;
  }
  .vol-fin__bulle::before { border-bottom-color: var(--border-card); }
  .vol-fin__bulle::after  { border-bottom-color: #1d1710; margin-bottom: -1px; }
}

.vol-sans-js,
.echappee-sans-js { gap: 1rem; font-size: 0.9rem; color: var(--text-muted); }
.vol-sans-js strong,
.echappee-sans-js strong { color: var(--text); }


@media (prefers-reduced-motion: reduce) {
  /* Le décor animé, les particules et le flottement du dragon sont déjà coupés
     par `vol.js`, qui lit la même préférence. Il ne reste ici que le fond
     translucide des panneaux : le flou coûte cher à rendre, et sur une machine
     qui peine il produit exactement le tremblement qu'on cherche à éviter. */
  .vol-depart, .vol-pause, .vol-fin, .vol-sans-js,
  .echappee-depart, .echappee-suspendu, .echappee-fin, .echappee-sans-js { backdrop-filter: none; background: rgba(8, 5, 18, 0.94); }

  /* La carte de fin arrive sans entrer, et le panda ne respire pas. Rien n'est
     retiré pour autant : l'annonce, le portrait et la bulle sont tous là, à
     leur place définitive dès la première image. Ce qu'on coupe est le
     mouvement, jamais ce qu'il amenait — la carte dit la même chose. */
  .vol-fin__carte,
  .vol-fin__bravo,
  .vol-fin__eclat,
  .vol-fin__panda,
  .vol-fin__bulle,
  .vol-fin__gain { animation: none; }

  /* Le geste s'arrête mais ne disparaît pas : il est figé au moment de
     l'appui, doigt posé et onde ouverte, le dragon en haut de sa montée. On
     perd le mouvement, pas ce qu'il montrait — c'est une consigne, et un
     joueur qui demande moins d'animation ne demande pas moins d'explications. */
  .vol-geste__doigt,
  .vol-geste__onde,
  .vol-geste__dragon { animation: none; }
  .vol-geste__doigt  { transform: translateY(7px); }
  .vol-geste__onde   { transform: scale(1.25); opacity: 0.5; }
  .vol-geste__dragon { transform: translateY(calc(-1 * var(--vol-geste-envol))); }
}

/* ── L'Échappée de Lumina : ce qui lui est propre ─────────────────────────────
   Tout le reste — la scène, ses panneaux, son compteur — vient des règles du
   vol, auxquelles cette page s'ajoute plus haut dans la feuille. Deux jeux qui
   se jouent plein écran sur un canvas, avec un écran de départ, un écran de fin
   et un bouton de sortie, n'ont aucune raison d'avoir deux mises en page.

   Ne reste ici que ce qui les sépare vraiment. */

/* La scène ne se redresse pas en portrait, contrairement à celle du vol.

   Le vol se joue sur l'altitude et a besoin de hauteur — d'où son 3/4. Une
   course se joue sur ce qui arrive de la droite : la rétrécir reviendrait à
   retirer au joueur le temps de voir venir, puisque la vitesse s'exprime en
   écrans par seconde. Elle s'élargit donc au maximum de ce que le format
   permet, et 4/3 est le compromis qui tient encore dans un téléphone tenu
   droit sans écraser la scène en bandeau. */
/* ── La scène de l'échappée se centre dans la hauteur disponible ────────────
   Le vol garde sa scène en haut, et il a ses raisons : elle prend déjà toute la
   place, il n'y a rien à répartir, et la réserve `--sortie-bas` s'occupe du
   dessous. L'échappée, elle, est bornée par sa **largeur** — 16/9 sur un
   ordinateur, et la hauteur reste — si bien que la scène se tenait collée sous
   le bandeau avec un vide sous elle. Le centrage rend ce vide des deux côtés,
   et rend du même coup la réserve inutile : c'est lui qui écarte la sortie du
   bord. */
.game-main:has(.echappee-scene) { justify-content: center; }

/* La sortie ne flotte plus au milieu de ce vide : elle suit la scène.
   `.jeu-actions` porte `margin: auto 0`, qui absorbait toute la place libre et
   aurait tenu le bouton loin sous le jeu malgré le centrage. */
.game-main:has(.echappee-scene) .jeu-actions { margin: 0; padding-top: 1.25rem; }

/* ── Le format en portrait ───────────────────────────────────────────────────
   3/4, comme le vol, et non un format large qui laisserait la scène minuscule
   au milieu de l'écran d'un téléphone. Elle était en 4/3 : sur un écran de
   390 px, cela donnait 366 × 274, soit un tiers de la hauteur du téléphone
   occupé par le jeu, et deux tiers de vide.

   Ce qu'on retire en largeur ne retire rien au joueur : vitesses, espacements
   et **largeurs d'obstacle** s'expriment en fractions d'écran (voir
   `echappee/regles.js`), si bien qu'une scène étroite se joue exactement comme
   une large. C'est ce qui rend ce choix possible — il ne l'était pas tant que
   les obstacles mesuraient un nombre fixe d'unités.

   Le plafond de hauteur, lui, reste : sur un téléphone court, c'est la hauteur
   disponible qui commande et le format cède, plutôt que la page se mette à
   défiler pendant la partie. */
@media (orientation: portrait) {
  .echappee-scene { aspect-ratio: 1 / 1; }
}

/* ── La créature qu'un jeu exige ─────────────────────────────────────────────
   Sur la carte de boutique d'un mini-jeu, et sur sa carte du hub.

   Un joueur qui achète l'Échappée de Lumina sans posséder Lumina paie trois
   cents rubis pour une page qui le renverra d'où il vient. La pastille est là
   pour que cela ne puisse pas arriver sans qu'il l'ait lu.

   ── Doré ou gris, et jamais la couleur seule ───────────────────────────────

   L'or dit « vous l'avez », le gris « il vous la faut » — c'est la couleur que
   ce document donne partout à ce qu'on possède. Mais un état signalé par la
   seule couleur n'est lisible que par une partie des joueurs : la phrase change
   avec elle, et c'est elle qui porte l'information. La couleur ne fait que la
   rendre repérable d'un coup d'œil. (CONVENTIONS.md § 6.)

   Le portrait manquant est traité en niveaux de gris **et** assombri : une
   désaturation seule laissait une vignette claire et nette, qui se lisait comme
   une image ordinaire plutôt que comme une absence. */
.creature-requise {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.75rem 0 0;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border-card);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.02);
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--text-muted);
}

.creature-requise__portrait {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  /* La créature entière, et non un cadrage sur sa tête : ces planches sont
     détourées et tiennent dans un cercle sans qu'on ait à les recadrer. Au
     remplissage, on ne voyait qu'un fragment, et Lumina n'y était pas
     reconnaissable — or c'est précisément ce que la pastille doit permettre. */
  object-fit: contain;
  border: 2px solid var(--border-card);
  background: rgba(255, 255, 255, 0.04);
}

.creature-requise__texte { min-width: 0; }

/* Le libellé d'état, au-dessus de la phrase : deux lignes, l'une qui nomme la
   condition, l'autre qui dit quoi en faire. */
.creature-requise__etat {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.creature-requise--acquise {
  border-color: var(--accent-border);
  background: rgba(232, 150, 16, 0.07);
}
.creature-requise--acquise .creature-requise__portrait {
  border-color: var(--accent);
  box-shadow: 0 0 14px rgba(232, 150, 16, 0.3);
}
.creature-requise--acquise .creature-requise__etat { color: var(--accent); }

.creature-requise--manquante .creature-requise__portrait {
  filter: grayscale(1) brightness(0.55);
  opacity: 0.85;
}
.creature-requise--manquante .creature-requise__etat { color: var(--text-dim); }

/* ── Le repère du geste, sur l'écran de départ ───────────────────────────────
   Une piste, une ronce, et Lumina qui la franchit. C'est le troisième de la
   famille — `.vol-geste` montre un battement d'ailes, `.bb-depart__piste` un
   glissé — et il en partage la couleur, la période et le principe : une
   pastille, pas une silhouette. Ce qu'il faut comprendre est « ça passe
   par-dessus quand je tape », et un dessin détaillé à cette taille ne dirait
   que « il y a une créature ».

   Ce qu'il montre de plus que les deux autres, c'est l'obstacle. Le geste du
   vol se suffit d'une montée ; ici, une pastille qui monte et redescend sans
   rien au-dessus de quoi passer aurait montré un saut, pas un jeu.

   Une seule période pour les trois animations — la course, le bond et l'appui.
   Trois durées indépendantes auraient dérivé l'une par rapport à l'autre en
   quelques boucles, et le doigt aurait fini par taper pendant que la pastille
   retombe : le repère aurait alors montré le contraire de ce qu'il explique. */
.echappee-geste {
  --echappee-geste-periode: 1.8s;

  /* Ce que la pastille parcourt, et de combien elle s'élève. La piste est plus
     large que la course : la pastille doit avoir de la place devant elle en fin
     de boucle, sinon elle semble buter sur le bord. */
  --echappee-geste-course: 4.2rem;
  --echappee-geste-bond: 1.45rem;

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
  color: var(--violet-commande-clair);
}

.echappee-geste__piste {
  position: relative;
  width: 5.6rem;
  /* La hauteur réserve la course du bond au-dessus de la pastille : sans elle,
     celle-ci sortirait par le haut et passerait sur ce qui la surmonte. */
  height: calc(0.95rem + var(--echappee-geste-bond) + 0.2rem);
}

/* Le sol : un trait qui s'efface aux deux bouts, pour que la piste n'ait pas
   de fin franche — on court, on ne traverse pas une pièce. */
.echappee-geste__sol {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  border-radius: 1px;
  background: linear-gradient(90deg, transparent, var(--violet-commande), transparent);
  opacity: 0.6;
}

/* La ronce : la même silhouette que dans le jeu — un dos arrondi surmonté de
   pointes — réduite à ce qu'on en distingue à cette taille. Plus pâle que la
   pastille : c'est elle qu'on suit, pas l'obstacle. */
.echappee-geste__ronce {
  position: absolute;
  bottom: 2px;
  left: 3rem;
  width: 0.85rem;
  height: 0.72rem;
  margin-left: -0.425rem;
  background: var(--violet-commande);
  opacity: 0.55;
  clip-path: polygon(0 100%, 18% 22%, 34% 78%, 50% 0, 66% 78%, 82% 22%, 100% 100%);
}

.echappee-geste__coureur {
  position: absolute;
  bottom: 2px;
  left: 0;
  animation: echappee-geste-court var(--echappee-geste-periode) linear infinite;
}

/* Deux transformations imbriquées, et c'est la seule façon d'obtenir un arc en
   CSS : le parent avance, l'enfant monte. Écrites sur un même élément, la
   seconde écraserait la première. */
.echappee-geste__bond {
  display: block;
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: var(--violet-commande);
  box-shadow: 0 0 16px var(--violet-commande-lueur);
  animation: echappee-geste-bond var(--echappee-geste-periode) ease-in-out infinite;
}

.echappee-geste__appui {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
}
.echappee-geste__doigt {
  display: block;
  position: relative;
  z-index: 1;
  animation: echappee-geste-tape var(--echappee-geste-periode) ease-in-out infinite;
}
/* L'onde part du bout de l'index, pas du centre du dessin : c'est là que le
   doigt touche, et une onde centrée sur la paume se lirait comme un halo. Le
   bout de l'index est à 8/24 et 2/24 du `viewBox`, soit 33 % et 8 % des 34 px
   du dessin — mêmes valeurs que sur le vol, c'est la même main. */
.echappee-geste__onde {
  position: absolute;
  top: 0.18rem;
  left: 33%;
  width: 2.5rem;
  height: 2.5rem;
  margin: -1.25rem 0 0 -1.25rem;
  border: 2px solid var(--violet-commande);
  border-radius: 50%;
  opacity: 0;
  animation: echappee-geste-onde var(--echappee-geste-periode) ease-out infinite;
}

/* Les quatre temps de la boucle, et leur accord :
     32 %  le doigt touche, l'onde part ;
     40 %  la pastille quitte le sol — l'appui la précède, comme dans le jeu ;
     60 %  elle est au sommet, exactement au-dessus de la ronce (3 rem sur la
           piste, soit ce que la course lui fait parcourir en 60 % du temps) ;
     84 %  elle a retouché le sol, franchement derrière l'obstacle. C'était 80,
           et elle atterrissait en le frôlant : le repère montrait alors un saut
           tout juste réussi, là où il doit montrer un saut réussi. */
@keyframes echappee-geste-court {
  0%   { transform: translateX(0); }
  100% { transform: translateX(var(--echappee-geste-course)); }
}
@keyframes echappee-geste-bond {
  0%, 40%   { transform: translateY(0); }
  60%       { transform: translateY(calc(-1 * var(--echappee-geste-bond))); }
  84%, 100% { transform: translateY(0); }
}
@keyframes echappee-geste-tape {
  0%, 20%   { transform: translateY(0); }
  32%       { transform: translateY(7px); }
  48%, 100% { transform: translateY(0); }
}
@keyframes echappee-geste-onde {
  0%, 28%   { transform: scale(0.35); opacity: 0; }
  36%       { opacity: 0.8; }
  72%, 100% { transform: scale(1.9); opacity: 0; }
}

/* ── La bulle de jeu : le palier franchi ─────────────────────────────────────
   Elle passe, elle n'arrête rien.

   Le vol met la partie en pause entre deux manches, et il a raison de le faire :
   ses manches sont des étapes, et la suivante recommence ailleurs. Ici la course
   est continue, et l'arrêter toutes les quinze secondes casserait le souffle qui
   fait le jeu. La bulle se pose donc au-dessus de la scène, dit ce qui est
   acquis, et s'efface d'elle-même.

   ── Pourquoi le portrait est en haut, et centré ─────────────────────────────

   Elle se pose sur un terrain de jeu, souvent tenu en portrait — et en ligne,
   portrait à gauche et texte à côté, elle s'étirait d'un bord à l'autre de la
   scène, barrant précisément la bande où le joueur regarde arriver les
   obstacles.

   En colonne, elle est deux fois moins large, elle tient au-dessus de la ligne
   d'horizon, et le portrait se lit avant le texte plutôt qu'à côté. C'est aussi
   la forme des annonces de jeu en général : un visage, puis ce qu'il dit — et,
   depuis, celle des bulles du site (`.mascot-toast`), qui butaient sur le même
   écueil dès qu'un téléphone les recevait.

   `pointer-events: none` : elle recouvre la scène deux secondes durant, et un
   tap qui tomberait dessus doit faire sauter Lumina, pas rien du tout. */
.jeu-toast {
  position: absolute;
  top: 12%;
  left: 50%;
  z-index: 4;
  transform: translateX(-50%);

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;

  /* Étroite, et c'est le point : sans plafond, elle s'allongeait autant que son
     texte, jusqu'à traverser la scène. Elle s'enroule sur deux lignes plutôt que
     de barrer le terrain. */
  width: max-content;
  max-width: min(72%, 15rem);

  padding: 1.95rem 0.9rem 0.75rem;
  border: 1px solid rgba(34, 197, 94, 0.4);
  border-radius: var(--r-lg);
  background: rgba(10, 26, 16, 0.92);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(34, 197, 94, 0.12);

  opacity: 0;
  pointer-events: none;
}
.jeu-toast[hidden] { display: none; }

/* Le portrait, à cheval sur le bord du haut — la moitié dedans, la moitié
   dehors. Le rembourrage du haut de la bulle lui fait sa place ; le fond du
   médaillon est opaque, parce qu'il doit couper la bordure qu'il chevauche. */
.jeu-toast__avatar {
  position: absolute;
  top: -1.45rem;
  left: 50%;
  transform: translateX(-50%);
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid rgba(34, 197, 94, 0.55);
  background: #0a1a10;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55);
}
.jeu-toast__avatar img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform: scale(1.12);
}

.jeu-toast__texte {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: 0.02em;
  text-align: center;
  text-wrap: balance;
  color: #86efac;
}

.jeu-toast--visible { animation: jeu-toast 2.2s ease-out forwards; }

/* Le centrage est reconduit dans chaque image-clé : une transform d'animation
   remplace celle de la règle au lieu de s'y ajouter, et la bulle partirait du
   bord. Même piège que `mascot-toast-in`. */
@keyframes jeu-toast {
  0%        { opacity: 0; transform: translateX(-50%) translateY(-0.5rem) scale(0.96); }
  12%, 68%  { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  100%      { opacity: 0; transform: translateX(-50%) translateY(-0.6rem) scale(1); }
}

/* Sur une scène basse — un téléphone en paysage — la bulle et son portrait
   prendraient le quart de la hauteur. Tout rétrécit plutôt que de recouvrir le
   terrain. */
@media (orientation: landscape) and (max-height: 540px) {
  .jeu-toast { top: 8%; padding: 1.3rem 0.7rem 0.55rem; max-width: min(60%, 13rem); }
  .jeu-toast__avatar { width: 40px; height: 40px; top: -1.1rem; }
  .jeu-toast__texte { font-size: 0.74rem; }
}

/* ── Le score de l'écran de fin ──────────────────────────────────────────────
   La distance est ce que le joueur vient chercher : elle est écrite plus grand
   que le reste du panneau, et dans la couleur d'accent du site, comme un gain.
   Le record et les paliers se lisent en dessous, à la taille des mentions. */
.echappee-fin__distance {
  margin: 0 0 0.2rem;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 7vw, 2.4rem);
  font-weight: 900;
  line-height: 1;
  color: var(--accent);
  text-shadow: 0 0 30px rgba(232, 150, 16, 0.4);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  /* La bulle reste lisible, elle cesse seulement de glisser : c'est une
     information, pas une décoration, et la retirer priverait le joueur du seul
     endroit où on lui dit qu'un palier est acquis. */
  .jeu-toast--visible { animation: jeu-toast-fixe 2.2s steps(1, end) forwards; }

  /* Le repère s'arrête mais ne disparaît pas : il est figé à l'instant qui
     explique tout — doigt posé, onde ouverte, Lumina au sommet de son bond,
     juste au-dessus de la ronce. On perd le mouvement, pas ce qu'il montrait.
     Un joueur qui demande moins d'animation ne demande pas moins
     d'explications. */
  .echappee-geste__doigt,
  .echappee-geste__onde,
  .echappee-geste__coureur,
  .echappee-geste__bond { animation: none; }
  .echappee-geste__doigt   { transform: translateY(7px); }
  .echappee-geste__onde    { transform: scale(1.25); opacity: 0.5; }
  .echappee-geste__coureur { transform: translateX(calc(var(--echappee-geste-course) * 0.6)); }
  .echappee-geste__bond    { transform: translateY(calc(-1 * var(--echappee-geste-bond))); }

  @keyframes jeu-toast-fixe {
    0%, 80% { opacity: 1; transform: translateX(-50%); }
    100%    { opacity: 0; transform: translateX(-50%); }
  }
}


/* === 23. RESPONSIVE === */
@media (max-width: 960px) {
  .hero__content { grid-template-columns: 1fr; text-align: center; padding-bottom: 4rem; }
  .hero__mascot  { order: 1; }
  .hero__mascot img { height: 32vh; width: auto; max-width: none; }
  .hero__ctas    { justify-content: center; }
  .footer-grid   { grid-template-columns: 1fr 1fr; }
  .footer-brand  { grid-column: 1 / -1; }
  .admin-sidebar {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    height: 100vh;
    z-index: 600;
    transform: translateX(-100%);
    transition: transform .35s cubic-bezier(.4,0,.2,1);
  }
  .admin-sidebar.is-open { transform: translateX(0); }
  .admin-sidebar-overlay { display: block; }
  .admin-sidebar-trigger { display: flex; }
  .admin-sidebar__mobile-close { display: flex; }
  .admin-content { padding: 1.5rem; }

  /* Le tiroir du menu burger : opaque, et réellement au-dessus du reste.

     Deux règles plus haut le contredisaient, et gagnaient par spécificité —
     `.admin-page > .admin-sidebar` (0,2,0) contre `.admin-sidebar` (0,1,0) :
     la première impose `position: sticky` et `z-index: 1`. Le tiroir n'était
     donc ni `fixed` ni au premier plan. Il restait dans le flux, poussait le
     contenu, et surtout passait SOUS le voile (`z-index: 599`), dont le
     `backdrop-filter: blur(2px)` le floutait avec le reste de la page — texte
     du menu compris.

     Le sélecteur est donc aligné sur celui qu'il doit battre, et placé après
     lui : à spécificité égale, c'est l'ordre qui tranche. Le verre est retiré
     au passage — sur grand écran la barre latérale n'a derrière elle que le
     dégradé sombre de `.admin-page::before` et 4 % de blanc s'y lisent bien,
     mais posée par-dessus les tableaux elle les laissait transparaître. */
  .admin-page > .admin-sidebar {
    position: fixed;
    z-index: 600;
    background: var(--bg-card-alt);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
/* Tablette et en dessous : la nav passe sur plusieurs lignes. */
@media (max-width: 1023px) { :root { --nav-h: 92px; } }

@media (max-width: 768px) {
  /* Page hero — empilement vertical */
  .page-hero { padding: 1.25rem 0 1rem; margin-bottom: 0.75rem; }
  .page-hero__inner { flex-direction: column; text-align: center; gap: 0.75rem; }
  .page-hero__mascot { height: 90px; }
}

/* ── Desktop (≥ 1024px) : 1 ligne horizontale ── */
@media (min-width: 1024px) {
  :root { --nav-h: 70px; }
  .nav-inner {
    flex-direction: row;
    align-items: center;
    height: var(--nav-h);
    padding: 0 1.5rem;
    gap: 1.25rem;
  }
  .nav-top-row { padding: 0; min-height: auto; flex-shrink: 0; gap: 1.5rem; }
  .nav-top-row--full { flex: 1; }
  .nav-user-row { padding: 0; border-top: none; margin-left: auto; }
  .nav-shortcut-btn { width: 2.2rem; height: 2.2rem; }
}
/* ── Mobile & tablette (≤ 1023px) : 2 lignes ──
   Ligne 1 = logo | raccourcis + paramètres.
   Ligne 2 = tableau de bord, inventaire et rubis regroupés à gauche. */
@media (max-width: 1023px) {
  .nav-user-row { justify-content: flex-start; gap: 0.7rem; }
  .nav-username { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; }
}

@media (max-width: 640px) {
  .creatures-grid { grid-template-columns: repeat(2, 1fr); }
  .features-grid  { grid-template-columns: repeat(2, 1fr); }
  .shop-grid      { grid-template-columns: repeat(2, 1fr); }

  /* Le footer passe en une colonne : plus rien ne justifie l'alignement à
     gauche, qui laissait les titres de rubriques flotter au-dessus d'un vide.
     On recentre l'ensemble — les conteneurs flex (logo, réseaux) ont besoin
     d'align-items, le reste suit le text-align hérité. */
  .footer-grid    { grid-template-columns: 1fr; text-align: center; }
  .footer-brand__logo,
  .footer-socials { justify-content: center; }
  .footer-col ul  { align-items: center; }
  .cta-banner     { flex-direction: column; text-align: center; }
  .cta-banner__mascot { max-width: 130px; }
  .home-widgets   { grid-template-columns: 1fr; }
}
@media (max-width: 400px) {
  .creatures-grid { grid-template-columns: 1fr; }
  .shop-grid      { grid-template-columns: 1fr; }
  .page-hero__mascot { height: 70px; }
  .page-hero { padding: 1rem 0 0.85rem; }
  .container { padding: 0 1rem; }
}
@media (max-width: 360px) {
  /* On garde la marque visible mais en plus petit plutôt que de la masquer */
  .nav-logo { gap: 0.4rem; }
  .nav-logo img { width: 30px; height: 30px; }
  .nav-logo__name { font-size: 0.78rem; letter-spacing: 0.03em; }
  .nav-logo__sub  { font-size: 0.46rem; letter-spacing: 0.12em; }
  .nav-top-row { padding: 0.5rem 0.5rem; }
  .nav-user-row { padding: 0.25rem 0.5rem 0.4rem; column-gap: 0.35rem; }
  .nav-username { max-width: 4.5rem; }
  /* La rangée porte un raccourci de plus depuis que les jeux ont le leur : on
     resserre les écarts avant d'avoir à sacrifier la marque. */
  .nav-shortcuts { gap: 0.3rem; }
}
@media (max-width: 300px) {
  /* Écrans très étroits : on masque enfin le texte pour laisser la place aux actions */
  .nav-logo__brand { display: none; }
}

/* ============================================================ */
/* === BULLES DE MASCOTTE — boutique et forge               === */
/* ============================================================ */
/* Une dépense se joue en deux temps, et la bulle change de rôle entre les
   deux. Celle de confirmation attend une réponse et ne part pas toute seule ;
   celle d'annonce constate et s'efface. Les deux se posent au centre de
   l'écran : c'est là qu'on regarde quand on vient de cliquer. Même coque,
   même or, même portrait rond — c'est la bulle du marchand, étendue à la
   forge.

   Le cadrage, lui, ne peut pas être commun : le marchand est un panda détouré
   qu'il faut serrer sur le visage, le forgeron une scène carrée où l'enclume
   compte. Chaque page choisit le sien par `data-toast-cadrage` sur sa
   mascotte. */
.mascot-toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 9999;
  animation: mascot-toast-in .25s ease;
  /* En colonne, et non en ligne : voir l'encadré du portrait juste dessous. */
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: .3rem;
  width: max-content;
  max-width: min(92vw, 360px);
  background: #1a1a2e;
  border: 1px solid rgba(245,200,66,.35);
  border-radius: 16px;
  padding: .85rem 1.1rem;
  /* Le second filet, en `inset`, est ce qui fait la boîte de dialogue : les
     cadres de jeu sont doublés, et un seul trait rendait la bulle plate. Il ne
     coûte aucun élément de plus. */
  box-shadow: 0 12px 32px rgba(0,0,0,.5), inset 0 0 0 1px rgba(245,200,66,.12);
}

/* ── Le portrait à cheval sur le cadre, en haut et au milieu ─────────────────
   C'est la façon dont une boîte de dialogue de jeu — ou une bulle de manga —
   présente celui qui parle : le portrait n'est pas *dans* la boîte, il est
   posé **sur** son bord, moitié dedans moitié dehors. La bulle devient alors ce
   qu'elle est, la parole de quelqu'un, et non un bandeau avec une vignette.

   Il était à gauche, la parole à côté de lui, et la bulle s'étirait d'autant :
   une phrase de deux lignes donnait un bandeau large et plat, et sur un
   téléphone le médaillon — calé à `left: -1.1rem` d'une bulle qui touche déjà
   les bords — sortait de l'écran. En haut et centré, la bulle n'a plus que la
   largeur de son texte, rien ne dépasse latéralement, et le portrait se lit
   avant la phrase plutôt qu'à côté. C'est déjà la forme de `.jeu-toast`.

   Le portrait est sorti du flux, et c'est le rembourrage du haut qui lui fait
   sa place. `:has()` pour que les bulles sans portrait — la boutique en monte
   quand la page n'a pas de bandeau — n'aient pas ce vide à combler.

   Le fond du médaillon est opaque et de la couleur de la bulle : il doit
   *couper* la bordure qu'il chevauche, sinon le trait lui passe au travers.

   La largeur minimale est celle du médaillon, avec de quoi respirer : la bulle
   se règle sur son texte (`max-content`), et un message de trois mots la
   rendrait plus étroite que le portrait posé dessus. Les bulles du panda la
   relèvent, voir `--panda`. */
.mascot-toast:has(.mascot-toast__avatar) {
  padding-top: 2.15rem;
  min-width: var(--toast-min, 7rem);
}
.mascot-toast__avatar {
  position: absolute;
  top: -1.5rem;
  left: 50%;
  transform: translateX(-50%);
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid rgba(245,200,66,.5);
  background: #1a1a2e;
  box-shadow: 0 6px 18px rgba(0,0,0,.55);
}
.mascot-toast__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}
/* Le panda du site est déjà une tête détourée, et rien d'autre : la cadrer au
   remplissage lui coupait les oreilles en haut et le menton en bas. `contain`
   la montre entière, et le léger agrandissement lui fait toucher les bords du
   médaillon — sans quoi elle flotterait au milieu d'un cercle trop grand.

   Le sélecteur ne vise que les bulles du serveur (`--panda`) : celles que monte
   la boutique portent des planches en pied, qui ont leur propre cadrage juste
   en dessous. */
.mascot-toast--panda .mascot-toast__avatar img {
  object-fit: contain;
  object-position: center center;
  transform: scale(1.12);
}

/* Serré sur le visage : la planche en pied d'Astrax marchand, dont la bulle ne
   garde que la tête. Le facteur cadre les oreilles sans les couper — au-delà de
   1.5, le haut du crâne sort du cercle. */
.mascot-toast--cadrage-visage .mascot-toast__avatar img {
  transform: scale(1.45);
  transform-origin: center 15%;
}
/* Serré sur le personnage : la scène carrée de la forge, dont les coins ne
   portent que le fond. */
.mascot-toast--cadrage-scene .mascot-toast__avatar img {
  object-position: center center;
  transform: scale(1.3);
  transform-origin: center 40%;
}
.mascot-toast__bubble { min-width: 0; width: 100%; }
.mascot-toast__name {
  display: block;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .12rem;
}
/* `balance` pour un texte centré : sans lui, une phrase de deux lignes laisse
   souvent un seul mot sur la seconde, et le déséquilibre se voit d'autant plus
   que la bulle est symétrique. */
.mascot-toast__text {
  margin: 0;
  font-size: .85rem;
  color: var(--text);
  line-height: 1.4;
  text-wrap: balance;
}

/* Une transform d'animation remplace celle de la règle au lieu de s'y
   ajouter : le centrage doit être reconduit dans les images-clés, sinon la
   bulle part du coin pendant le quart de seconde d'apparition. */
@keyframes mascot-toast-in {
  from { opacity: 0; transform: translate(-50%, -50%) scale(.96); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* --- Annonce : elle s'efface au bout de quelques secondes --- */
.mascot-toast--danger { border-color: rgba(255,90,90,.45); }
.mascot-toast--danger .mascot-toast__avatar { border-color: rgba(255,90,90,.5); }
.mascot-toast--danger .mascot-toast__name { color: #ff8a8a; }
.mascot-toast--warn { border-color: rgba(224,138,22,.5); }
.mascot-toast--warn .mascot-toast__name { color: #f0b35a; }

/* --- Confirmation : elle attend une réponse --- */
.mascot-toast--confirm {
  padding: 1rem 1.1rem .9rem;
  box-shadow: 0 12px 32px rgba(0,0,0,.55);
}
.mascot-toast--confirm .mascot-toast__avatar { width: 66px; height: 66px; top: -1.8rem; }
.mascot-toast--confirm:has(.mascot-toast__avatar) { padding-top: 2.5rem; }
.mascot-toast__cost {
  display: block;
  margin-top: .25rem;
  font-size: .78rem;
  color: var(--accent);
}
/* Centrées sous la question, et non calées à gauche : la bulle est symétrique,
   et deux boutons en biais y font une béquille. */
.mascot-toast__actions {
  display: flex;
  justify-content: center;
  gap: .5rem;
  margin-top: .65rem;
}
.mascot-toast__btn {
  font-family: inherit;
  font-size: .78rem;
  font-weight: 700;
  border-radius: 10px;
  padding: .35rem .85rem;
  cursor: pointer;
  border: 1px solid transparent;
  transition: filter .15s, border-color .15s, color .15s;
}
.mascot-toast__btn:hover { filter: brightness(1.12); }
.mascot-toast__btn--confirm { background: linear-gradient(135deg, #e0a81a, #f5c84a); color: #221a05; }
.mascot-toast__btn--cancel {
  background: transparent;
  color: var(--text-muted);
  border-color: rgba(255,255,255,.18);
}
.mascot-toast__btn--cancel:hover { border-color: rgba(255,255,255,.35); color: var(--text); }

/* Sur un téléphone, le portrait descend d'autant que la bulle est resserrée :
   il garde la moitié de sa hauteur au-dessus du cadre, et le rembourrage du
   haut suit.

   Après les règles de la confirmation, et non avant : elles visent le même
   médaillon avec la même spécificité, et depuis un `@media` elles ne gagnent
   rien de plus. Placé au-dessus, le bloc était muet pour la bulle de
   confirmation — son portrait gardait ses 66 px sur un écran de 360 px. */
@media (max-width: 480px) {
  .mascot-toast { max-width: 86vw; }
  .mascot-toast__avatar { width: 46px; height: 46px; top: -1.25rem; }
  .mascot-toast:has(.mascot-toast__avatar) { padding-top: 1.8rem; }
  .mascot-toast--confirm .mascot-toast__avatar { width: 52px; height: 52px; top: -1.4rem; }
  .mascot-toast--confirm:has(.mascot-toast__avatar) { padding-top: 2.05rem; }
}

/* Sous 560 px, la bulle tenait entre deux marges, d'un bord à l'autre de
   l'écran : c'était la seule façon de faire rentrer un portrait calé à gauche
   d'un texte. Le portrait est passé en haut, la bulle n'a plus que la largeur
   de sa phrase et se contente de `max-width` — elle reste centrée comme sur un
   grand écran, et les cas particuliers d'animation qui allaient avec le
   calage par les bords sont tombés avec lui. */
@media (prefers-reduced-motion: reduce) {
  .mascot-toast { animation: none; }
}

/* --- Le panda roux : inscription, connexion, déconnexion ---
   Rendue par le serveur. Celle qui guide ne s'efface pas d'elle-même : elle
   annonce un changement d'état, pas le résultat d'un clic, et le joueur vient
   peut-être de changer de page. La croix la referme. L'adieu de la déconnexion
   fait exception, voir `--ephemere` plus bas.

   La croix est posée dans le coin, au-dessus du texte centré, et non poussée
   par un rembourrage à droite : celui-ci décentrerait la phrase de sa largeur.
   Reste à lui laisser le coin : une phrase courte donnerait une bulle si
   étroite que le médaillon centré viendrait buter dedans, d'où la largeur
   minimale relevée. */
.mascot-toast--panda {
  --toast-min: min(15rem, 84vw);
}

/* L'adieu de la déconnexion : pas de croix, il s'en va seul.

   Haut de page, à hauteur du bandeau, et non au centre comme les bulles qui
   posent une question : rien n'est demandé au joueur, la bulle n'a donc pas à
   se mettre devant ce qu'il regarde. Elle salue depuis le haut et s'efface.

   Le retrait est une animation CSS et non un minuteur JavaScript — sans croix à
   cliquer, une bulle resterait posée là chez un joueur sans JavaScript.
   `visibility` la retire aussi du chemin du pointeur, ce que l'opacité seule ne
   fait pas. Le décalage vaut mieux qu'une durée longue : quatre secondes
   pleines de lisibilité, puis un départ en moins d'une demie.

   L'écart au bandeau compte le portrait : il dépasse d'une moitié de médaillon
   au-dessus du cadre, et sans cette marge de plus il viendrait se poser dans la
   barre de navigation.

   Rien à refermer, donc pas de croix à loger dans le coin : la bulle retombe
   sur la largeur minimale commune, celle du seul médaillon, et se contente de
   sa phrase. */
.mascot-toast--ephemere {
  top: calc(var(--nav-h) + 2.1rem);
  transform: translateX(-50%);
  --toast-min: 7rem;
  animation: mascot-toast-haut .25s ease, mascot-toast-sortie .45s ease 4s forwards;
}
/* Sa propre entrée : celle des bulles centrées reconduit `translate(-50%, -50%)`
   dans ses images-clés, et recentrerait celle-ci verticalement le temps de
   l'animation. */
@keyframes mascot-toast-haut {
  from { opacity: 0; transform: translateX(-50%) translateY(-8px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes mascot-toast-sortie {
  to { opacity: 0; visibility: hidden; }
}
/* Le départ reste, même quand les animations sont refusées : c'est lui qui
   libère l'écran, et il ne bouge rien — l'opacité seule. Seule l'apparition
   tombe. Il faut la redire ici : la règle générale plus haut, `animation: none`,
   a la même spécificité et perdrait contre celle du dessus. */
@media (prefers-reduced-motion: reduce) {
  .mascot-toast--ephemere { animation: mascot-toast-sortie .45s ease 4s forwards; }
}
.mascot-toast__fermer {
  position: absolute;
  top: .35rem;
  right: .45rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  font-size: .85rem;
  line-height: 1;
  color: var(--text-muted);
  transition: color .15s, background .15s;
}
.mascot-toast__fermer:hover { color: var(--text); background: rgba(255,255,255,.08); }
.mascot-toast__fermer:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  color: var(--text);
}

/* === RUBY GAIN ANIMATION === */
.ruby-gain-badge {
  position: fixed;
  z-index: 9999;
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--accent);
  pointer-events: none;
  white-space: nowrap;
  text-shadow: 0 2px 10px rgba(0,0,0,0.7);
  animation: ruby-float 1.8s ease-out forwards;
}
@keyframes ruby-float {
  0%   { opacity: 0;   transform: translateY(0)    scale(0.8); }
  15%  { opacity: 1;   transform: translateY(-8px)  scale(1.2); }
  80%  { opacity: 1;   transform: translateY(-50px) scale(1); }
  100% { opacity: 0;   transform: translateY(-70px) scale(0.9); }
}

/* Le débit reprend le badge du gain, en rouge : même trajectoire, sens
   inverse pour la lecture. */
.ruby-spend-badge { color: #ff7a7a; }


/* ============================================================ */
/* === DUEL ASTRAL                                           === */
/* ============================================================ */

/* --- Sélection (home_duel) --- */
/* Les deux camps restent en vis-à-vis à TOUTE largeur : le sien à gauche,
   l'adversaire à droite. Les colonnes se repliaient l'une sous l'autre en
   dessous de 768 px, et le duel devenait deux listes empilées — on choisissait
   son champion, puis on faisait défiler pour découvrir contre qui. Le face-à-
   face est ce que la page raconte ; il ne se replie pas.

   `minmax(0, 1fr)` et non `1fr` : la taille minimale par défaut d'une piste de
   grille est son contenu, et un nom de créature un peu long aurait élargi sa
   colonne jusqu'à pousser l'autre hors de l'écran. C'est ce `0` qui autorise
   les deux camps à rétrécir également. */
.duel-setup {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}

.duel-setup__title {
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 1rem;
  text-align: center;
}

.duel-setup__vs {
  font-size: 2rem;
  font-weight: 900;
  color: var(--accent);
  align-self: center;
  text-align: center;
  padding-top: 2rem;
}

.duel-cards {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

.duel-card {
  background: var(--bg-card);
  border: 2px solid transparent;
  border-radius: 12px;
  padding: .7rem;
  cursor: pointer;
  display: flex;
  gap: .7rem;
  align-items: center;
  transition: border-color .2s, transform .15s, box-shadow .2s;
}
.duel-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.duel-card.is-selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(232,150,16,.25);
  background: var(--bg-card-alt, #1e1e2e);
}

/* Le portrait est ce qui se réduit : il décore, il n'informe pas. Les chiffres,
   l'élément et la compétence décident du combat, et ils restent entiers. */
.duel-card__img {
  width: 52px;
  height: 52px;
  border-radius: 8px;
  object-fit: cover;
  flex-shrink: 0;
}
.duel-card__img--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.05);
  font-size: 1.5rem;
  color: var(--text-muted, #888);
}
.duel-card__img--boss { font-size: 1.7rem; }

.duel-card__body { flex: 1; min-width: 0; }
/* Deux colonnes laissent peu de place à un nom : il se coupe plutôt que de
   déborder de sa carte. */
.duel-card__name {
  font-weight: 700;
  font-size: .9rem;
  margin-bottom: .25rem;
  overflow-wrap: anywhere;
}
.duel-card__stats {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .6rem;
  font-size: .75rem;
  margin: .3rem 0;
  opacity: .85;
}
.duel-card__skill { font-size: .75rem; opacity: .7; overflow-wrap: anywhere; }
/* Les runes du champion. Elles tenaient dans un `<style>` posé au milieu de
   `home_duel.html.twig`, seules de tout le duel à vivre hors de cette feuille :
   la carte se réduit ici, et une moitié de son dessin serait restée ailleurs. */
.duel-card__rune {
  margin-top: .35rem;
  font-size: .72rem;
  font-weight: 600;
  color: #c87cff;
  letter-spacing: .02em;
  overflow-wrap: anywhere;
}

/* ── Les deux camps sur un écran étroit ──────────────────────────────────────
   Le vis-à-vis tient, donc chaque camp n'a qu'une demi-largeur : la carte passe
   en colonne — portrait au-dessus, informations dessous — pour que les chiffres
   disposent de toute la case au lieu des soixante pixels qui restaient à côté
   d'une vignette. Le « VS » et les écarts maigrissent d'autant : ils ne portent
   rien que les deux colonnes ne disent déjà. */
@media (max-width: 768px) {
  .duel-setup { gap: .55rem; }
  .duel-setup__title { font-size: .82rem; margin-bottom: .6rem; }
  .duel-setup__vs { font-size: 1.15rem; padding-top: 1.6rem; }
  .duel-cards { gap: .45rem; }
  .duel-card {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
    gap: .4rem;
    padding: .55rem .45rem;
    border-width: 1px;
  }
  .duel-card__img { width: 44px; height: 44px; margin: 0 auto; }
  .duel-card__stats { justify-content: center; gap: .15rem .4rem; font-size: .7rem; margin: .2rem 0; }
  .duel-card__skill { font-size: .7rem; }
  .duel-card__name { font-size: .78rem; }
  .duel-card__element { font-size: .62rem; padding: .1rem .35rem; }
  .duel-card__rune { font-size: .68rem; margin-top: .2rem; }
}

/* Le soulèvement au survol n'était pas gardé : il l'est maintenant, comme le
   reste des cartes du site. La bordure et l'ombre disent la sélection aussi
   bien, et elles ne bougent pas. */
@media (prefers-reduced-motion: reduce) {
  .duel-card:hover { transform: none; }
}

/* Badges élément */
.duel-card__element {
  display: inline-block;
  font-size: .7rem;
  font-weight: 700;
  padding: .15rem .5rem;
  border-radius: 99px;
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-bottom: .25rem;
}
.duel-element--feu    { background: rgba(255,80,30,.2);  color: #ff6040; }
.duel-element--eau    { background: rgba(30,140,255,.2); color: #5ab0ff; }
.duel-element--terre  { background: rgba(100,200,60,.2); color: #8dd65a; }
.duel-element--astral { background: rgba(180,80,255,.2); color: #c47fff; }
.duel-element--ombre  { background: rgba(100,80,180,.2); color: #9080c8; }
.duel-element--lumiere{ background: rgba(255,240,120,.2);color: #ffe87a; }
.duel-element--foudre { background: rgba(200,255,60,.2); color: #c8ff3c; }
.duel-element--glace  { background: rgba(120,220,255,.2);color: #78dcff; }

/* --- Arène de combat --- */
/* L'arène garde ses deux camps en vis-à-vis à toute largeur, comme la
   sélection : sur un écran étroit c'est le « VS » du milieu qui s'efface, pas
   l'un des deux combattants. `minmax(0, 1fr)` pour la même raison que
   `.duel-setup` — une piste de grille ne rétrécit pas sous son contenu sans ce
   `0`, et un nom de boss un peu long pousserait le champion hors de l'écran. */
.combat-arena {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 2rem;
  align-items: center;
  margin-bottom: 2rem;
}
@media (max-width: 640px) {
  .combat-arena { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .75rem; }
  .combat-vs { display: none; }
}

.combat-vs {
  font-size: 2rem;
  font-weight: 900;
  color: var(--accent);
  text-align: center;
}

/* --- Topbar duel --- */
.duel-topbar {
  position: relative;
  z-index: 1;
  padding: .75rem 0;
  background: rgba(8,6,20,.5);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.duel-topbar__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
}
.duel-topbar__status {
  font-size: .95rem;
  font-weight: 600;
  opacity: .9;
  margin: 0;
}

/* --- Scène de combat --- */
.duel-stage {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1.5rem;
  padding: 2rem 2rem 1rem;
  position: relative;
  z-index: 1;
}
@media (max-width: 600px) {
  .duel-stage {
    grid-template-columns: 1fr 1fr;
    gap: .75rem;
    padding: 1rem .75rem .5rem;
  }
  .duel-stage__vs { display: none; }
}

.duel-stage__vs {
  font-size: 1.6rem;
  font-weight: 900;
  color: var(--accent);
  text-align: center;
  text-shadow: 0 0 20px rgba(232,150,16,.6);
}

/* --- Fighter (un combattant) --- */
.fighter {
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.fighter--boss { align-items: flex-end; text-align: right; }

.fighter__header {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}
.fighter__header--boss { justify-content: flex-end; }

.fighter__name {
  font-weight: 700;
  font-size: .95rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.fighter__hp-row {
  display: flex;
  align-items: center;
  gap: .5rem;
}
.fighter__hp-row--boss { flex-direction: row-reverse; }

.fighter__hp-bar {
  flex: 1;
  height: 10px;
  background: rgba(255,255,255,.12);
  border-radius: 99px;
  overflow: hidden;
}
.fighter__hp-fill {
  height: 100%;
  background: #4caf50;
  border-radius: 99px;
  transition: width .4s ease;
}
.fighter__hp-fill--boss { background: #c47fff; }

.fighter__hp-text {
  font-size: .75rem;
  opacity: .75;
  white-space: nowrap;
  flex-shrink: 0;
}

/* --- Image du combattant --- */
.fighter__visual {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  max-width: 240px;
}
.fighter--boss .fighter__visual { margin-left: auto; }

.fighter__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 16px;
  display: block;
}
.fighter__img--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.05);
  font-size: 5rem;
  border-radius: 16px;
  width: 100%;
  height: 100%;
}
.fighter__img--boss-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(196,127,255,.08);
  font-size: 5rem;
  border-radius: 16px;
  width: 100%;
  height: 100%;
}

/* Overlay rouge sur les dégâts */
.fighter__hit-overlay {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  background: #ff2020;
  opacity: 0;
  pointer-events: none;
}

/* Animation dégâts : shake + flash rouge */
@keyframes fighter-shake {
  0%  { transform: translateX(0); }
  20% { transform: translateX(-10px); }
  40% { transform: translateX(10px); }
  60% { transform: translateX(-6px); }
  80% { transform: translateX(6px); }
  100%{ transform: translateX(0); }
}
@keyframes hit-flash {
  0%       { opacity: 0; }
  20%      { opacity: .65; }
  100%     { opacity: 0; }
}

.fighter__img.is-hit,
.fighter__img--placeholder.is-hit,
.fighter__img--boss-placeholder.is-hit {
  animation: fighter-shake 0.4s ease-out;
}
.fighter__hit-overlay.is-hit {
  animation: hit-flash 0.4s ease-out forwards;
}

.fighter__skill {
  font-size: .75rem;
  opacity: .55;
  margin: 0;
}
.fighter__skill--right { text-align: right; }

/* --- Actions --- */
.combat-actions {
  display: flex;
  gap: 1rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

.btn-accent {
  background: linear-gradient(135deg, #7c3aed, #c47fff);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: .6rem 1.4rem;
  font-weight: 700;
  cursor: pointer;
  transition: opacity .2s, transform .15s;
}
.btn-accent:hover:not(:disabled) { opacity: .85; transform: translateY(-1px); }
.btn-accent:disabled { opacity: .4; cursor: not-allowed; }

/* --- Log de combat --- */
.combat-log {
  background: rgba(0,0,0,.3);
  border-radius: 12px;
  padding: 1rem;
  max-height: 220px;
  overflow-y: auto;
  font-size: .85rem;
  margin-bottom: 2rem;
  border: 1px solid rgba(255,255,255,.08);
}
.combat-log__entry { margin-bottom: .3rem; padding: .2rem 0; }
.combat-log__entry--system  { color: var(--text-muted, #888); font-style: italic; }
.combat-log__entry--damage  { color: #ff8080; }
.combat-log__entry--heal    { color: #80ff90; }
.combat-log__entry--shield  { color: #80d0ff; }
.combat-log__entry--poison  { color: #c47fff; }
.combat-log__entry--skill   { color: var(--accent); font-weight: 700; }

/* --- Résultat final --- */
.combat-result {
  text-align: center;
  padding: 2rem;
  background: var(--bg-card);
  border-radius: 16px;
  margin-top: 1rem;
}
.combat-result__title { font-size: 2rem; font-weight: 900; margin-bottom: .5rem; }
.combat-result__title--win  { color: var(--accent); }
.combat-result__title--lose { color: #ff6060; }


/* ============================================================ */
/* === ADMIN — DUEL ASTRAL FORM SECTIONS                    === */
/* ============================================================ */

.admin-form-section {
  margin-bottom: 2rem;
  padding-bottom: 2rem;
  border-bottom: 1px solid rgba(255,255,255,.07);
}
.admin-form-section:last-of-type { border-bottom: none; }

.admin-form-section__title {
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: .75rem;
  display: flex;
  align-items: center;
  gap: .6rem;
}

.admin-form-section__badge {
  font-size: .7rem;
  font-weight: 600;
  padding: .15rem .55rem;
  border-radius: 99px;
  background: rgba(255,255,255,.08);
  color: var(--text-muted, #888);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.admin-form-section__badge--active {
  background: rgba(232,150,16,.2);
  color: var(--accent);
}

.admin-form-section--combat { background: rgba(232,150,16,.04); border-radius: 12px; padding: 1.25rem; border: 1px solid rgba(232,150,16,.12); }

.admin-form-section__hint {
  font-size: .82rem;
  color: var(--text-muted, #888);
  margin-bottom: 1.25rem;
  line-height: 1.5;
}

.admin-form-section__subtitle {
  font-size: .85rem;
  font-weight: 700;
  color: var(--accent);
  margin: 1.25rem 0 .75rem;
  padding-top: 1rem;
  border-top: 1px dashed rgba(232,150,16,.2);
}

.admin-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 1rem;
}
.admin-stats-grid--skill {
  grid-template-columns: 1fr 1.5fr 1fr;
}
@media (max-width: 640px) {
  .admin-stats-grid,
  .admin-stats-grid--skill { grid-template-columns: 1fr; }
}

/* ── Groupes de cases à cocher ──
   Un choix étendu rendu par `form_widget` ne pose aucune enveloppe par case :
   le conteneur aligne des `<input>` et des `<label>` collés, sans une seule
   espace entre eux. Or une ligne ne se coupe qu'aux espaces — la liste des
   éléments ne se repliait donc pas, elle débordait sur la droite, et aucune
   règle CSS ne pouvait y créer un point de coupure. Les gabarits enveloppent
   maintenant chaque paire, et c'est cette enveloppe qui se replie.

   `.form-checkboxes--columns` disparaît au passage : `columns: 3` posé sur le
   conteneur fragmentait par ligne et non par case, et le `break-inside: avoid`
   qui l'accompagnait annulait la fragmentation. La règle ne rendait déjà
   qu'une colonne. */
.form-checkboxes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
}
.form-checkbox {
  display: inline-flex;
  align-items: center;
}
.form-checkbox label {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-size: 0.875rem;
  color: var(--text);
  cursor: pointer;
}

/* Un groupe de cases doit être annoncé par `fieldset`/`legend` : l'étiquette
   qui le coiffait n'était rattachée à rien: sans `for`, et sans rien à viser,
   puisqu'une case à cocher unique ne représente pas le groupe. Un lecteur
   d'écran énonçait « Feu, case à cocher » sans jamais dire de quoi.

   La bordure du `fieldset` est retirée et la légende reprend au trait près le
   style de `.form-group > label` : la sémantique change, l'apparence non. */
.form-fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 1.25rem;
  min-width: 0;
}
.form-fieldset > legend {
  padding: 0;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.4rem;
}

.form-hint {
  font-size: .78rem;
  color: var(--text-muted, #888);
  margin-top: .3rem;
  line-height: 1.4;
}


/* ============================================================ */
/* === CREATURE CARD — stats & tags                         === */
/* ============================================================ */

.creature-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin-bottom: .4rem;
}

.creature-card__meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-top: .25rem;
}

.creature-tag {
  display: inline-block;
  font-size: .68rem;
  font-weight: 700;
  padding: .1rem .45rem;
  border-radius: 99px;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.creature-tag--classe {
  background: rgba(255,255,255,.08);
  color: var(--accent);
}

.creature-card__stats {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .65rem;
  font-size: .75rem;
  opacity: .8;
  margin: .4rem 0 .2rem;
}

.creature-card__skill {
  font-size: .75rem;
  opacity: .6;
  margin-top: .15rem;
}


/* ============================================================ */
/* === DUEL ASTRAL — background & card flip                 === */
/* ============================================================ */

.duel-combat-wrapper {
  position: relative;
  min-height: 100vh;
  padding-top: var(--nav-h);
}

.duel-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: url('/images/duel-astral.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Overlay sombre pour garder le texte lisible */
.duel-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(8, 6, 20, 0.72) 0%,
    rgba(8, 6, 20, 0.60) 50%,
    rgba(8, 6, 20, 0.80) 100%
  );
}

/* Le hero sur la page combat est plus sobre (pas de gradient propre) */
.page-hero--duel {
  background: rgba(8, 6, 20, 0.45);
  backdrop-filter: blur(4px);
  border-bottom: 1px solid rgba(255,255,255,.08);
}

/* Le log et les sections */
.duel-combat-wrapper .combat-log {
  background: rgba(0,0,0,.55);
  backdrop-filter: blur(6px);
}
.duel-combat-wrapper .combat-result {
  background: rgba(12, 10, 28, 0.85);
  backdrop-filter: blur(10px);
}



/* ============================================================ */
/* === DUEL ASTRAL — animations soin & poison               === */
/* ============================================================ */

/* Soin : rebond vers le haut + flash vert */
@keyframes fighter-bounce {
  0%   { transform: translateY(0); }
  35%  { transform: translateY(-12px); }
  65%  { transform: translateY(-6px); }
  100% { transform: translateY(0); }
}
@keyframes heal-flash {
  0%       { opacity: 0; }
  25%      { opacity: .55; }
  100%     { opacity: 0; }
}

.fighter__img.is-heal,
.fighter__img--placeholder.is-heal,
.fighter__img--boss-placeholder.is-heal {
  animation: fighter-bounce 0.55s ease-out;
}
.fighter__hit-overlay.is-heal {
  background: #22c55e;
  animation: heal-flash 0.55s ease-out forwards;
}

/* Poison : wobble lent + flash violet */
@keyframes fighter-wobble {
  0%   { transform: rotate(0deg); }
  20%  { transform: rotate(-4deg); }
  40%  { transform: rotate(4deg); }
  60%  { transform: rotate(-3deg); }
  80%  { transform: rotate(3deg); }
  100% { transform: rotate(0deg); }
}
@keyframes poison-pulse {
  0%       { opacity: 0; }
  20%      { opacity: .5; }
  50%      { opacity: .2; }
  75%      { opacity: .45; }
  100%     { opacity: 0; }
}

.fighter__img.is-poison,
.fighter__img--placeholder.is-poison,
.fighter__img--boss-placeholder.is-poison {
  animation: fighter-wobble 0.7s ease-in-out;
}
.fighter__hit-overlay.is-poison {
  background: #a855f7;
  animation: poison-pulse 0.7s ease-out forwards;
}

/* ============================================================ */
/* === DUEL ASTRAL — statut poison & bulles                 === */
/* ============================================================ */

/* Badge statut poison (sous la barre de vie) */
.fighter__poison-status {
  min-height: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.25rem 0 0.1rem;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  transition: opacity 0.3s;
}
.fighter__poison-status--boss {
  justify-content: flex-end;
}
.fighter__poison-status.is-active {
  background: rgba(168, 85, 247, 0.15);
  border: 1px solid rgba(168, 85, 247, 0.5);
  border-radius: 999px;
  padding: 0.15rem 0.7rem;
  color: #d8b4fe;
  animation: poison-badge-pulse 1.8s ease-in-out infinite;
}
@keyframes poison-badge-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(168,85,247,0); }
  50%       { opacity: 0.65; box-shadow: 0 0 6px 2px rgba(168,85,247,0.35); }
}

/* Conteneur de bulles (positionné en absolu dans fighter__visual) */
.fighter__bubbles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
}

/* Bulle individuelle (créée dynamiquement en JS) */
.fighter__bubble {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, rgba(220,180,255,0.9), rgba(168,85,247,0.55));
  box-shadow: 0 0 4px rgba(168,85,247,0.6);
  animation: bubble-rise 1.1s ease-out forwards;
}
@keyframes bubble-rise {
  0%   { transform: translateY(0) scale(1);   opacity: 0.85; }
  60%  { transform: translateY(-70px) scale(0.7); opacity: 0.6; }
  100% { transform: translateY(-120px) scale(0.3); opacity: 0; }
}

/* ============================================================ */
/* === DUEL ASTRAL — statut bouclier & particules soin      === */
/* ============================================================ */

/* Badge statut bouclier */
.fighter__shield-status {
  min-height: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.15rem 0 0.1rem;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  transition: opacity 0.3s;
}
.fighter__shield-status--boss { justify-content: flex-end; }
.fighter__shield-status.is-active {
  background: rgba(59, 130, 246, 0.15);
  border: 1px solid rgba(59, 130, 246, 0.5);
  border-radius: 999px;
  padding: 0.15rem 0.7rem;
  color: #93c5fd;
  animation: shield-badge-pulse 2s ease-in-out infinite;
}
@keyframes shield-badge-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(59,130,246,0); }
  50%       { opacity: 0.7; box-shadow: 0 0 8px 2px rgba(59,130,246,0.4); }
}

/* Aura bleue persistante (dans fighter__visual, position absolute) */
.fighter__shield-aura {
  position: absolute;
  inset: 0;
  border-radius: 8px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s;
}
.fighter__shield-aura.is-active {
  opacity: 1;
  animation: shield-aura-glow 1.6s ease-in-out infinite alternate;
}
@keyframes shield-aura-glow {
  from { box-shadow: inset 0 0 14px rgba(59,130,246,0.25), 0 0 18px rgba(59,130,246,0.2); }
  to   { box-shadow: inset 0 0 32px rgba(59,130,246,0.55), 0 0 42px rgba(59,130,246,0.45); }
}

/* Flash bleu à l'activation du bouclier */
@keyframes shield-activate-flash {
  0%   { opacity: 0; }
  30%  { opacity: 0.65; }
  100% { opacity: 0; }
}
@keyframes shield-appear {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.07); }
  100% { transform: scale(1); }
}
.fighter__img.is-shield,
.fighter__img--placeholder.is-shield,
.fighter__img--boss-placeholder.is-shield {
  animation: shield-appear 0.45s ease-out;
}
.fighter__hit-overlay.is-shield {
  background: #3b82f6;
  animation: shield-activate-flash 0.45s ease-out forwards;
}

/* Conteneur particules soin */
.fighter__heal-particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
/* Particule "+" individuelle (créée dynamiquement en JS) */
.fighter__heal-particle {
  position: absolute;
  font-size: 0.9rem;
  font-weight: 700;
  color: #4ade80;
  text-shadow: 0 0 5px rgba(74,222,128,0.9);
  animation: heal-particle-rise 0.9s ease-out forwards;
  user-select: none;
  line-height: 1;
}
@keyframes heal-particle-rise {
  0%   { transform: translateY(0) scale(1);     opacity: 1; }
  65%  { transform: translateY(-55px) scale(1.15); opacity: 0.85; }
  100% { transform: translateY(-95px) scale(0.8);  opacity: 0; }
}

/* ============================================================ */
/* === DUEL ASTRAL — statut gel (rune de Glace)             === */
/* ============================================================ */

.fighter__freeze-status {
  min-height: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.15rem 0 0.1rem;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  transition: opacity 0.3s;
}
.fighter__freeze-status--boss { justify-content: flex-end; }
.fighter__freeze-status.is-active {
  background: rgba(56, 189, 248, 0.15);
  border: 1px solid rgba(56, 189, 248, 0.5);
  border-radius: 999px;
  padding: 0.15rem 0.7rem;
  color: #bae6fd;
  animation: freeze-badge-pulse 2s ease-in-out infinite;
}
@keyframes freeze-badge-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(56,189,248,0); }
  50%       { opacity: 0.7; box-shadow: 0 0 8px 2px rgba(56,189,248,0.4); }
}

@keyframes freeze-shiver {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-2px); }
  75%      { transform: translateX(2px); }
}
.fighter__img.is-frozen,
.fighter__img--placeholder.is-frozen,
.fighter__img--boss-placeholder.is-frozen {
  animation: freeze-shiver 0.5s ease-in-out;
  filter: brightness(1.15) saturate(0.6) hue-rotate(160deg);
}
.fighter__hit-overlay.is-frozen {
  background: #38bdf8;
  animation: poison-pulse 0.7s ease-out forwards;
}

/* ================================================================
   CINÉMATIQUE — SCÈNE 1 : LE RÉVEIL D'ASTRA
   ================================================================ */

.cin-body {
  margin: 0; padding: 0;
  background: #000;
  overflow: hidden;
  width: 100vw; height: 100vh;
}

/* ── Conteneur principal ── */
.cin {
  position: relative;
  width: 100vw; height: 100vh;
  overflow: hidden;
  background: #000;
  font-family: 'Cinzel', serif;
  color: #fff;
  user-select: none;
}

/* ── Background ── */
.cin__bg {
  position: absolute; inset: 0; z-index: 1;
  opacity: 0;
  transition: opacity 3s ease;
}
.cin[data-scene="2"] .cin__bg,
.cin[data-scene="3"] .cin__bg,
.cin[data-scene="4"] .cin__bg,
.cin[data-scene="5"] .cin__bg,
.cin[data-scene="6"] .cin__bg,
.cin[data-scene="7"] .cin__bg { opacity: 1; }

.cin__bg-img {
  width: 100%; height: 100%; object-fit: cover;
  animation: cin-slow-zoom 50s ease-in-out infinite alternate;
  filter: brightness(0.72) saturate(1.15);
}
@keyframes cin-slow-zoom {
  from { transform: scale(1); }
  to   { transform: scale(1.14); }
}

.cin__bg-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.25) 0%, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.55) 100%);
}

/* ── Écran noir intro ── */
.cin__black {
  position: absolute; inset: 0; z-index: 10;
  background: #000;
  opacity: 1;
  transition: opacity 2.5s ease;
  pointer-events: none;
}
.cin[data-scene="2"] .cin__black,
.cin[data-scene="3"] .cin__black,
.cin[data-scene="4"] .cin__black,
.cin[data-scene="5"] .cin__black,
.cin[data-scene="6"] .cin__black,
.cin[data-scene="7"] .cin__black { opacity: 0; }

/* ── Particules cosmiques ── */
.cin__particles {
  position: absolute; inset: 0; z-index: 2;
  pointer-events: none;
  opacity: 0;
  transition: opacity 3s ease;
}
.cin[data-scene="2"] .cin__particles,
.cin[data-scene="3"] .cin__particles,
.cin[data-scene="4"] .cin__particles,
.cin[data-scene="5"] .cin__particles,
.cin[data-scene="6"] .cin__particles,
.cin[data-scene="7"] .cin__particles { opacity: 1; }

.cin__particle {
  position: absolute; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,220,100,0.95), rgba(180,100,255,0.45));
  box-shadow: 0 0 4px rgba(255,200,100,0.5);
  animation: cin-particle-float linear infinite;
  opacity: 0;
}
@keyframes cin-particle-float {
  0%   { transform: translateY(0) scale(1);   opacity: 0; }
  12%  { opacity: 0.85; }
  88%  { opacity: 0.55; }
  100% { transform: translateY(-80px) scale(0.4); opacity: 0; }
}

/* ── Textes narratifs ── */
.cin__narration {
  position: absolute; z-index: 20;
  left: 0; right: 0;
  display: flex; flex-direction: column;
  align-items: center; gap: 1.4rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.8s ease;
}
.cin__narration--center {
  top: 50%; transform: translateY(-50%);
}
.cin__narration--bottom {
  bottom: 10%;
  padding: 0 10%;
  text-align: center;
}
.cin__narration--final-text {
  bottom: 10%; left: 0; right: 0;
  text-align: center;
}

.cin__text {
  font-family: 'Cinzel', serif;
  font-size: clamp(1rem, 2.8vw, 1.45rem);
  color: rgba(255, 248, 220, 0.92);
  text-shadow: 0 0 24px rgba(200, 160, 255, 0.65), 0 2px 10px rgba(0,0,0,0.9);
  letter-spacing: 0.06em;
  margin: 0;
  opacity: 0;
  transition: opacity 1.6s ease;
}
.cin__text--active { opacity: 1; }
.cin__text--large {
  font-size: clamp(1.3rem, 4vw, 2rem);
  font-weight: 900;
  letter-spacing: 0.12em;
}

/* Narration intro — visible scènes 0 et 1 */
#cin-narration-intro {
  opacity: 0;
  transition: opacity 1.6s ease;
}
.cin[data-scene="0"] #cin-narration-intro,
.cin[data-scene="1"] #cin-narration-intro { opacity: 1; }

#cin-text-1 { opacity: 0; transition: opacity 1.5s ease; }
#cin-text-2 { opacity: 0; transition: opacity 1.5s ease; }
.cin[data-scene="0"] #cin-text-1 { opacity: 1; }
.cin[data-scene="1"] #cin-text-2 { opacity: 1; }

/* Narration plan 3 (Crystal) — scènes 4 */
.cin[data-scene="4"] #cin-narration-3 { opacity: 1; }

/* Narration finale — scène 6 */
.cin[data-scene="6"] #cin-narration-final { opacity: 1; }

/* ── Astra ── */
.cin__astra {
  position: absolute; z-index: 5;
  bottom: 0; left: 8%;
  width: clamp(180px, 32vw, 460px);
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 3s ease, transform 3s ease;
}
.cin[data-scene="3"] .cin__astra,
.cin[data-scene="4"] .cin__astra,
.cin[data-scene="5"] .cin__astra,
.cin[data-scene="6"] .cin__astra,
.cin[data-scene="7"] .cin__astra {
  opacity: 1; transform: translateY(0);
}

.cin__astra-img {
  width: 100%; display: block;
  animation: cin-float-astra 5.5s ease-in-out infinite;
  filter: drop-shadow(0 0 28px rgba(255,210,100,0.5))
          drop-shadow(0 0 60px rgba(180,100,255,0.3));
}
@keyframes cin-float-astra {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-14px); }
}

.cin__astra-glow {
  position: absolute; bottom: -8%; left: 50%;
  transform: translateX(-50%);
  width: 85%; height: 38%;
  background: radial-gradient(ellipse, rgba(255,200,80,0.28) 0%, transparent 70%);
  animation: cin-glow-pulse 3.5s ease-in-out infinite alternate;
  pointer-events: none;
}
@keyframes cin-glow-pulse {
  from { opacity: 0.5; transform: translateX(-50%) scale(1); }
  to   { opacity: 1;   transform: translateX(-50%) scale(1.12); }
}

/* ── Crystal ── */
.cin__crystal {
  position: absolute; z-index: 4;
  bottom: 3%; left: 50%;
  transform: translateX(-50%) scale(0.65);
  width: clamp(70px, 11vw, 160px);
  opacity: 0;
  transition: opacity 2.5s ease, transform 2.5s ease;
}
.cin[data-scene="4"] .cin__crystal,
.cin[data-scene="5"] .cin__crystal,
.cin[data-scene="6"] .cin__crystal,
.cin[data-scene="7"] .cin__crystal {
  opacity: 1; transform: translateX(-50%) scale(1);
}

.cin__crystal-img {
  width: 100%; display: block;
  animation: cin-crystal-pulse 2.2s ease-in-out infinite;
}
@keyframes cin-crystal-pulse {
  0%, 100% {
    filter: drop-shadow(0 0 18px rgba(255,120,50,0.7))
            drop-shadow(0 0 45px rgba(255,160,80,0.45))
            brightness(1);
  }
  50% {
    filter: drop-shadow(0 0 38px rgba(255,150,60,1))
            drop-shadow(0 0 80px rgba(255,190,100,0.8))
            brightness(1.35);
  }
}

/* ── Lumina ── */
.cin__lumina {
  position: absolute; z-index: 6;
  bottom: 38%; right: 10%;
  width: clamp(75px, 13vw, 175px);
  opacity: 0;
  transform: translateY(22px) scale(0.88);
  transition: opacity 2.5s ease, transform 2.5s ease;
}
.cin[data-scene="5"] .cin__lumina,
.cin[data-scene="6"] .cin__lumina,
.cin[data-scene="7"] .cin__lumina {
  opacity: 1; transform: translateY(0) scale(1);
}

.cin__lumina-img {
  width: 100%; display: block;
  animation: cin-lumina-float 4.2s ease-in-out infinite;
  filter: drop-shadow(0 0 14px rgba(255,230,100,0.85))
          drop-shadow(0 0 38px rgba(255,200,60,0.5));
}
@keyframes cin-lumina-float {
  0%, 100% { transform: translateY(0) rotate(-4deg) scale(1); }
  50%       { transform: translateY(-11px) rotate(4deg) scale(1.03); }
}

.cin__lumina-particles {
  position: absolute; inset: -60%;
  pointer-events: none;
}
.cin__lumina-dust {
  position: absolute; width: 4px; height: 4px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,235,110,1), rgba(255,200,60,0.5));
  box-shadow: 0 0 5px rgba(255,220,80,0.8);
  animation: cin-dust-float linear infinite;
  opacity: 0;
}
@keyframes cin-dust-float {
  0%   { opacity: 0; transform: translateY(0) scale(1); }
  18%  { opacity: 0.9; }
  82%  { opacity: 0.45; }
  100% { opacity: 0; transform: translateY(-45px) scale(0.3); }
}

/* ── Écran final ── */
.cin__final {
  position: absolute; inset: 0; z-index: 30;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 1.2rem;
  opacity: 0;
  transition: opacity 2.2s ease;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(0,0,0,0.15), rgba(0,0,0,0.45));
}
.cin[data-scene="7"] .cin__final {
  opacity: 1; pointer-events: auto;
}

.cin__final-logo {
  width: clamp(55px, 9vw, 110px);
  filter: drop-shadow(0 0 22px rgba(255,200,100,0.65));
  animation: cin-float-astra 4s ease-in-out infinite;
}

.cin__final-title {
  font-family: 'Cinzel', serif;
  font-size: clamp(1.8rem, 5vw, 3.6rem);
  font-weight: 900;
  letter-spacing: 0.18em;
  color: #fff; margin: 0;
  animation: cin-title-glow 2.8s ease-in-out infinite alternate;
}
@keyframes cin-title-glow {
  from { text-shadow: 0 0 22px rgba(200,150,255,0.5), 0 0 45px rgba(255,200,100,0.3); }
  to   { text-shadow: 0 0 44px rgba(200,150,255,0.95), 0 0 90px rgba(255,200,100,0.65); }
}

.cin__final-sub {
  font-family: 'Lato', sans-serif;
  font-size: clamp(0.7rem, 1.8vw, 0.95rem);
  letter-spacing: 0.35em;
  color: rgba(255,220,180,0.65);
  text-transform: uppercase;
  margin: 0;
}

.cin__final-btn {
  margin-top: 1.2rem;
  padding: 0.9rem 2.8rem;
  font-family: 'Cinzel', serif;
  font-size: clamp(0.82rem, 1.8vw, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: #fff;
  background: linear-gradient(135deg, rgba(110,50,190,0.8), rgba(50,90,200,0.8));
  border: 1px solid rgba(200,150,255,0.45);
  border-radius: 4px;
  text-decoration: none;
  transition: box-shadow 0.35s ease, transform 0.35s ease, border-color 0.35s ease;
  position: relative;
}
.cin__final-btn:hover {
  transform: translateY(-3px);
  border-color: rgba(200,150,255,0.8);
  box-shadow: 0 0 30px rgba(180,120,255,0.55), 0 0 65px rgba(100,150,255,0.3);
}

/* ── Boutons Skip / Revoir ── */
.cin__skip,
.cin__revoir {
  position: absolute; bottom: 1.8rem; right: 1.8rem; z-index: 50;
  background: rgba(0,0,0,0.38);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 4px;
  color: rgba(255,255,255,0.5);
  font-size: 0.78rem;
  font-family: 'Lato', sans-serif;
  padding: 0.45rem 1rem;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
  letter-spacing: 0.04em;
}
.cin__skip:hover,
.cin__revoir:hover {
  color: rgba(255,255,255,0.9);
  border-color: rgba(255,255,255,0.45);
  background: rgba(0,0,0,0.55);
}
.cin__revoir { display: none; }

/* ── Hero mascot — hover magique ── */

.hero__mascot img {
  transition: filter 0.5s ease;
  will-change: filter;
}

/* Conteneur lien */
.hero__mascot-link {
  display: block;
  position: relative;
  cursor: pointer;
}

/* Halo discret juste sous les pieds */
.hero__mascot-link::before {
  content: '';
  position: absolute;
  bottom: 2%; left: 20%; right: 20%;
  height: 12%;
  border-radius: 50%;
  background: radial-gradient(ellipse,
    rgba(200,140,255,0.32) 0%,
    rgba(255,200,80,0.12) 55%,
    transparent           80%
  );
  filter: blur(10px);
  opacity: 0;
  transition: opacity 0.6s ease;
  pointer-events: none;
}
.hero__mascot-link:hover::before { opacity: 1; }

/* Glow image au hover */
.hero__mascot-link:hover img {
  filter: drop-shadow(0 0 20px rgba(180,120,255,0.7))
          drop-shadow(0 0 50px rgba(255,210,80,0.4))
          drop-shadow(0 16px 48px rgba(232,150,16,0.3));
}

/* Particules — actives en permanence */
.mascot-spark {
  position: absolute;
  font-size: 0.95rem;
  color: rgba(255,215,80,0.95);
  text-shadow: 0 0 10px rgba(200,150,255,0.95), 0 0 20px rgba(255,200,80,0.6);
  pointer-events: none;
  will-change: transform, opacity;
  animation: mascot-spark-rise 2s ease-out infinite;
}
.ms1 { top: 30%; left:  5%; animation-delay: 0s; }
.ms2 { top: 14%; left: 36%; animation-delay: 0.4s; }
.ms3 { top: 20%; right: 8%; animation-delay: 0.8s; }
.ms4 { top: 50%; right: 3%; animation-delay: 1.2s; }
.ms5 { top: 65%; left: 16%; animation-delay: 1.6s; }

@keyframes mascot-spark-rise {
  0%   { opacity: 0;  transform: translateY(0)    scale(0.5); }
  22%  { opacity: 1;  transform: translateY(-22px) scale(1.2); }
  100% { opacity: 0;  transform: translateY(-70px) scale(0.3) rotate(30deg); }
}

/* Mobile */
@media (max-width: 768px) {
  .mascot-spark { font-size: 0.78rem; }
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
  .cin__bg-img,
  .cin__astra-img,
  .cin__crystal-img,
  .cin__lumina-img,
  .cin__astra-glow,
  .cin__final-logo,
  .cin__final-title { animation: none; }
  .cin__particle,
  .cin__lumina-dust { display: none; }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(-8px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ══════════════════════════════════════════════════════════
   DASHBOARD (home_private)
══════════════════════════════════════════════════════════ */

/* La page n'a plus d'image de fond : le décor tient dans le seul bandeau
   (`.page-hero--tableau-de-bord`). Reste la hauteur minimale, pour qu'un
   bestiaire vide ne laisse pas le pied de page remonter à mi-écran. */
.dashboard-page {
    min-height: 100vh;
}

/* L'en-tête « Mon bestiaire » est passé dans le bandeau : sans lui, les 4 rem
   de `.section` ouvraient un vide entre le bandeau et la barre de filtres. */
.dashboard-page .section { padding-top: 1rem; }

/* Hero overlay */
.dp-hero {
    position: relative;
    padding: 3rem 0 2rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 2rem;
}
.dp-hero__overlay {
    position: absolute; inset: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.35) 100%);
}
.dp-hero__inner {
    position: relative; z-index: 1;
    display: flex; align-items: center;
    justify-content: space-between; gap: 2rem;
}
.dp-hero__left { flex: 1; }
.dp-hero__name {
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: clamp(1.5rem,4vw,2.5rem);
    font-weight: 900; margin: 0;
    background: linear-gradient(135deg,#f5c842,#e8963c);
    -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
    text-shadow: none;
}
.dp-hero__sub {
    color: rgba(255,255,255,0.65);
    font-size: 0.9rem; margin: 0.4rem 0 0;
}


/* Navigation cards */
.dp-nav-cards {
    display: flex; align-items: center; gap: 2.5rem;
    flex-shrink: 0;
}
.dp-nav-card {
    display: flex; flex-direction: column; align-items: center; gap: 0.75rem;
    text-decoration: none; color: inherit;
    transition: transform .3s ease;
    cursor: pointer;
}
.dp-nav-card:hover { transform: translateY(-10px); }
.dp-nav-card__img {
    width: 65px; height: 65px; object-fit: contain;
    filter: drop-shadow(0 8px 24px rgba(0,0,0,0.55));
}
/* Jeux — wiggle */
.dp-nav-card:nth-child(1) .dp-nav-card__img {
    animation: dp-wiggle 2.4s ease-in-out infinite;
}
/* Forge — pulse glow doré */
.dp-nav-card:nth-child(2) .dp-nav-card__img {
    animation: dp-pulse-glow 2.8s ease-in-out infinite;
}
/* Bestiaire — wiggle décalé */
.dp-nav-card:nth-child(3) .dp-nav-card__img {
    animation: dp-wiggle 2.4s ease-in-out infinite;
    animation-delay: -1.2s;
}

@keyframes dp-wiggle {
    0%,100%  { transform: rotate(0deg) scale(1); }
    15%      { transform: rotate(-9deg) scale(1.05); }
    30%      { transform: rotate(9deg) scale(1.05); }
    45%      { transform: rotate(-5deg) scale(1.02); }
    60%      { transform: rotate(5deg) scale(1.02); }
    75%      { transform: rotate(-2deg); }
}
@keyframes dp-pulse-glow {
    0%,100%  { transform: scale(1);    filter: drop-shadow(0 8px 24px rgba(0,0,0,0.55)) drop-shadow(0 0 0px #f5c842); }
    50%      { transform: scale(1.1);  filter: drop-shadow(0 8px 24px rgba(0,0,0,0.55)) drop-shadow(0 0 18px rgba(245,200,66,0.8)); }
}
@keyframes dp-shake {
    0%,100%  { transform: translateX(0); }
    10%      { transform: translateX(-6px); }
    20%      { transform: translateX(6px); }
    30%      { transform: translateX(-5px); }
    40%      { transform: translateX(5px); }
    50%      { transform: translateX(-3px); }
    60%      { transform: translateX(3px); }
    70%      { transform: translateX(-1px); }
    80%      { transform: translateX(1px); }
}
.dp-nav-card__label {
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: 1rem; letter-spacing: 0.08em;
    color: #f5c842;
    text-shadow: 0 2px 8px rgba(0,0,0,0.6);
}
@media (max-width: 600px) {
    .dp-hero__inner { flex-direction: column; align-items: flex-start; gap: 1.25rem; }
    .dp-nav-cards { align-self: center; gap: 2rem; }
}
@media (max-width: 480px) {
    .dp-nav-cards { gap: 1.5rem; }
    .dp-nav-card__img { width: 48px; height: 48px; }
    .dp-nav-card__label { font-size: 0.85rem; }
}

/* ── Accessory drawer trigger ── */
.acc-drawer-trigger {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-top: 0.85rem;
    padding: 0.45rem 1rem;
    background: rgba(245,200,66,0.12);
    border: 1px solid rgba(245,200,66,0.35);
    border-radius: 20px;
    color: #f5c842;
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: 0.85rem; letter-spacing: 0.06em;
    cursor: pointer;
    transition: background .2s, border-color .2s, transform .2s;
}
.acc-drawer-trigger:hover {
    background: rgba(245,200,66,0.22);
    border-color: rgba(245,200,66,0.65);
    transform: translateY(-2px);
}
.acc-drawer-trigger__count {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.3rem; height: 1.3rem;
    background: #f5c842; color: #1a1a2e;
    border-radius: 50%;
    font-size: 0.7rem; font-weight: 900;
    font-family: var(--font-body, sans-serif);
}

/* ── Accessory drawer ── */
.acc-drawer-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.55);
    backdrop-filter: blur(2px);
    z-index: 400;
    opacity: 0; pointer-events: none;
    transition: opacity .3s ease;
}
.acc-drawer-overlay.is-open { opacity: 1; pointer-events: all; }

.acc-drawer {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(360px, 92vw);
    background: var(--bg-card, #1e1e3a);
    border-left: 1px solid rgba(245,200,66,0.2);
    box-shadow: -8px 0 40px rgba(0,0,0,0.6);
    z-index: 401;
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform .35s cubic-bezier(.4,0,.2,1);
}
.acc-drawer.is-open { transform: translateX(0); }

.acc-drawer__header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 1.25rem 1.25rem 1rem;
    border-bottom: 1px solid rgba(245,200,66,0.15);
    flex-shrink: 0;
}
.acc-drawer__title {
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: 1.1rem; color: #f5c842;
    margin: 0;
}
.acc-drawer__close {
    background: none; border: none;
    color: rgba(255,255,255,0.5); font-size: 1.1rem;
    cursor: pointer; padding: 0.25rem;
    transition: color .2s;
}
.acc-drawer__close:hover { color: #fff; }

.acc-drawer__body {
    flex: 1; overflow-y: auto;
    padding: 1rem 1.25rem;
}

.acc-drawer__empty {
    text-align: center; padding: 3rem 0;
    color: rgba(255,255,255,0.5);
}
.acc-drawer__empty p { margin-bottom: 1rem; }

.acc-drawer__list { display: flex; flex-direction: column; gap: 0.75rem; }

.acc-drawer__item {
    display: flex; align-items: center; gap: 0.85rem;
    padding: 0.75rem;
    background: rgba(255,255,255,0.04);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 10px;
    transition: background .2s;
}
.acc-drawer__item:hover { background: rgba(255,255,255,0.08); }

.acc-drawer__item-img {
    width: 3rem; height: 3rem; flex-shrink: 0;
    border-radius: 8px;
    overflow: hidden;
    background: rgba(255,255,255,0.06);
    display: flex; align-items: center; justify-content: center;
}
.acc-drawer__item-img img { width: 100%; height: 100%; object-fit: cover; }
.acc-drawer__item-placeholder { font-size: 1.4rem; }

.acc-drawer__item-name {
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: 0.85rem; color: #f5f5f5;
    margin: 0 0 0.2rem;
}
.acc-drawer__item-desc {
    font-size: 0.78rem; color: rgba(255,255,255,0.5);
    margin: 0 0 0.3rem;
    line-height: 1.4;
}
.acc-drawer__item-bonus {
    font-size: 0.75rem; color: #f5c842;
    font-weight: 700;
}

.acc-drawer__footer {
    padding-top: 1rem;
    border-top: 1px solid rgba(255,255,255,0.08);
    margin-top: 0.5rem;
    text-align: center;
}
.acc-drawer__forge-link {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}
.acc-drawer__forge-icon {
    width: 1.15em;
    height: 1.15em;
    object-fit: contain;
    flex-shrink: 0;
}

/* ── Drawer : sections déroulantes ───────────────────────── */
.acc-drawer__section { border-bottom: 1px solid rgba(255,255,255,.06); }
.acc-drawer__section:last-of-type { border-bottom: none; }

.acc-drawer__section-toggle {
    display: flex; align-items: center; justify-content: space-between;
    width: 100%; padding: .65rem 0; background: none; border: none;
    cursor: pointer; color: inherit; text-align: left;
    -webkit-tap-highlight-color: transparent;
}
.acc-drawer__section-toggle:hover .acc-drawer__section-label { color: var(--text); }
.acc-drawer__section-label {
    font-size: .72rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
    color: var(--text-muted); display: flex; align-items: center; gap: .45rem;
    transition: color .15s;
}
.acc-drawer__chevron {
    font-size: .65rem; color: var(--text-muted);
    display: inline-block; transition: transform .2s;
    flex-shrink: 0;
}
.acc-drawer__section-toggle[aria-expanded="true"] .acc-drawer__chevron { transform: rotate(180deg); }

.acc-drawer__section-body { padding-bottom: .65rem; }
.acc-drawer__section-body[hidden] { display: none; }

.acc-drawer__section-count {
    background: rgba(245,200,66,.15); color: #f5c842;
    border: 1px solid rgba(245,200,66,.3);
    border-radius: 20px; font-size: .65rem; font-weight: 800;
    padding: .05rem .4rem; line-height: 1.4;
}
.acc-drawer__empty-small { font-size: .75rem; color: rgba(255,255,255,.3); margin: 0; font-style: italic; }

/* ── Drawer : rune-grid (fragments + astralithe) ── */
.acc-drawer__rune-grid { display: flex; flex-wrap: wrap; gap: .5rem; }
.acc-drawer__rune-item {
    position: relative; width: 52px; height: 52px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1);
    border-radius: 10px; display: flex; align-items: center; justify-content: center;
    cursor: default;
}
.acc-drawer__rune-img { width: 32px; height: 32px; object-fit: contain; filter: drop-shadow(0 2px 4px rgba(0,0,0,.5)); }
.acc-drawer__rune-img--frag { filter: drop-shadow(0 2px 4px rgba(0,0,0,.5)) sepia(.4) hue-rotate(10deg) brightness(.85); }
.acc-drawer__rune-qty { position: absolute; bottom: 2px; right: 4px; font-size: .6rem; font-weight: 800; color: #f5c842; line-height: 1; }
.acc-drawer__equip-state { position: absolute; bottom: 2px; right: 3px; font-size: .5rem; font-weight: 800; line-height: 1; padding: 1px 3px; border-radius: 3px; }
.acc-drawer__equip-state--on   { background: rgba(180,80,255,.35); color: #e0b8ff; }
.acc-drawer__equip-state--free { background: rgba(60,200,120,.25); color: #7ee8a2; }

/* ── Drawer : équipements compacts ── */
.acc-drawer__equip-grid { display: flex; flex-wrap: wrap; gap: .5rem; }
.acc-drawer__equip-slot {
    position: relative; width: 52px; height: 52px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1);
    border-radius: 10px; display: flex; align-items: center; justify-content: center;
    cursor: default; overflow: hidden;
    transition: border-color .15s;
}
.acc-drawer__equip-slot--arme       { border-color: rgba(255,80,60,.25); }
.acc-drawer__equip-slot--protection { border-color: rgba(60,160,255,.25); }
.acc-drawer__equip-slot--artefact   { border-color: rgba(180,80,255,.25); }
.acc-drawer__equip-img { width: 36px; height: 36px; object-fit: contain; filter: drop-shadow(0 2px 4px rgba(0,0,0,.5)); }
.acc-drawer__equip-icon { font-size: 1.4rem; line-height: 1; }
.acc-drawer__equip-badge {
    position: absolute; bottom: 2px; right: 3px;
    font-size: .55rem; line-height: 1;
}

/* tooltip géré en JS (position: fixed) — pas de ::after clippé par le drawer */

/* ── Tooltip mobile (bas d'écran) ── */
.inv-tooltip {
    position: fixed; bottom: 0; left: 0; right: 0; z-index: 1100;
    background: #110d07; border-top: 1px solid rgba(255,255,255,.12);
    padding: 1rem 1.1rem 1.4rem; border-radius: 16px 16px 0 0;
    transform: translateY(100%); transition: transform .25s cubic-bezier(.4,0,.2,1);
    pointer-events: none;
}
.inv-tooltip.is-open { transform: translateY(0); pointer-events: all; }
.inv-tooltip__close {
    position: absolute; top: .7rem; right: .9rem;
    background: none; border: none; color: var(--text-muted);
    font-size: .85rem; cursor: pointer; padding: .2rem .4rem;
    -webkit-tap-highlight-color: transparent;
}
.inv-tooltip__name {
    font-family: var(--font-display); font-size: 1rem; font-weight: 700;
    color: #c8d8ff; margin: 0 0 .45rem;
}
.inv-tooltip__meta { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .45rem; }
.inv-tooltip__meta-tag {
    font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    border-radius: 4px; padding: .1rem .4rem; border: 1px solid;
}
.inv-tooltip__desc { font-size: .78rem; color: var(--text-muted); margin: 0 0 .3rem; }
.inv-tooltip__desc:empty { display: none; }
.inv-tooltip__bonus {
    display: inline-block; font-size: .72rem; font-weight: 700;
    background: rgba(80,140,255,.12); border: 1px solid rgba(80,140,255,.3);
    color: #80b0ff; border-radius: 20px; padding: .1rem .5rem;
}
.inv-tooltip__bonus:empty { display: none; }

/* ── Drawer : potions ── */
.acc-drawer__potion-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .3rem; }

/* Potion row — compact + expand */
.acc-potion-row {
    border-radius: 9px;
    border: 1px solid rgba(120,80,200,.15);
    background: rgba(120,80,200,.06);
    overflow: hidden;
    transition: border-color .15s;
}
.acc-potion-row:has(.acc-potion-panel:not([hidden])) { border-color: rgba(120,80,200,.35); }

.acc-potion-row__head {
    display: flex; align-items: center; gap: .5rem;
    width: 100%; padding: .42rem .6rem;
    background: none; border: none; cursor: pointer;
    color: inherit; text-align: left;
    transition: background .12s;
}
.acc-potion-row__head:hover { background: rgba(120,80,200,.1); }

.acc-potion-row__icon { font-size: .95rem; flex-shrink: 0; }
.acc-potion-row__info { flex: 1; min-width: 0; }
.acc-potion-row__name {
    display: block; font-size: .74rem; font-weight: 700; color: #c8a8ff;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.acc-potion-row__bonus { display: block; font-size: .6rem; color: var(--text-muted); }
.acc-potion-row__qty {
    flex-shrink: 0; font-size: .68rem; font-weight: 800;
    background: rgba(120,80,200,.2); color: #c8a8ff; border-radius: 10px; padding: .06rem .32rem;
}
.acc-potion-row__chevron {
    flex-shrink: 0; font-size: .65rem; color: var(--text-muted);
    display: inline-block; transition: transform .18s;
}
.acc-potion-row__head[aria-expanded="true"] .acc-potion-row__chevron { transform: rotate(180deg); }

/* Panel revealed on expand */
.acc-potion-panel {
    padding: .4rem .6rem .55rem;
    border-top: 1px solid rgba(120,80,200,.15);
    display: flex; flex-direction: column; gap: .45rem;
}
.acc-potion-panel[hidden] { display: none; }

/* Monster pills */
.acc-potion-pills {
    display: flex; flex-wrap: wrap; gap: .28rem;
    max-height: 96px; overflow-y: auto;
}
.acc-potion-pill {
    display: inline-flex; align-items: center; gap: .28rem;
    padding: .18rem .42rem .18rem .22rem;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1);
    border-radius: 20px; cursor: pointer;
    color: var(--text-muted); font-size: .63rem; font-weight: 600;
    transition: background .12s, border-color .12s, color .12s;
}
.acc-potion-pill:hover { background: rgba(245,200,66,.1); border-color: rgba(245,200,66,.35); color: #f5e8c0; }
.acc-potion-pill.is-selected { background: rgba(245,200,66,.18); border-color: rgba(245,200,66,.65); color: #f5c842; }
.acc-potion-pill__img { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.acc-potion-pill__name { max-width: 68px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.acc-potion-pill__lvl { flex-shrink: 0; color: var(--text-dim); font-size: .56rem; }
.acc-potion-pill.is-selected .acc-potion-pill__lvl { color: rgba(245,200,66,.65); }

/* Form row */
.acc-potion-form { display: flex; justify-content: flex-end; }
.acc-potion-form__submit:disabled { opacity: .38; cursor: not-allowed; }
.acc-drawer__flash { font-size: .78rem; border-radius: 6px; padding: .35rem .65rem; margin-bottom: .5rem; }
.acc-drawer__flash--success { background: rgba(80,200,120,.15); color: #7ae0a0; border: 1px solid rgba(80,200,120,.3); }
.acc-drawer__flash--error,.acc-drawer__flash--fail { background: rgba(220,80,80,.15); color: #f08080; border: 1px solid rgba(220,80,80,.3); }

/* ── Navbar : bouton inventaire ── */

.section-count {
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(245,200,66,0.1); border: 1px solid rgba(245,200,66,0.25);
    border-radius: 20px; padding: 0.1rem 0.6rem;
    font-size: 0.72rem; color: var(--accent);
    font-family: var(--font-body, 'Lato', sans-serif);
    font-weight: 400; letter-spacing: 0; vertical-align: middle;
    margin-left: 0.5rem;
}
.dash-empty { color: var(--text-muted); padding: 1.5rem 0; }
.dash-empty a { color: var(--accent); }

/* ── Filter toggle ── */
.bestiary-filter-bar {
    display: flex; align-items: center; gap: 0.75rem; margin: 0.5rem 0 0;
}
.bestiary-filter-toggle {
    display: inline-flex; align-items: center; gap: 0.45rem;
    background: #212134; border: 1px solid rgba(255,255,255,0.14);
    border-radius: 8px; padding: 0.45rem 1rem;
    font-size: 0.82rem; color: var(--text-muted);
    cursor: pointer; transition: border-color .2s, color .2s;
}
.bestiary-filter-toggle:hover,
.bestiary-filter-toggle[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.bestiary-filter-toggle.has-active { border-color: rgba(245,200,66,0.45); color: var(--accent); }
.filter-toggle__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.filter-toggle__chevron { font-size: 0.7rem; transition: transform .25s; }
.bestiary-filter-toggle[aria-expanded="true"] .filter-toggle__chevron { transform: rotate(180deg); }
.bestiary-filter-reset { font-size: 0.78rem; color: var(--text-muted); text-decoration: none; opacity: .7; transition: opacity .2s, color .2s; }
.bestiary-filter-reset:hover { opacity: 1; color: #f08080; }

/* ── Le passage au plein écran, dans la barre de la grille ───────────────────
   Le pendant exact du ⊞ que la scène porte sur son bord. C'était une bascule à
   deux libellés, « Grille | Plein écran », qui disait surtout où l'on était
   déjà : sur la page de la grille, la moitié du contrôle ne menait nulle part.

   Même surface, même bord et même rayon que le bouton « Filtrer » qu'il côtoie —
   ce sont deux commandes de la même barre, elles n'ont pas à se ressembler de
   loin. Carré plutôt qu'allongé, parce qu'il ne porte qu'un signe. */
.bestiary-vue-raccourci {
    display: inline-flex; align-items: center; justify-content: center;
    margin-left: auto; flex-shrink: 0;
    width: 2.5rem; height: 2.5rem;
    background: #212134; border: 1px solid rgba(255,255,255,0.14);
    border-radius: 8px;
    font-size: 0.95rem; color: var(--text-muted);
    text-decoration: none;
    transition: border-color .2s, color .2s;
}
.bestiary-vue-raccourci:hover { border-color: var(--accent); color: var(--accent); }
.bestiary-vue-raccourci:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Filter panel ── */
.bestiary-filter-panel {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .3s ease, margin .3s ease; margin-top: 0;
}
.bestiary-filter-panel.is-open { grid-template-rows: 1fr; margin-top: 1rem; }
.bestiary-filter-panel > .bestiary-filters__row { overflow: hidden; padding-bottom: 1.25rem; }
.bestiary-filters__row { display: flex; flex-direction: column; gap: 1rem; }
.bestiary-filter-label { display: block; font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.4rem; }
.bestiary-filter-pills { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.filter-pill {
    display: inline-block; padding: 0.3rem 0.75rem; border-radius: 20px; font-size: 0.78rem;
    border: 1px solid rgba(255,255,255,0.12); color: var(--text-muted); text-decoration: none;
    transition: border-color .2s, color .2s, background .2s; white-space: nowrap;
}
.filter-pill:hover { border-color: var(--accent); color: var(--accent); }
.filter-pill--active { background: rgba(245,200,66,0.12); border-color: var(--accent); color: var(--accent); }
.filter-pill--feu    { border-color: rgba(255,90,50,0.4);   color: #ff7a55; }
.filter-pill--eau    { border-color: rgba(60,160,255,0.4);  color: #5aafff; }
.filter-pill--terre  { border-color: rgba(140,200,80,0.4);  color: #9ecf5a; }
.filter-pill--astral { border-color: rgba(170,100,255,0.4); color: #c07fff; }
.filter-pill--ombre  { border-color: rgba(100,80,180,0.4);  color: #9080c8; }
.filter-pill--lumiere{ border-color: rgba(255,240,120,0.4); color: #ffe87a; }

/* ── Beast-card ── */

/* Le conteneur du bestiaire ne dessine plus rien : c'est le fond de page qui
   porte l'ambiance, et les cartes s'y posent directement. Il reste dans le
   balisage parce que le rechargement AJAX des filtres remplace son contenu —
   le supprimer casserait le point d'ancrage.

   Les cartes, elles, sont bien des surfaces opaques reprenant celles du modal
   monstre : le `backdrop-filter` d'avant floutait l'illustration de fond et le
   texte y perdait le contraste que la carte est censée lui garantir. */
.bestiary-glass-panel {
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
}
/* La vitrine du bestiaire. Elle fut une piste de carrousel que le CSS
   redessinait en grille selon la vue ; le carrousel des créatures a été retiré,
   et la grille redevient ce qu'elle est — une liste en colonnes, que personne
   n'a besoin de mesurer.

   Les cases s'étirent à la hauteur de leur rangée (`stretch`, par défaut), et la
   carte remplit sa case : sans cela, deux créatures aux textes de longueurs
   différentes donnent deux cartes de hauteurs différentes et la rangée flotte. */
.bestiary-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 1.5rem; padding-bottom: 2rem; margin-top: 1.25rem;
}
.bestiary-grid > li { display: flex; }
.bestiary-grid > li > .beast-card { width: 100%; }
.beast-card {
    position: relative; display: flex; flex-direction: column;
    background: #212134; border: 1px solid rgba(245,200,66,0.22);
    border-radius: 14px; padding: 1rem; color: inherit;
    box-shadow: 0 4px 16px rgba(0,0,0,0.4);
    transition: border-color .25s, transform .25s, box-shadow .25s; cursor: pointer; overflow: hidden;
}
.beast-card::before {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(135deg, rgba(245,200,66,0.04) 0%, transparent 60%); pointer-events: none;
}
.beast-card:hover, .beast-card:focus-visible {
    border-color: rgba(245,200,66,0.55); transform: translateY(-3px);
    box-shadow: 0 8px 28px rgba(0,0,0,.35), 0 0 20px rgba(245,200,66,.08);
    outline: none;
}
.beast-card--evolve { border-color: rgba(100,220,100,0.35); box-shadow: 0 0 18px rgba(100,220,100,.07); }
.beast-card--evolve:hover { border-color: rgba(100,220,100,0.7); }
.beast-card--fav::after {
    content: ''; position: absolute; top: 0; right: 0; width: 60px; height: 60px;
    background: radial-gradient(circle at top right, rgba(255,80,80,.12), transparent 70%); pointer-events: none;
}
.beast-card__fav-badge { position: absolute; top: .6rem; left: .7rem; color: #f08080; font-size: .85rem; z-index: 2; filter: drop-shadow(0 0 4px rgba(255,80,80,.5)); }
.beast-card__evolve-badge {
    position: absolute; top: .55rem; right: .6rem;
    background: rgba(80,200,80,.18); border: 1px solid rgba(80,200,80,.4);
    border-radius: 20px; padding: .15rem .55rem; font-size: .65rem; color: #7ecf7e; letter-spacing: .04em; z-index: 2;
    animation: evolve-pulse 2.5s ease-in-out infinite;
}
@keyframes evolve-pulse { 0%,100%{opacity:1} 50%{opacity:.6} }
.beast-card__img-wrap { display: flex; justify-content: center; align-items: center; height: 130px; margin-bottom: .75rem; }
.beast-card__img { max-height: 120px; max-width: 100%; object-fit: contain; transition: transform .3s ease; filter: drop-shadow(0 4px 12px rgba(0,0,0,.4)); }
.beast-card:hover .beast-card__img { transform: scale(1.06) translateY(-3px); }

.beast-card__header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: .5rem; gap: .5rem; }
.beast-card__name { font-weight: 700; font-size: .95rem; color: #f5e8c0; margin: 0; line-height: 1.2; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.beast-card__level { font-size: .72rem; color: var(--accent); background: rgba(245,200,66,.1); border: 1px solid rgba(245,200,66,.25); border-radius: 10px; padding: .1rem .45rem; white-space: nowrap; flex-shrink: 0; }
.beast-card__tags { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .6rem; }
.beast-rarity { display: inline-flex; align-items: center; gap: .25rem; font-size: .65rem; border-radius: 10px; padding: .1rem .5rem; border: 1px solid; }
.beast-rarity--commun     { color: #a0a0a0; border-color: rgba(160,160,160,.3); }
.beast-rarity--rare       { color: #5aafff; border-color: rgba(90,175,255,.35); }
.beast-rarity--epique     { color: #c07fff; border-color: rgba(192,127,255,.35); }
.beast-rarity--legendaire { color: #f5c842; border-color: rgba(245,200,66,.45); text-shadow: 0 0 8px rgba(245,200,66,.4); }
.beast-rarity--peu_commun { color: #7ecfa0; border-color: rgba(126,207,160,.3); }
.beast-card__stats { display: flex; gap: .55rem; flex-wrap: wrap; font-size: .72rem; color: var(--text-muted); margin-bottom: .6rem; }
.beast-card__xp { margin-bottom: .75rem; }
.xp-bar { height: 4px; background: rgba(255,255,255,.08); border-radius: 2px; overflow: hidden; margin-bottom: .25rem; }
.xp-bar__fill { height: 100%; background: linear-gradient(90deg,#f5c842,#e8963c); border-radius: 2px; transition: width 1.1s ease-out; }
.xp-label { font-size: .65rem; color: var(--text-muted); }
/* `.beast-card__cta` — « Voir les détails → », qui n'apparaissait qu'au survol —
   a laissé la place à `.beast-card__actions` : deux vraies commandes, visibles
   sans survol, atteignables au doigt et au clavier. Son dessin est plus bas. */

@media (max-width: 900px) {
    .bestiary-grid { grid-template-columns: repeat(3,1fr); gap: 1rem; }
}
@media (max-width: 640px) {
    .bestiary-grid { grid-template-columns: repeat(2,1fr); gap: .75rem; }
    .beast-card { padding: .75rem; }
    .beast-card__img-wrap { height: 90px; margin-bottom: .5rem; }
    .beast-card__img { max-height: 80px; }
    .beast-card__stats { display: none; }
}
@media (max-width: 380px) {
    .bestiary-grid { grid-template-columns: 1fr; }
    .beast-card__img-wrap { height: 100px; }
    .beast-card__img { max-height: 90px; }
}

/* ══════════════════════════════════════════════════════════
   MODAL MONSTRE
══════════════════════════════════════════════════════════ */
.mm-overlay {
    display: none; position: fixed; inset: 0; z-index: 1100;
    background: rgba(0,0,0,0.75); backdrop-filter: blur(4px);
    align-items: center; justify-content: center; padding: 1rem;
}
.mm-overlay.active { display: flex; }

.mm-box {
    position: relative; background: #1a1a2e;
    border: 1px solid rgba(245,200,66,0.25); border-radius: 18px;
    width: 100%; max-width: 700px; max-height: 90dvh;
    padding: 2rem; animation: mm-in .22s ease; overflow: hidden;
    display: flex; flex-direction: column;
}
@keyframes mm-in {
    from { opacity: 0; transform: scale(0.95) translateY(10px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
}

.mm-close {
    position: absolute; top: 1rem; right: 1rem;
    background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12);
    border-radius: 50%; width: 2rem; height: 2rem;
    color: var(--text-muted); cursor: pointer; font-size: 0.85rem;
    display: flex; align-items: center; justify-content: center;
    transition: background .2s, color .2s; flex-shrink: 0;
}
.mm-close:hover { background: rgba(255,80,80,0.15); color: #f08080; }

/* ── Navigation d'une créature à l'autre ──────────────────────
   Les chevrons « respirent » pour signaler qu'il y a une suite de chaque
   côté — l'animation se coupe si la personne a demandé moins de mouvement.
   Ils se masquent quand il n'y a rien dans cette direction : `hidden` plutôt
   qu'une opacité nulle, pour qu'ils sortent aussi de l'ordre de tabulation. */
.mm-nav {
    position: absolute; top: 50%; transform: translateY(-50%);
    z-index: 2;
    width: 3rem; height: 3rem; border-radius: 50%;
    background: rgba(26,26,46,0.85);
    border: 1px solid rgba(245,200,66,0.3);
    color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; padding: 0;
    transition: background .2s, border-color .2s, box-shadow .2s;
}
.mm-nav[hidden] { display: none; }
.mm-nav:hover, .mm-nav:focus-visible {
    background: rgba(245,200,66,0.14);
    border-color: rgba(245,200,66,0.75);
    box-shadow: 0 0 18px rgba(245,200,66,0.22);
    outline: none;
}
.mm-nav--prev { left: clamp(0.5rem, calc(50% - 25rem), 3rem); }
.mm-nav--next { right: clamp(0.5rem, calc(50% - 25rem), 3rem); }

.mm-nav__chevron {
    font-size: 2rem; line-height: 1;
    font-family: var(--font-fantasy,'Cinzel',serif);
    text-shadow: 0 0 10px rgba(245,200,66,0.45);
    animation: mm-nav-breathe 2.2s ease-in-out infinite;
}
.mm-nav--next .mm-nav__chevron { animation-name: mm-nav-breathe-next; }

@keyframes mm-nav-breathe {
    0%, 100% { opacity: .55; transform: translateX(0)      scale(1); }
    50%      { opacity: 1;   transform: translateX(-3px)   scale(1.12); }
}
@keyframes mm-nav-breathe-next {
    0%, 100% { opacity: .55; transform: translateX(0)      scale(1); }
    50%      { opacity: 1;   transform: translateX(3px)    scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
    .mm-nav__chevron { animation: none; opacity: .85; }
}

/* Sous 900 px le modal occupe toute la largeur : les chevrons se posent sur
   ses bords plutôt qu'à côté, et rétrécissent pour ne pas manger le portrait. */
@media (max-width: 900px) {
    .mm-nav { width: 2.4rem; height: 2.4rem; }
    .mm-nav__chevron { font-size: 1.5rem; }
    .mm-nav--prev { left: 0.35rem; }
    .mm-nav--next { right: 0.35rem; }
}

/* ── La fiche, de haut en bas ─────────────────────────────────────────────
   Elle tenait sur deux colonnes — portrait et actions à gauche, tout le reste
   à droite —, et l'ordre de lecture y dépendait de la place disponible : la
   description arrivait avant les statistiques sur grand écran, après sur
   téléphone, et les pastilles d'élément se lisaient avant le niveau.

   Une seule colonne, donc, et un ordre choisi : l'allure, le nom, ce qu'elle
   vaut, sa lignée, son évolution, ses chiffres, ce qu'on peut en faire, son
   histoire, ses éléments. C'est l'ordre du balisage — aucune règle `order` ne
   le récrit, à aucune largeur. Ce qu'on lit est ce qui est écrit. */
.mm-layout {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    overflow-y: auto; overflow-x: hidden; flex: 1; min-width: 0;
}
.mm-layout > * { min-width: 0; overflow-wrap: break-word; }

.mm-portrait {
    border-radius: 14px; background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    display: flex; align-items: center; justify-content: center;
    padding: 1.5rem 1.25rem; min-height: 210px;
}
.mm-portrait.mm-glow--feu    { box-shadow: 0 0 28px rgba(255,90,50,.18); }
.mm-portrait.mm-glow--eau    { box-shadow: 0 0 28px rgba(60,160,255,.18); }
.mm-portrait.mm-glow--terre  { box-shadow: 0 0 28px rgba(140,200,80,.18); }
.mm-portrait.mm-glow--astral { box-shadow: 0 0 28px rgba(170,100,255,.18); }
.mm-portrait.mm-glow--ombre  { box-shadow: 0 0 28px rgba(100,80,180,.18); }
.mm-portrait.mm-glow--lumiere{ box-shadow: 0 0 28px rgba(255,240,120,.18); }

.mm-img {
    max-width: 190px; max-height: 190px; object-fit: contain;
    filter: drop-shadow(0 6px 18px rgba(0,0,0,.5));
    animation: float-detail 5s ease-in-out infinite;
}
@keyframes float-detail { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-6px)} }

/* .mm-content remplacé par la colonne unique de .mm-layout */

/* 2 — Le nom, la classe et la rareté sur la même ligne. Elles se replient
   dessous quand le nom est long : `baseline` aligne alors le premier rang, et
   les pastilles restent posées sur le trait du nom plutôt que flottantes. */
.mm-titre {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 0.35rem 0.7rem;
}
.mm-name {
    font-family: var(--font-fantasy,'Cinzel',serif);
    font-size: clamp(1.1rem,2.5vw,1.5rem); font-weight: 900; margin: 0;
    background: linear-gradient(135deg,#f5c842,#e8963c);
    -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
    padding-right: 1.5rem;
}

/* 6 — L'évolution est centrée : c'est la seule commande de la colonne, et une
   invitation alignée à gauche au milieu d'une fiche ne se voit pas. */
.mm-evolve { display: flex; justify-content: center; }
.mm-evolve:empty { display: none; }
.mm-evolve > .mm-evolve-block { width: 100%; max-width: 26rem; }

/* 9 — L'histoire garde son fer à gauche : c'est du texte suivi, et du texte
   suivi centré se relit mal ligne après ligne. */
.mm-desc { text-align: left; }

/* 10 — Le pied : les éléments, et la mention de favori. Centré, comme la
   signature d'une fiche — le seul bloc qu'on lit une fois qu'on sait déjà
   tout le reste. */
.mm-pied {
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
    gap: 0.35rem; padding-top: 0.25rem;
    border-top: 1px solid rgba(255,255,255,0.06);
}
.mm-pied[hidden], .mm-badges[hidden], .mm-element-line[hidden], .mm-fav-indicator[hidden] { display: none; }

.mm-element-line { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.mm-element-pill {
    font-size: 0.7rem; font-weight: 600; letter-spacing: 0.03em;
    padding: 0.15rem 0.55rem; border-radius: 20px;
    border: 1px solid currentColor; opacity: 0.85;
}
.mm-badges { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.mm-fav-indicator {
    font-size: 0.72rem; color: #f08080;
    padding: 0.1rem 0.5rem; border-radius: 10px;
    background: rgba(255,80,80,0.1); border: 1px solid rgba(255,80,80,0.25);
}

/* Stats */
.mm-stats { display: grid; grid-template-columns: repeat(2,1fr); gap: 0.45rem; }
.mm-stat {
    display: flex; align-items: center; gap: 0.4rem;
    background: rgba(255,255,255,0.03); border-radius: 8px; padding: 0.45rem 0.6rem;
    min-width: 0;
}
.mm-stat-icon  { font-size: 0.95rem; flex-shrink: 0; }
.mm-stat-label { font-size: 0.72rem; color: var(--text-muted); flex: 1; }
.mm-stat-val   { font-weight: 700; font-size: 0.92rem; color: #f5e8c0; }
.mm-stat-base  { font-size: 0.62rem; color: var(--text-muted); margin-left: 0.2rem; }

/* XP */
.mm-xp-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.4rem; }
.mm-level-badge {
    background: rgba(245,200,66,0.15); border: 1px solid rgba(245,200,66,0.35);
    border-radius: 20px; padding: 0.15rem 0.75rem;
    font-size: 0.78rem; color: var(--accent); font-weight: 700;
}
.mm-xp-text { font-size: 0.78rem; color: var(--text-muted); }
.xp-bar--lg { height: 6px; border-radius: 3px; }

/* Skill */
.mm-skill {
    background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
    border-radius: 10px; padding: 0.7rem 0.9rem;
}
.mm-skill-label { font-size: 0.62rem; letter-spacing:.1em; text-transform:uppercase; color:var(--text-muted); margin:0 0 0.2rem; }
.mm-skill-name  { font-weight:700; color:#f5e8c0; margin:0 0 0.15rem; font-size:0.9rem; }
.mm-skill-effect{ font-size:0.78rem; color:var(--text-muted); margin:0; }
.mm-skill-val   { color:var(--accent); font-weight:700; margin-left:0.2rem; }

/* Description */
.mm-desc-text {
    font-size: 0.84rem; color: var(--text-muted); line-height: 1.6; margin: 0;
    padding: 0.6rem 0.85rem;
    background: rgba(255,255,255,0.03);
    border-left: 2px solid rgba(245,200,66,0.25);
    border-radius: 0 8px 8px 0;
}

/* ── Lignée d'évolution ───────────────────────────────────────
   La frise de la chaîne, et l'aperçu d'une forme qu'on n'a plus. */
.mm-lineage { display: block; }
.mm-lineage[hidden] { display: none; }
.mm-lineage__label {
    font-size: 0.62rem; letter-spacing: .1em; text-transform: uppercase;
    color: var(--text-muted); margin: 0 0 0.45rem;
}
.mm-lineage__chain {
    display: flex; align-items: center; gap: 0.35rem;
    flex-wrap: wrap;
}
.mm-lineage__link {
    position: relative; flex-shrink: 0;
    width: 3.25rem; height: 3.25rem; padding: 0;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 10px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: border-color .2s, background .2s, transform .2s;
}
.mm-lineage__link:hover,
.mm-lineage__link:focus-visible {
    border-color: rgba(245,200,66,0.6); transform: translateY(-2px); outline: none;
}
.mm-lineage__img { max-width: 78%; max-height: 78%; object-fit: contain; }

/* La forme possédée aujourd'hui : elle se distingue à l'œil sans légende. */
.mm-lineage__link.is-current {
    border-color: rgba(245,200,66,0.55);
    background: rgba(245,200,66,0.08);
    box-shadow: 0 0 12px rgba(245,200,66,0.15);
}
/* Les formes à venir n'apparaissent que pour un administrateur : le pointillé
   dit qu'elles ne sont pas encore atteintes, et le voile qu'on les regarde
   depuis la régie, pas depuis le jeu. */
.mm-lineage__link.is-future {
    border-style: dashed; border-color: rgba(255,255,255,0.22); opacity: .55;
}
.mm-lineage__link.is-future:hover { opacity: .9; }
.mm-lineage__link.is-active { border-color: var(--accent); background: rgba(245,200,66,0.12); }

.mm-lineage__stage {
    position: absolute; bottom: -2px; right: 2px;
    font-size: 0.55rem; font-weight: 700; color: var(--text-muted);
    background: #1a1a2e; border-radius: 4px; padding: 0 0.2rem;
}
.mm-lineage__link.is-current .mm-lineage__stage { color: var(--accent); }
.mm-lineage__arrow { color: var(--text-muted); font-size: 0.9rem; flex-shrink: 0; }

/* ── Aperçu d'une forme de la lignée ──────────────────────────
   L'aperçu retire du modal tout ce qui ne décrit que la créature possédée :
   son niveau, son XP, sa compétence, son évolution et ses actions. Ce qui
   reste — l'allure, le nom, l'histoire, les statistiques de base — appartient
   bien à la forme regardée. */
.mm-box.is-preview .mm-actions,
.mm-box.is-preview .mm-badges,
.mm-box.is-preview .mm-pied,
.mm-box.is-preview .mm-xp,
.mm-box.is-preview .mm-skill,
.mm-box.is-preview #mm-rename-wrap,
.mm-box.is-preview .mm-evolve { display: none; }

.mm-preview-banner {
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
    background: rgba(255,255,255,0.04);
    border: 1px dashed rgba(245,200,66,0.3);
    border-radius: 10px; padding: 0.55rem 0.8rem;
}
.mm-preview-banner[hidden] { display: none; }
.mm-preview-banner__label {
    font-size: 0.72rem; letter-spacing: .04em; text-transform: uppercase;
    color: var(--accent);
}
.mm-preview-banner__back {
    margin-left: auto; background: none; border: 0; padding: 0;
    color: var(--text-muted); font-size: 0.75rem; cursor: pointer;
    text-decoration: underline; text-underline-offset: 2px;
}
.mm-preview-banner__back:hover { color: var(--accent); }

/* Évolution */
.mm-evolve-block {
    border-radius: 10px; padding: 0.8rem 1rem;
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
}
.mm-evolve--locked { background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.08); }
.mm-evolve--ready  { background:rgba(80,200,80,0.07); border:1px solid rgba(80,200,80,0.3); }
.mm-evolve--max    { background:rgba(245,200,66,0.07); border:1px solid rgba(245,200,66,0.25); }
/* Lignée non définie : ni verrou ni promesse. Le pointillé dit « en cours »
   là où le trait plein du verrou dirait « refusé ». */
.mm-evolve--soon   { background:rgba(120,160,255,0.06); border:1px dashed rgba(140,170,255,0.35); }
.mm-evolve--soon .evolve-title { color:#9fb8ff; }
.mm-evolve-block .evolve-icon  { font-size:1.2rem; }
.mm-evolve-block .evolve-title { font-weight:700; font-size:0.9rem; margin:0; color:#f5e8c0; flex:1; }
.mm-evolve-block .evolve-desc  { font-size:0.8rem; color:var(--text-muted); margin:0; width:100%; }
.mm-evolve-block .evolve-missing{ font-size:0.78rem; color:#f08080; margin:0; width:100%; }
.evolve-anim { display:inline-block; animation:evolve-bounce 1.8s ease-in-out infinite; }
@keyframes evolve-bounce { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-5px)} }

/* Flash */
#mm-flash { font-size:0.83rem; border-radius:8px; }
#mm-flash:not(:empty) { padding:0.55rem 0.8rem; }
#mm-flash.mm-ok  { background:rgba(80,200,80,0.12); border:1px solid rgba(80,200,80,0.3); color:#7ecf7e; }
#mm-flash.mm-err { background:rgba(255,80,80,0.1); border:1px solid rgba(255,80,80,0.25); color:#f08080; }

/* Actions */
.mm-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    padding: 0.7rem 1rem;
    background: rgba(10, 6, 2, 0.88);
    border-radius: 14px;
    border: 1px solid rgba(232, 150, 16, 0.22);
    box-shadow: 0 2px 20px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04);
}
.mm-actions form { flex: none; margin: 0; }
.mm-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(232, 150, 16, 0.08);
    border: 1px solid rgba(232, 150, 16, 0.22);
    color: var(--text-muted);
    font-size: 1.1rem;
    cursor: pointer;
    transition: background .15s, transform .12s, border-color .15s, color .15s, box-shadow .15s;
    text-decoration: none;
    flex-shrink: 0;
    line-height: 1;
}
.mm-action-btn:hover {
    background: rgba(232, 150, 16, 0.18);
    border-color: rgba(232, 150, 16, 0.55);
    color: var(--text);
    transform: scale(1.1);
    box-shadow: 0 0 10px rgba(232,150,16,0.15);
}
.mm-action-btn--active { color: #f08080 !important; background: rgba(240,128,128,0.12) !important; border-color: rgba(240,128,128,0.4) !important; }
.mm-action-btn--equip {
    background: rgba(245,200,66,0.1);
    border-color: rgba(245,200,66,0.38);
    color: #f5c842;
}
.mm-action-btn--equip:hover {
    background: rgba(245,200,66,0.2);
    border-color: rgba(245,200,66,0.65);
    color: #f5e8c0;
    transform: scale(1.03);
    box-shadow: 0 0 18px rgba(245,200,66,0.2);
}

/* Responsive modal */
@media (max-width: 580px) {
    .mm-box { padding: 1rem 0.85rem; max-height: 95dvh; }
    /* La colonne est la même qu'en grand : plus étroite, plus serrée, mais dans
       le même ordre. Aucune règle `order` ici — c'était ce qui faisait que la
       fiche ne se lisait pas pareil selon l'écran. */
    .mm-layout       { gap: 0.6rem; }
    .mm-portrait     { min-height: 140px; padding: 0.75rem; }
    .mm-img          { max-width: 125px; max-height: 125px; }
    .mm-actions      { padding: 0.45rem 0.65rem; gap: 0.35rem; }
    .mm-action-btn   { width: 32px; height: 32px; font-size: 0.88rem; }

    .mm-name { padding-right: 1.8rem; font-size: clamp(0.95rem, 4vw, 1.2rem); }

    .mm-desc-text {
        font-size: 0.76rem; line-height: 1.55;
        overflow-wrap: break-word; word-break: break-word; white-space: normal;
        padding: 0.5rem 0.65rem;
    }

    .mm-stats { grid-template-columns: 1fr; gap: 0.3rem; }
    .mm-stat  { padding: 0.35rem 0.5rem; }
    .mm-stat-icon  { font-size: 0.85rem; }
    .mm-stat-label { font-size: 0.68rem; }
    .mm-stat-val   { font-size: 0.82rem; }

    .mm-xp-header { flex-wrap: wrap; gap: 0.25rem; margin-bottom: 0.3rem; }
    .mm-level-badge { font-size: 0.68rem; padding: 0.1rem 0.5rem; }
    .mm-xp-text   { font-size: 0.66rem; }

    .mm-skill { padding: 0.5rem 0.65rem; }
    .mm-skill-name   { font-size: 0.82rem; }
    .mm-skill-effect { font-size: 0.72rem; }

    .mm-evolve-block { padding: 0.6rem 0.75rem; }
    .mm-evolve-block .evolve-title { font-size: 0.82rem; }
    .mm-evolve-block .evolve-desc,
    .mm-evolve-block .evolve-missing { font-size: 0.72rem; }
}

/* Rename form */
.rename-field { display: flex; gap: 0.4rem; align-items: center; }
.rename-input {
    flex: 1; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.15);
    border-radius: 8px; padding: 0.45rem 0.75rem; color: inherit; font-size: 0.88rem;
}
.rename-input:focus { outline: none; border-color: var(--accent); }
.rename-hint { font-size: 0.72rem; color: var(--text-muted); margin: 0.3rem 0 0; }

/* ═══════════════════════════════════════════════════════
   FICHE MONSTRE JOUEUR (monster/detail.html.twig)
═══════════════════════════════════════════════════════ */

.md-back {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 0 .5rem;
}
.md-back__rubis {
  font-size: .82rem;
  color: var(--accent);
  font-weight: 600;
  background: rgba(245,200,66,.08);
  border: 1px solid rgba(245,200,66,.2);
  border-radius: 20px;
  padding: .2rem .75rem;
}

.md-layout {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 2rem;
  padding: 1.5rem 0 3rem;
  align-items: start;
}

/* Colonne gauche */
.md-portrait {
  background: rgba(10,8,6,.55);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  min-height: 220px;
  transition: box-shadow .3s;
  backdrop-filter: blur(6px);
}
.md-portrait__img { max-height: 180px; max-width: 100%; object-fit: contain; filter: drop-shadow(0 4px 16px rgba(0,0,0,.5)); }
.md-portrait__placeholder { font-size: 3rem; color: var(--text-muted); }

.md-identity { margin: 1rem 0; }
.md-name { font-size: 1.4rem; font-weight: 700; color: #f5e8c0; margin: 0 0 .2rem; }
.md-base-name { font-size: .8rem; color: var(--text-muted); margin: 0 0 .5rem; font-style: italic; }
.md-badges { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .5rem; }

.element-pill {
  font-size: .72rem;
  padding: .15rem .55rem;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,.15);
  background: rgba(255,255,255,.05);
}
.element-pill--feu    { color: #ff8c5a; border-color: rgba(255,90,50,.35); }
.element-pill--eau    { color: #5ab8ff; border-color: rgba(60,160,255,.35); }
.element-pill--terre  { color: #8cc85a; border-color: rgba(140,200,80,.35); }
.element-pill--astral { color: #c07fff; border-color: rgba(192,127,255,.35); }
.element-pill--ombre  { color: #9080cc; border-color: rgba(100,80,180,.35); }
.element-pill--lumiere{ color: #f5e870; border-color: rgba(245,232,112,.35); }

.md-classe-badge {
  font-size: .72rem;
  padding: .15rem .55rem;
  border-radius: 10px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.12);
  color: #d0c8b0;
}

/* Sections */
.md-section {
  background: rgba(10,8,6,.55);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 14px;
  padding: 1.1rem 1.25rem;
  margin-bottom: 1.25rem;
  backdrop-filter: blur(6px);
}
.md-section__title {
  font-size: .9rem;
  font-weight: 700;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin: 0 0 .9rem;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.md-section__badge {
  font-size: .65rem;
  padding: .1rem .45rem;
  border-radius: 99px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}
.md-section__badge--gold { background: rgba(245,200,66,.2); color: var(--accent); border: 1px solid rgba(245,200,66,.3); }

/* Niveau + XP */
.md-level-row { display: flex; align-items: center; gap: .75rem; margin-bottom: .4rem; }
.md-xp-text { font-size: .78rem; color: var(--text-muted); }
.md-xp-bar {
  height: 6px;
  background: rgba(255,255,255,.08);
  border-radius: 99px;
  overflow: hidden;
  margin-bottom: .9rem;
}
.md-xp-bar__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), #ffda6a);
  border-radius: 99px;
  transition: width 1.1s ease-out;
}

/* Stats */
.md-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: .5rem;
}
.md-stat {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
  font-size: .85rem;
  background: rgba(255,255,255,.03);
  border-radius: 8px;
  padding: .4rem .6rem;
}
.md-stat__icon { font-size: .9rem; }
.md-stat__label { font-size: .72rem; color: var(--text-muted); flex: 1; }
.md-stat__val { font-weight: 700; color: #f5e8c0; }
.md-stat__base { font-size: .62rem; color: var(--text-muted); margin-left: .15rem; }
.md-stat__equip-bonus {
    font-size: .6rem; font-weight: 800; color: #6edb8a;
    background: rgba(80,200,120,.12); border: 1px solid rgba(80,200,120,.3);
    border-radius: 4px; padding: .05rem .3rem; margin-left: .2rem;
}

/* Compétences */
.md-skills { display: flex; flex-direction: column; gap: .6rem; }
.md-skill {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
  background: rgba(255,255,255,.03);
  border-radius: 10px;
  padding: .55rem .8rem;
  border-left: 3px solid rgba(255,255,255,.1);
}
.md-skill__name { font-weight: 700; font-size: .88rem; color: #f5e8c0; flex: 1; min-width: 120px; }
.md-skill__badge {
  font-size: .7rem;
  padding: .1rem .5rem;
  border-radius: 8px;
  font-weight: 600;
}
.md-skill__badge--degats  { background: rgba(255,80,60,.15);  color: #ff7060; border: 1px solid rgba(255,80,60,.3); }
.md-skill__badge--soin    { background: rgba(80,200,80,.12);  color: #7ecf7e; border: 1px solid rgba(80,200,80,.3); }
.md-skill__badge--bouclier{ background: rgba(80,150,255,.12); color: #70b0ff; border: 1px solid rgba(80,150,255,.3); }
.md-skill__badge--poison  { background: rgba(160,80,200,.12); color: #c07fff; border: 1px solid rgba(160,80,200,.3); }
.md-skill__val { font-weight: 700; color: var(--accent); font-size: .9rem; }

/* Évolution */
.md-evo-max { font-size: .85rem; color: var(--accent); font-weight: 600; margin: 0; }
.md-hint { font-size: .82rem; color: var(--text-muted); margin: 0; }

.md-evo-target {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: .9rem;
  flex-wrap: wrap;
}
.md-evo-arrow { font-size: 1.2rem; color: var(--accent); }
.md-evo-target__img { height: 48px; object-fit: contain; filter: drop-shadow(0 2px 6px rgba(0,0,0,.4)); }
.md-evo-target__name { font-weight: 700; color: #f5e8c0; }
.md-evo-target__req {
  font-size: .72rem;
  padding: .1rem .45rem;
  border-radius: 8px;
  margin-left: auto;
}
.md-evo-target__req--ok { background: rgba(80,200,80,.12); color: #7ecf7e; border: 1px solid rgba(80,200,80,.25); }
.md-evo-target__req--ko { background: rgba(255,80,60,.1);  color: #f08080; border: 1px solid rgba(255,80,60,.25); }

.md-rune-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: .8rem;
  color: var(--text-muted);
  margin-bottom: .65rem;
}
.md-rune-stock { font-weight: 600; color: #f5e8c0; }
.md-rune-stock--empty { color: #f08080; }

.md-rune-slots {
  display: flex;
  gap: .75rem;
  margin-bottom: .8rem;
}
.md-rune-slot {
  flex: 1;
  height: 64px;
  border-radius: 12px;
  background: rgba(255,255,255,.04);
  border: 2px dashed rgba(255,255,255,.14);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  transition: all .25s;
}
.md-rune-slot--filled {
  background: rgba(245,200,66,.08);
  border: 2px solid rgba(245,200,66,.4);
  box-shadow: 0 0 14px rgba(245,200,66,.12);
}
.md-rune-slot--locked.md-rune-slot--filled {
  border-color: rgba(80,200,80,.5);
  background: rgba(80,200,80,.07);
  box-shadow: 0 0 14px rgba(80,200,80,.1);
}
.md-rune-slot__icon--empty { opacity: .3; }

.md-rune-actions {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;
}
.md-rune-actions form { flex: 1; }
.md-rune-actions form .btn,
.md-rune-actions form button { width: 100%; justify-content: center; }

.btn-evolve {
  width: 100%;
  padding: .65rem 1rem;
  background: linear-gradient(135deg, rgba(80,200,80,.2), rgba(60,180,60,.15));
  border: 1px solid rgba(80,200,80,.5);
  color: #7ecf7e;
  border-radius: 10px;
  font-weight: 700;
  font-size: .9rem;
  cursor: pointer;
  letter-spacing: .03em;
  transition: all .2s;
  animation: evolve-bounce 1.8s ease-in-out infinite;
}
.btn-evolve:hover { background: rgba(80,200,80,.25); box-shadow: 0 0 18px rgba(80,200,80,.2); }

/* Ici vivait une seconde définition de `.btn-danger`, en rouge translucide sur
   texte rose pâle. Écrite au milieu de la fiche monstre, elle s'appliquait
   partout — et comme elle venait six mille neuf cents lignes après la première,
   c'est elle qui gagnait. Tous les boutons « Supprimer » de l'administration
   rendaient donc plus discrets que ce que la charte imprime.
   Retirée : le rouge plein de la ligne 622 est celui du document 4, et une
   action irréversible n'a pas à s'annoncer à voix basse. */

/* Équipement — slots visuels */
.md-equip-visual {
  display: flex;
  gap: .75rem;
  margin-bottom: 1rem;
}
.md-equip-visual-slot {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .4rem;
  background: rgba(255,255,255,.04);
  border: 2px dashed rgba(255,255,255,.14);
  border-radius: 12px;
  padding: .75rem .5rem;
  transition: all .25s;
  text-align: center;
  min-height: 110px;
  justify-content: center;
}
.md-equip-visual-slot--filled {
  background: rgba(160,130,255,.08);
  border: 2px solid rgba(160,130,255,.4);
  box-shadow: 0 0 14px rgba(160,130,255,.1);
}
.md-equip-visual-slot__img-wrap {
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: rgba(255,255,255,.05);
  overflow: hidden;
}
.md-equip-visual-slot__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.md-equip-visual-slot__letter {
  font-size: 1.4rem;
  font-weight: 700;
  color: #c09fff;
}
.md-equip-visual-slot__empty {
  font-size: 1.4rem;
  opacity: .25;
}
.md-equip-visual-slot__label {
  font-size: .75rem;
  font-weight: 600;
  color: #f5e8c0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.md-equip-visual-slot__bonus {
  font-size: .67rem;
  color: #7ecf7e;
}

/* Formulaires d'équipement */
.md-equip-forms { display: flex; flex-direction: column; gap: .55rem; }
.md-equip-form {
  display: flex;
  gap: .5rem;
  align-items: center;
}
.md-equip-form__label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-muted);
  white-space: nowrap;
  min-width: 36px;
}
.md-equip-select {
  flex: 1;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 8px;
  padding: .4rem .65rem;
  color: inherit;
  font-size: .82rem;
}
.md-equip-select:focus { outline: none; border-color: var(--accent); }

/* Responsive */
@media (max-width: 768px) {
  .md-layout { grid-template-columns: 1fr; }
  .md-stats { grid-template-columns: repeat(2,1fr); }
}
/* Très petit écran (≤ 400px) : tout passe sur une colonne pour éviter le débordement */
@media (max-width: 400px) {
  .md-stats { grid-template-columns: 1fr; }
  .md-skill__name { min-width: 0; }
}

/* ═══════════════════════════════════════════════════════
   ADMIN — Grille compétences
═══════════════════════════════════════════════════════ */
.admin-competences-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-bottom: .5rem;
}
.admin-competence-block {
  background: rgba(255,255,255,.03);
  border: 1px solid rgba(255,255,255,.07);
  border-radius: 10px;
  padding: .75rem;
}
.admin-competence-block__label {
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--accent);
  margin-bottom: .6rem;
}
@media (max-width: 640px) {
  .admin-competences-grid { grid-template-columns: 1fr; }
}

/* ── Lien d'évitement ────────────────────────────────────────────────────────
   Il doit être atteignable au clavier sans être visible à la souris. La règle
   ne l'écarte donc pas avec `display:none` — un élément ainsi masqué ne reçoit
   jamais le focus, et le lien ne servirait à personne. Il est sorti de l'écran
   et revient à sa place dès qu'on tabule dessus.                             */
.skip-link {
    position: absolute;
    left: 0;
    top: -4rem;
    z-index: 10000;
    padding: 0.75rem 1.25rem;
    background: var(--accent, #7c5cfc);
    color: #fff;
    font-weight: 600;
    text-decoration: none;
    border-radius: 0 0 0.4rem 0;
    transition: top 0.15s ease;
}

.skip-link:focus {
    top: 0;
    outline: 3px solid #fff;
    outline-offset: -3px;
}

/* La cible du lien porte `tabindex="-1"` pour pouvoir recevoir le focus ;
   sans cette règle, le navigateur l'entourerait d'un contour permanent. */
main:focus {
    outline: none;
}

/* ══════════════════════════════════════════════════════════
   PASTILLES, TABLEAU DE BORD ET VITRINE DU BESTIAIRE
══════════════════════════════════════════════════════════ */

:root {
  /* La couleur du diamant, seconde monnaie annoncée pour la V3.
     Froide, à l'écart de l'or des rubis et du violet des commandes : trois
     familles, trois rôles — ce qu'on gagne en jouant, ce qu'on achètera, ce
     qu'on manœuvre. Elle est déclarée ici, avec l'interface qui l'annonce, et
     non dans `GameConstants` : un montant se déclare en PHP le jour où il se
     paie, une couleur n'a jamais rien payé. */
  --diamant:        #7fd4f5;
  --diamant-dim:    rgba(127, 212, 245, 0.10);
  --diamant-border: rgba(127, 212, 245, 0.32);
}

/* ── La pastille ─────────────────────────────────────────────────────────────
   Un jeton qui dit une valeur et ce qu'elle vaut : un gain, un état, une
   échéance. Le site en avait déjà cinq dessins — `.beast-card__level`,
   `.beast-rarity`, `.creature-tag`, `.section-count`, `.creature-card__price` —
   qui disent tous la même chose de la même façon : petit, cerclé, arrondi. Ce
   composant-ci ne les remplace pas, il donne un nom à ce qu'ils ont en commun,
   pour les deux emplois qui arrivent ensemble : les gains d'un jeu et ce qui
   n'est pas encore ouvert.

   Elle est petite, et délibérément : une pastille est une mention en marge de
   ce qu'elle accompagne — le montant d'un gain sous un titre de jeu, une
   échéance à côté d'un bouton. Grossie, elle se met à peser autant que le
   contenu et la carte ne se lit plus. 0,62 rem est le plancher retenu, celui
   qu'emploient déjà les mentions en capitales du site. */
.pastille {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--border-card);
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--text-muted);
  background: rgba(0, 0, 0, 0.25);
  white-space: nowrap;
}

/* Le rubis : ce qui se gagne aujourd'hui, en jouant. C'est le gain réel, il
   porte donc la couleur de l'accent — celle de tout ce que le site donne. */
.pastille--rubis {
  color: var(--accent);
  border-color: var(--accent-border);
  background: var(--accent-dim);
}

/* Le diamant : le même emplacement, préparé. Il se voit — sinon il n'annoncerait
   rien — mais il ne brille pas comme le rubis : pas de fond plein, pas de
   bordure franche. Une promesse n'a pas le même poids qu'un gain. */
.pastille--diamant {
  color: var(--diamant);
  border-color: var(--diamant-border);
  background: var(--diamant-dim);
}

/* La mention qui suit le libellé, dans la pastille : « Prochainement ». En
   capitales et détachée par un filet, pour qu'on la lise comme une échéance et
   non comme une quantité. Elle n'est jamais seule : c'est toujours « Diamants —
   Prochainement », jamais « Prochainement » tout court, qui ne dirait pas quoi. */
.pastille__note {
  padding-left: 0.35rem;
  border-left: 1px solid currentColor;
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.85;
}

/* Ce qui n'ouvre rien encore, et le dit seul — la tuile du journal des quêtes.
   Sourde exprès : elle occupe sa place dans la rangée sans appeler le geste,
   puisqu'il n'y a pas de geste à faire. */
.pastille--a-venir {
  color: var(--text-muted);
  border-color: var(--border-card);
  background: var(--glass-bg);
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── La porte du bestiaire ───────────────────────────────────────────────────
   La mascotte du bandeau, devenue le chemin vers la collection. Le halo, les
   étincelles et la lueur au survol viennent de `.hero__mascot-link`, que le lien
   de l'accueil porte déjà : rien n'est redessiné ici. Ne restent que la
   disposition — l'appel sous l'image — et ce qu'un bandeau en ligne impose.

   Le libellé n'est pas un ornement. Une image qui brille dit qu'on peut
   cliquer ; elle ne dit jamais vers où, et personne ne devine « bestiaire » dans
   un panda. */
.page-hero__porte {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  text-decoration: none;
}
.page-hero__porte .page-hero__mascot { margin: 0; }

/* L'appel et le compte tiennent sur une ligne, et se replient l'un sous l'autre
   plutôt que d'étirer le bandeau sur un écran étroit. */
.page-hero__porte-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}

.page-hero__porte-appel {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.45);
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--accent);
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.page-hero__porte:hover .page-hero__porte-appel {
  color: var(--accent-hover);
  border-color: var(--accent);
  background: rgba(0, 0, 0, 0.6);
}
/* ── Le compte de créatures, sur la porte ────────────────────────────────────
   L'appel est une pastille creuse, celle-ci est pleine : deux pastilles du même
   dessin côte à côte se liraient comme deux morceaux d'un même libellé, alors
   que l'une nomme la destination et l'autre dit ce qu'on y trouvera. L'or plein
   est d'ailleurs celui des commandes principales du site — il attire l'œil sur
   le chiffre, qui est la nouvelle du bandeau.

   Un cercle tant que le nombre est court, une pilule au-delà : `min-width` égale
   la hauteur, et le rembourrage horizontal fait le reste passé trois chiffres. */
.page-hero__porte-compte {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 0.5rem;
  border: 1px solid rgba(255, 216, 140, 0.55);
  border-radius: 999px;
  background: linear-gradient(160deg, #f5b331 0%, var(--accent) 55%, #c4760a 100%);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 236, 190, 0.5);
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 900;
  line-height: 1;
  color: #120a02;
  transition: box-shadow 0.2s, transform 0.2s;
}
.page-hero__porte:hover .page-hero__porte-compte {
  transform: translateY(-1px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 18px rgba(232, 150, 16, 0.4),
              inset 0 1px 0 rgba(255, 240, 205, 0.65);
}

/* Le contour du focus se pose sur la ligne et non sur le lien entier : la boîte
   du lien inclut les étincelles, qui débordent de l'image — le cadre aurait
   flotté à trois centimètres de ce qu'il désigne. Il entoure l'appel ET le
   compte, qui sont ce que la porte donne à lire. */
.page-hero__porte:focus-visible {
  outline: none;
}
.page-hero__porte:focus-visible .page-hero__porte-ligne {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 999px;
}

/* Le retour au tableau de bord, dans le bandeau du bestiaire. `.btn-back` en
   donne le dessin ; il ne manque que sa place sous le sous-titre, et un ton plus
   clair que le brun de `--text-muted`, qui se perdait sur le décor du bandeau. */
.page-hero__retour {
  display: inline-block;
  margin-top: 0.6rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text);
  transition: color 0.2s;
}
.page-hero__retour:hover { color: var(--accent); }
.page-hero__retour:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* ── La rangée de commandes du bandeau ───────────────────────────────────────
   L'engrenage des réglages et le journal des quêtes, sur une ligne. Elle se
   replie sur téléphone plutôt que de pousser le bandeau en largeur. */
/* Les deux commandes s'alignent par le haut, et non par leur milieu : le
   journal porte une mention sous son icône, et un alignement centré aurait
   remonté l'engrenage d'une demi-ligne pour compenser un texte qui n'est pas le
   sien. */
.page-hero__actions {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.6rem;
}
/* ── Le journal des quêtes ───────────────────────────────────────────────────
   Une pastille ronde comme sa voisine, et sa mention dessous. Le groupe est une
   colonne : la mention se range sous l'icône sans pousser l'engrenage vers la
   droite, et les deux pastilles restent alignées sur la même ligne.

   Le bouton est `disabled`, et ce n'est pas une commande en panne : c'est une
   commande qui existe et n'est pas encore ouverte — le navigateur l'annonce
   ainsi sans qu'on ait rien à écrire. Son contour est tireté : ce qui n'est pas
   encore construit ne se dessine pas d'un trait plein. */
.page-hero__quete-groupe {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}
.page-hero__quete {
  border-style: dashed;
  border-color: var(--accent-border);
  /* Le curseur dit ce que l'attribut dit déjà : rien à cliquer ici. L'icône
     reste pleinement lisible — une annonce à demi effacée n'annonce rien. */
  cursor: default;
}
/* Un bouton désactivé ne reçoit pas le survol partout de la même façon, et le
   doigt n'en a aucun : la pastille ne change donc pas d'aspect au passage. */
.page-hero__quete:hover {
  color: var(--accent);
  border-color: var(--accent-border);
}

/* ── La bascule d'affichage ──────────────────────────────────────────────────
   Deux liens rangés dans la barre de filtres, poussés à droite : le filtrage est
   à gauche, l'affichage à droite, et les deux ne se disputent pas la même
   rangée. Sur téléphone la barre se replie et la bascule passe à la ligne,
   entière — jamais un demi-bouton en bout de ligne. */
/* La barre de filtres accueille maintenant la bascule d'affichage à sa droite :
   trois éléments sur une rangée qui n'en attendait que deux. Elle se replie
   plutôt que de déborder — sur téléphone, « Filtrer », « Réinitialiser » et la
   bascule tiennent sur deux lignes. */
.bestiary-filter-bar { flex-wrap: wrap; }

/* Les nouvelles de l'accueil passent dans une piste de carrousel : la carte
   occupe toute la hauteur de sa case, sans quoi trois phrases et dix phrases
   donnent deux cartes de hauteurs différentes et la rangée flotte.

   Le bestiaire avait la même règle, pour la même raison : sa vitrine était une
   piste, elle aussi. Elle est devenue une grille, et le carrousel des créatures
   n'existe plus — la grille montre la collection entière, et la scène plein
   écran fait mieux ce que le carrousel essayait de faire. */
.carrousel__element > .news-item {
  width: 100%;
  height: 100%;
}

/* ── Les deux gestes d'une carte de créature ─────────────────────────────────
   « Aperçu » ouvre la fenêtre de consultation rapide — favori, surnom,
   évolution, lignée ; « Gérer » mène à la fiche, où se font l'équipement et les
   runes. Deux gestes distincts, donc deux commandes : la carte n'est plus
   elle-même un bouton, et une commande ne contient plus une commande.

   Ils sont poussés en bas de la carte par `margin-top: auto` — quelle que soit
   la longueur du nom ou le nombre d'éléments au-dessus, les boutons de deux
   cartes voisines restent sur la même ligne. */
.beast-card__actions {
  display: flex;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.25rem;
}
.beast-card__actions .btn {
  flex: 1;
  justify-content: center;
  /* Le doigt avant la densité : `.btn-sm` fait 1,9 rem de haut, sous la cible
     de 44 px que vise le reste du site sur les commandes qu'on touche. */
  min-height: 2.5rem;
}

/* ── Mouvement réduit ────────────────────────────────────────────────────────
   Les étincelles de la mascotte tournent en boucle, sans fin et sans raison
   fonctionnelle : ce sont exactement celles que ce réglage vise. Le lien reste
   entier — halo, lueur au survol, libellé —, il cesse seulement de scintiller.

   La règle vise `.mascot-spark` partout, donc aussi l'accueil, qui les a
   posées le premier et les animait sans garde. */
@media (prefers-reduced-motion: reduce) {
  .mascot-spark { display: none; }
  .page-hero__porte-appel { transition: none; }
  /* La pastille garde son or et sa lueur au survol, elle cesse seulement de
     monter : c'est le déplacement qui gêne, pas la couleur. */
  .page-hero__porte-compte { transition: box-shadow 0.2s; }
  .page-hero__porte:hover .page-hero__porte-compte { transform: none; }
}

/* ════════════════════════════════════════════════════════════════════════════
   LES SCEAUX — les commandes de la scène
   ════════════════════════════════════════════════════════════════════════════
   `.btn` habille tout le site et n'a pas à changer : c'est une commande de
   formulaire, discrète, faite pour être répétée dans une page dense. Sur la
   scène il n'y en a que deux, devant une illustration qui prend l'écran — un
   bouton de 1,9 rem à angle arrondi s'y perd.

   Le sceau reprend les couleurs de la charte et rien d'autre : l'or de l'accent,
   la nuit du fond, la Cinzel des titres. Ce qui change est la matière — un
   dégradé qui descend comme du métal frappé, un filet gravé à l'intérieur du
   bord, un liseré clair en haut et sombre en bas pour que la face paraisse
   biseautée. Il ne s'enfonce pas au survol : il monte d'un cheveu et s'éclaire,
   et c'est au clic qu'il redescend.

   Deux rangs seulement, comme partout ailleurs : l'or pour le geste qu'on
   attend, la nuit pour l'autre. */
.sceau {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  min-height: 3rem;
  min-width: 9.5rem;
  padding: 0.8rem 1.75rem;
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 0.18s ease, box-shadow 0.22s ease,
              background 0.22s ease, border-color 0.22s ease, color 0.22s ease;
}

/* Le filet gravé. Il prend la couleur du texte : sombre sur l'or, clair sur la
   nuit — un seul jeu de règles, deux rendus justes. */
.sceau::before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px solid currentColor;
  border-radius: 3px;
  opacity: 0.16;
  pointer-events: none;
}

/* Le signe devant le libellé n'est pas décoratif au hasard : le marteau dit
   l'atelier, l'étoile dit ce qu'on vient regarder. Il s'anime seul au survol —
   le bouton, lui, ne bouge presque pas. */
.sceau__signe {
  font-size: 1rem;
  letter-spacing: 0;
  opacity: 0.85;
  transition: transform 0.25s ease;
}
.sceau:hover .sceau__signe,
.sceau:focus-visible .sceau__signe { transform: rotate(-12deg) scale(1.15); }

.sceau:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.sceau:active { transform: translateY(1px); }

.sceau--or {
  background: linear-gradient(160deg, #f5b331 0%, var(--accent) 52%, #c4760a 100%);
  color: #120a02;
  border-color: rgba(255, 216, 140, 0.55);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45),
              inset 0 1px 0 rgba(255, 236, 190, 0.55),
              inset 0 -1px 0 rgba(90, 50, 0, 0.35);
}
.sceau--or:hover {
  color: #120a02;
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.5),
              0 0 26px rgba(232, 150, 16, 0.38),
              inset 0 1px 0 rgba(255, 240, 205, 0.7);
}

.sceau--nuit {
  background: rgba(10, 6, 2, 0.72);
  color: var(--text);
  border-color: rgba(232, 150, 16, 0.3);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4),
              inset 0 1px 0 rgba(255, 224, 180, 0.06);
}
.sceau--nuit:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(232, 150, 16, 0.1);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), 0 0 18px rgba(232, 150, 16, 0.16);
}

/* ════════════════════════════════════════════════════════════════════════════
   LA SCÈNE — le bestiaire en plein écran
   ════════════════════════════════════════════════════════════════════════════
   Une créature par écran, et rien autour d'elle que son nom, son rang et ses
   deux commandes. Tout le reste est aux bords : les filtres derrière un ⚙, la
   sortie derrière un ✕, le parcours dans les chevrons et la rangée de points.

   La barre de navigation reste : c'est elle qui porte les rubis, l'inventaire
   et le réglage du son. La scène occupe ce qu'elle laisse. */
.scene-hote { padding-top: var(--nav-h); }

.scene {
  position: relative;
  height: calc(100dvh - var(--nav-h));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(232, 150, 16, 0.07), transparent 62%),
    var(--bg);
}

/* ── Les commandes de bord ─────────────────────────────────────────────────
   La barre ne prend pas de place dans la colonne : elle flotte au-dessus de
   l'illustration. Elle ne capte donc pas le clic sur toute sa largeur — seuls
   ses boutons le font, sans quoi une bande invisible barrerait le haut de
   l'image. */
.scene__bord {
  position: absolute;
  top: 1rem; left: 1rem; right: 1rem;
  z-index: 5;
  display: flex;
  justify-content: space-between;
  pointer-events: none;
}
.scene__bord > * { pointer-events: auto; }

/* Filtrer et repasser en grille vont ensemble : elles agissent toutes deux sur
   ce qu'on regarde. La sortie, elle, reste seule de l'autre côté. */
.scene__bord-groupe {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.scene-rond {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: rgba(10, 6, 2, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(232, 150, 16, 0.28);
  color: var(--text-muted);
  font-size: 1rem;
  cursor: pointer;
  text-decoration: none;
  transition: color 0.18s, border-color 0.18s, background 0.18s, transform 0.18s;
}
.scene-rond:hover,
.scene-rond:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(232, 150, 16, 0.12);
  transform: scale(1.07);
}
.scene-rond:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.scene-rond--sortie:hover,
.scene-rond--sortie:focus-visible { color: #f08080; border-color: rgba(240, 128, 128, 0.6); background: rgba(240, 128, 128, 0.1); }

/* La pastille qui dit qu'un filtre est posé : sans elle, le ⚙ fermé laisse
   croire qu'on regarde toute la collection. */
.scene-rond--marque::after {
  content: '';
  position: absolute;
  top: 0.3rem; right: 0.3rem;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px rgba(232, 150, 16, 0.9);
}

/* ── Le tiroir des filtres ─────────────────────────────────────────────────
   Le même panneau que dans les autres vitrines, où il se déplie en hauteur par
   `grid-template-rows`. Ici il glisse depuis le bord : la scène n'a pas de
   hauteur à lui donner. `visibility` plutôt qu'une opacité seule — fermé, il
   sort aussi du parcours de tabulation. */
.scene__tiroir {
  display: block;
  grid-template-rows: none;
  position: absolute;
  top: 4.5rem;
  left: 1rem;
  z-index: 6;
  width: min(23rem, calc(100% - 2rem));
  max-height: calc(100% - 7.5rem);
  overflow-y: auto;
  padding: 1.1rem;
  background: rgba(12, 8, 5, 0.95);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(232, 150, 16, 0.22);
  border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  transform: translateX(-12px);
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0.22s;
}
.scene__tiroir.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  margin-top: 0;
}
.scene__tiroir > .bestiary-filters__row { overflow: visible; padding-bottom: 0; }
.scene__tiroir-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

/* ── La piste ──────────────────────────────────────────────────────────────
   Un conteneur à défilement horizontal avec des points d'arrêt : c'est le
   navigateur qui fait passer d'une créature à la suivante, au doigt comme à la
   molette. `bestiaire-scene.js` n'ajoute que les chevrons et le point actif. */
.scene__piste {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
}
.scene__piste:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; border-radius: 12px; }

.scene__case {
  flex: 0 0 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 3.5rem 4.25rem 0.5rem;
}

/* ── La carte de la scène ──────────────────────────────────────────────────
   C'est la `.beast-card` des autres vitrines — mêmes données, lues par la même
   fenêtre de détail —, mais elle n'est plus une carte : ni fond, ni bord, ni
   ombre. L'illustration est la surface. Elle n'est pas cliquable non plus :
   sur une image qu'on fait défiler au doigt, le clic ne doit rien déclencher. */
.beast-card--scene {
  width: 100%;
  max-width: 44rem;
  height: 100%;
  padding: 0;
  gap: 0.9rem;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  cursor: default;
}
.beast-card--scene::before,
.beast-card--scene.beast-card--fav::after { content: none; }
.beast-card--scene:hover,
.beast-card--scene:focus-visible {
  transform: none;
  border-color: transparent;
  box-shadow: none;
}

.scene__portrait {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.scene__image {
  max-height: 100%;
  max-width: min(100%, 34rem);
  width: auto;
  object-fit: contain;
  filter: drop-shadow(0 18px 45px rgba(0, 0, 0, 0.7));
  animation: scene-flotte 6s ease-in-out infinite;
}
@keyframes scene-flotte {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}

/* Le nom reprend l'or des titres. `.beast-card__name` le coupait aux points de
   suspension parce qu'une carte de vitrine a une largeur imposée ; ici il a la
   page, et un nom long passe à la ligne plutôt que de se faire tronquer. */
.scene__nom {
  margin: 0;
  flex: none;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 5vw, 2.6rem);
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.1;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  background: linear-gradient(135deg, #f5c842, #e8963c);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.scene__rang {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: -0.45rem 0 0;
}
.scene__niveau {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.scene__separateur { color: var(--text-dim); }

/* ── Les éléments de la créature ─────────────────────────────────────────────
   Leur propre ligne, sous le rang : le niveau et la rareté disent ce que vaut
   la créature, les éléments ce qu'elle est. Mêlés sur une ligne, cela aurait
   fait trois sortes de pastilles séparées par des points.

   La couleur vient de `.duel-element--*`, partagée avec les cartes de la grille
   et la fenêtre de détail : un élément a une teinte, et elle est la même
   partout. Ne restent ici que la taille et l'espacement — la scène peut se
   permettre d'être lue de plus loin qu'une carte de vitrine. */
.scene__elements {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin: -0.25rem 0 0;
}
.scene__element {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.scene__gestes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  padding-bottom: 0.25rem;
}

/* ── Les chevrons ──────────────────────────────────────────────────────────
   Posés sur les bords de la scène, au milieu de la hauteur. Ils partent
   `hidden` dans le gabarit — sans JavaScript ils ne commandent rien — et le
   module les masque de nouveau au bout de la collection. */
.scene-fleche {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 6, 2, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(232, 150, 16, 0.25);
  color: var(--accent);
  font-size: 1.9rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.18s, transform 0.18s, border-color 0.18s;
}
.scene-fleche[hidden] { display: none; }
.scene-fleche--prec { left: 0.75rem; }
.scene-fleche--suiv { right: 0.75rem; }
.scene-fleche:hover {
  background: rgba(232, 150, 16, 0.18);
  border-color: var(--accent);
  transform: translateY(-50%) scale(1.09);
}
.scene-fleche:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ── La rangée de points ───────────────────────────────────────────────────
   Des ancres, pas des boutons : sans JavaScript, cliquer amène bien la créature
   à l'écran. Elles se replient sur plusieurs rangs quand la collection est
   longue, dans une bande qui défile plutôt qu'une bande qui grandit. */
.scene-points {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  max-height: 4.25rem;
  overflow-y: auto;
  padding: 0.9rem 1.25rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
}
.scene-point {
  display: block;
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(232, 150, 16, 0.25);
  transition: background 0.2s, transform 0.2s, box-shadow 0.2s;
}
.scene-point:hover { background: rgba(232, 150, 16, 0.6); }
.scene-point:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.scene-point--actif {
  background: var(--accent);
  transform: scale(1.45);
  box-shadow: 0 0 10px rgba(232, 150, 16, 0.6);
}

.scene__vide {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  padding: 2rem 1.25rem;
  color: var(--text-muted);
  text-align: center;
}

/* ── La scène sur petit écran ──────────────────────────────────────────────
   Les chevrons rentrent dans les bords et rétrécissent ; la case leur rend la
   place qu'ils prenaient. Les deux sceaux s'étalent sur toute la largeur : à
   360 px, deux commandes côte à côte tombent sous la cible de 44 px. */
@media (max-width: 640px) {
  .scene__case { padding: 3.25rem 0.75rem 0.5rem; }
  .scene-fleche { width: 2.5rem; height: 2.5rem; font-size: 1.5rem; }
  .scene-fleche--prec { left: 0.35rem; }
  .scene-fleche--suiv { right: 0.35rem; }
  .scene__gestes { width: 100%; }
  .scene__gestes .sceau { flex: 1 1 10rem; min-width: 0; padding: 0.8rem 1rem; letter-spacing: 0.1em; }
  .scene__tiroir { top: 4.25rem; left: 0.75rem; width: calc(100% - 1.5rem); }
  .scene-points { gap: 0.4rem; max-height: 3.5rem; }
}

/* ── Mouvement réduit ──────────────────────────────────────────────────────
   Le flottement de la créature tourne en boucle, sans fin et sans raison
   fonctionnelle : c'est exactement ce que ce réglage vise. Les sceaux gardent
   leurs couleurs et leur relief, ils cessent seulement de monter et de
   pivoter ; le tiroir apparaît au lieu de glisser. Rien ne disparaît. */
@media (prefers-reduced-motion: reduce) {
  .scene__image { animation: none; }
  .sceau, .sceau__signe, .scene-rond, .scene-fleche, .scene-point { transition: none; }
  .sceau:hover, .sceau:active { transform: none; }
  .sceau:hover .sceau__signe, .sceau:focus-visible .sceau__signe { transform: none; }
  .scene-rond:hover, .scene-rond:focus-visible { transform: none; }
  .scene-fleche:hover { transform: translateY(-50%); }
  .scene__tiroir { transition: opacity 0.01ms, visibility 0.01ms; transform: none; }
}
