/* ==========================================================================
   Nuit sur Thiercelieux — feuille de style
   1. Jetons de design
   2. Décor (image de fond, torches, lune)
   3. Coquille de l'application
   4. Composants
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Jetons de design
   -------------------------------------------------------------------------- */
:root{
  --ink:#070a12;
  --dusk:#111a2b;
  --mist:#a9b8cc;
  --parchment:#f0e8d6;
  --ember:#c8563c;
  --candle:#e0ad4d;
  --moss:#5f8468;

  /* Bois, laiton et sang séché : la palette du plateau, par-dessus le décor. */
  --bark:#241c13;
  --gold:#c9a24a;
  --gold-hi:#f0d79a;
  --gold-lo:#7d5a1c;
  --blood:#c22a20;
  --blood-hi:#e8412c;

  --line:rgba(201,162,74,.22);
  --line-soft:rgba(201,162,74,.13);
  --glass:rgba(10,15,26,.62);

  /* Angles francs : le coin arrondi est la signature d'une interface moderne,
     c'est précisément ce dont on ne veut pas ici. */
  --radius:3px;

  /* Grain de papier / de métal piqué. Généré, donc aucun fichier à charger et
     aucune tuile visible : le bruit fractal se raccorde de lui-même.
     Toujours posé en `background-blend-mode`, jamais seul. */
  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
}
*{box-sizing:border-box;}
html,body{height:100%;}
body{
  margin:0;
  font-family:'Jost',system-ui,-apple-system,'Segoe UI',sans-serif;
  color:var(--parchment);
  background:var(--ink);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* --------------------------------------------------------------------------
   2. Décor

   L'image est peinte par `background-size:cover` sur un élément calé sur le
   viewport : aucune transformation, donc aucun rééchantillonnage au demi-pixel
   (c'est ce qui rendait le fond flou quand le cadre avait une largeur impaire).

   Les lumières, elles, vivent dans un cadre carré centré par transform. Comme
   l'illustration est carrée, `cover` produit exactement la même géométrie que
   `max(100vw,100vh)` : les halos restent calés sur leur source, et le flou de
   sous-pixel n'a aucune importance sur des dégradés diffus.
   -------------------------------------------------------------------------- */
.scene{position:fixed; inset:0; z-index:0; overflow:hidden; pointer-events:none;}
.scene__img{
  position:absolute; inset:0;
  background-image:url("../img/village-1280.jpg");
  background-position:center center;
  background-size:cover;
  background-repeat:no-repeat;
  background-color:#0d1424;
  opacity:0;
  transition:opacity .9s ease;
}
.scene__img.is-loaded{opacity:1;}
/* Écrans larges ou à haute densité : source 2048 px, sinon le fond est
   agrandi par le navigateur et perd tout son piqué. */
@media (min-width:760px), (min-resolution:1.5dppx){
  .scene__img{background-image:url("../img/village-2048.jpg");}
}

/* --- Le jour --------------------------------------------------------------
   Même village, même point de vue, en plein soleil. Posé par-dessus la nuit
   et révélé en fondu : les deux images se recouvrant exactement, le passage
   se lit comme un lever de soleil et non comme un changement d'écran.

   Le fondu est long — la bascule accompagne la phrase du narrateur qui
   annonce l'aube, elle ne la précède pas.
   -------------------------------------------------------------------------- */
.scene__day{
  position:absolute; inset:0;
  background-image:url("../img/village-jour-1280.jpg");
  background-position:center center;
  background-size:cover;
  background-repeat:no-repeat;
  opacity:0;
  transition:opacity 2.6s ease-in-out;
}
@media (min-width:760px), (min-resolution:1.5dppx){
  .scene__day{background-image:url("../img/village-jour-2048.jpg");}
}
.scene.is-day .scene__day{opacity:1;}

/* En plein jour, le voile nocturne s'allège, les torches s'éteignent et la
   brume se dissipe. Sans ça, on garderait des halos de flamme en plein midi. */
.scene__veil,
.scene__frame,
.scene__fog{transition:opacity 2.6s ease-in-out;}
.scene.is-day .scene__veil{opacity:.28;}
.scene.is-day .scene__frame{opacity:0;}
.scene.is-day .scene__fog{opacity:.35;}

/* Voile d'assombrissement : sous les lumières, pour ne jamais les ternir. */
.scene__veil{
  position:absolute; inset:0;
  background:
    linear-gradient(to bottom, rgba(7,10,18,.62) 0%, rgba(7,10,18,.30) 28%, rgba(7,10,18,.52) 66%, rgba(7,10,18,.86) 100%),
    radial-gradient(ellipse at 50% 45%, rgba(7,10,18,0) 35%, rgba(7,10,18,.55) 100%);
}
.scene__frame{
  position:absolute; top:50%; left:50%;
  width:max(100vw,100vh); height:max(100vw,100vh);
  transform:translate(-50%,-50%);
}
.scene__fog{
  position:absolute; left:0; right:0; bottom:0; height:38%;
  background:linear-gradient(to top, rgba(7,10,18,.92), rgba(7,10,18,0));
}
.scene__filters{position:absolute; width:0; height:0; overflow:hidden;}

/* --- Lune ---------------------------------------------------------------- */
.light{
  position:absolute;
  left:var(--x); top:var(--y);
  width:var(--size); height:var(--size);
  margin-left:calc(var(--size) / -2);
  margin-top:calc(var(--size) / -2);
  border-radius:50%;
  mix-blend-mode:screen;
}
.moon-halo{
  background:radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.60) 0%,
    rgba(223,236,255,.38) 22%,
    rgba(158,190,255,.18) 42%,
    rgba(96,134,224,.08) 62%,
    rgba(40,70,140,0) 80%);
  animation:moonBreath 11s ease-in-out infinite alternate;
}
.moon-core{
  background:radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.42) 0%,
    rgba(244,249,255,.34) 52%,
    rgba(226,238,255,.20) 76%,
    rgba(200,220,255,0) 92%);
  animation:moonBreath 11s ease-in-out infinite alternate-reverse;
}
@keyframes moonBreath{
  0%   {opacity:.82; transform:scale(1);}
  100% {opacity:1;   transform:scale(1.045);}
}

/* --- Torches -------------------------------------------------------------
   Trois calques de forme différente, jamais un simple disque :
     .torch__bloom  panache vertical, bord déchiqueté par un filtre SVG
     .torch__spill  nappe large et basse (la lumière tombe sur le mur/le sol)
     .torch__core   coeur incandescent

   L'intensité et la déformation viennent de scene.js (bruit apériodique), pas
   de @keyframes : une boucle CSS finit toujours par se laisser deviner.
   Seuls le conteneur et le coeur sont animés — les calques filtrés restent
   statiques, pour que le navigateur ne recalcule pas le filtre à chaque image.
   -------------------------------------------------------------------------- */
.torch{
  position:absolute;
  left:var(--x); top:var(--y);
  width:var(--size); height:var(--size);
  margin-left:calc(var(--size) / -2);
  margin-top:calc(var(--size) / -2);
  mix-blend-mode:screen;
  opacity:var(--i,.8);
  /* --tx/--ty sont des nombres nus : exprimés en % de la torche elle-même,
     le tremblement reste proportionnel quelle que soit la taille de l'écran. */
  transform:translate(calc(var(--tx,0) * 1%), calc(var(--ty,0) * 1%)) scale(var(--sc,1));
  will-change:opacity, transform;
}
.torch > *{position:absolute; pointer-events:none;}

.torch__bloom{
  left:-14%; top:-30%; width:128%; height:150%;
  border-radius:50% 50% 46% 54% / 42% 44% 56% 58%;
  background:radial-gradient(ellipse 46% 40% at 50% 44%,
    rgba(255,242,210,.85) 0%,
    rgba(255,196,108,.46) 24%,
    rgba(240,136,44,.22) 46%,
    rgba(176,68,20,.08) 68%,
    rgba(110,38,8,0) 84%);
  filter:url(#flameEdge);
}
.torch__spill{
  left:-24%; top:18%; width:148%; height:96%;
  border-radius:52% 48% 50% 50% / 62% 58% 42% 38%;
  background:radial-gradient(ellipse 50% 38% at 50% 34%,
    rgba(255,206,140,.42) 0%,
    rgba(238,146,60,.24) 30%,
    rgba(170,74,24,.10) 58%,
    rgba(96,34,8,0) 82%);
  filter:url(#flameEdgeSoft);
}
.torch__core{
  left:38%; top:36%; width:24%; height:30%;
  border-radius:50% 50% 46% 54% / 60% 60% 40% 40%;
  background:radial-gradient(ellipse 50% 46% at 50% 46%,
    rgba(255,253,240,.98) 0%,
    rgba(255,214,132,.72) 42%,
    rgba(255,158,56,.20) 74%,
    rgba(255,140,40,0) 100%);
  opacity:var(--ci,.85);
  transform:scale(var(--cs,1));
}

/* Sans mouvement : intensité fixe, aucune boucle rAF (voir scene.js). */
@media (prefers-reduced-motion:reduce){
  .moon-halo,.moon-core{animation:none;}
  .torch{opacity:.8; transform:none;}
  .torch__core{opacity:.85; transform:none;}
}

/* --------------------------------------------------------------------------
   3. Coquille de l'application
   -------------------------------------------------------------------------- */
.app{
  position:relative; z-index:2;
  max-width:460px; margin:0 auto;
  padding:28px 16px calc(48px + env(safe-area-inset-bottom));
  min-height:100vh;
}


/* --- Titres ---------------------------------------------------------------
   Le titre n'est pas une couleur unie : un dégradé de laiton sur lequel le
   grain est mélangé en `overlay`, le tout découpé à la forme des lettres par
   `background-clip:text`. On obtient un métal piqué, terni par endroits, là où
   une couleur plate donnait un texte propre — donc récent.

   Les ombres passent par `filter:drop-shadow`, qui suit l'alpha des glyphes :
   `text-shadow` n'a plus aucune prise sur un texte dont la couleur est
   transparente.
   -------------------------------------------------------------------------- */
.eyebrow{
  font-family:'Cinzel',Georgia,serif; font-weight:500;
  font-size:12.5px; letter-spacing:.34em; text-transform:uppercase;
  color:var(--gold); text-align:center; margin-bottom:10px;
  text-shadow:0 0 16px rgba(201,162,74,.45), 0 1px 6px rgba(0,0,0,.9);
}
.title{
  font-family:'Cinzel',Georgia,serif; font-weight:700;
  font-size:clamp(31px,8.6vw,42px); line-height:1.12;
  text-align:center; letter-spacing:.055em; text-transform:uppercase;
  margin:0 0 8px;
  background-image:var(--grain),
    linear-gradient(177deg,#fff8e8 0%,#ecd6a4 24%,#c9a24a 50%,#8a6320 74%,#e2c78d 100%);
  background-size:180px 180px, 100% 100%;
  background-blend-mode:overlay, normal;
  -webkit-background-clip:text; background-clip:text;
  color:transparent; -webkit-text-fill-color:transparent;
  filter:
    drop-shadow(0 1px 1px rgba(0,0,0,.95))
    drop-shadow(0 6px 16px rgba(0,0,0,.8))
    drop-shadow(0 0 30px rgba(201,162,74,.26));
}
/* Sans découpe du fond par le texte, la couleur transparente rendrait le
   titre invisible : on repasse en doré plein. */
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .title{color:var(--gold-hi); -webkit-text-fill-color:var(--gold-hi);}
}

/* Filet gravé, avec ou sans mention centrale. Sert de séparateur partout :
   sous le titre, et entre les loups et le village. */
.rule{display:flex; align-items:center; gap:12px; margin:0 0 20px;}
.rule::before,.rule::after{
  content:''; flex:1; height:1px;
  background:linear-gradient(90deg, transparent, rgba(201,162,74,.42), transparent);
}
.rule__label{
  font-family:'Cinzel',Georgia,serif; font-size:12.5px; font-weight:500;
  letter-spacing:.3em; text-transform:uppercase; color:var(--gold);
  white-space:nowrap; text-shadow:0 0 14px rgba(201,162,74,.35);
}
.rule__glyph{width:13px; height:13px; fill:var(--gold); opacity:.75; flex:none;}

.sub{text-align:center; color:var(--mist); font-size:14px; margin:0 0 24px; line-height:1.55; text-shadow:0 1px 8px rgba(0,0,0,.8);}
.hint{text-align:center; font-size:12.5px; color:var(--mist); margin:0 0 16px; line-height:1.5;}
/* Centré comme les mentions des filets gravés : c'est ce qui fait que le titre
   d'un panneau et le séparateur qu'il contient se répondent au lieu de tirer
   l'oeil chacun de son côté. */
.label{
  display:block; text-align:center;
  font-family:'Cinzel',Georgia,serif; font-size:14px; font-weight:500;
  text-transform:uppercase; letter-spacing:.24em; color:var(--gold);
  margin:0 0 14px;
}
/* Titre long, avec son décompte en dessous.

   « Habitants de Thiercelieux » fait vingt-cinq caractères, et le `.label`
   ordinaire les écarte de .24em : la ligne dépasse la carte sur tout téléphone,
   et le nombre de joueurs se retrouvait seul sur une seconde ligne, en capitales
   dorées, avec le même poids que le nom du village. Le décompte descend donc
   d'un cran — plus petit, sans empattement, couleur de brume : on lit le titre,
   puis le chiffre, et non l'inverse. */
.label--titre{letter-spacing:.16em; margin-bottom:12px;}
.label__compte{
  display:block; margin-top:5px;
  font-family:'Jost',sans-serif; font-size:11px; font-weight:500;
  letter-spacing:.16em; color:var(--mist);
}

.row{display:flex; align-items:center; justify-content:space-between; gap:10px;}
/* Hors carte, à même l'image : ombre portée obligatoire. */
.error{color:var(--blood-hi); font-size:13px; text-align:center; margin:-2px 0 12px; text-shadow:0 1px 10px rgba(0,0,0,.95);}
.card .error{color:var(--blood-hi); text-shadow:none;}
.footer-note{font-size:11px; color:#8e9bb0; text-align:center; margin-top:22px; line-height:1.7; text-shadow:0 1px 10px rgba(0,0,0,.95);}

/* --- Panneau --------------------------------------------------------------
   Une planche sombre plutôt qu'une vitre : bois teinté, grain mélangé en
   `soft-light`, filet intérieur et équerres de laiton aux quatre coins. Le
   flou d'arrière-plan est conservé — c'est lui qui laisse les torches du décor
   transparaître derrière le panneau.
   -------------------------------------------------------------------------- */
.card{
  position:relative;
  background-image:var(--grain),
    linear-gradient(168deg, rgba(46,35,24,.74) 0%, rgba(20,15,10,.80) 48%, rgba(9,7,5,.86) 100%);
  background-size:180px 180px, 100% 100%;
  background-blend-mode:soft-light, normal;
  -webkit-backdrop-filter:blur(12px) saturate(110%);
  backdrop-filter:blur(12px) saturate(110%);
  border:1px solid var(--line);
  border-radius:var(--radius);
  padding:24px 20px; margin-bottom:16px;
  box-shadow:
    0 20px 46px rgba(0,0,0,.58),
    inset 0 0 70px rgba(0,0,0,.45),
    inset 0 1px 0 rgba(240,215,154,.09);
}
.card::before{
  content:''; position:absolute; inset:6px; pointer-events:none;
  border:1px solid var(--line-soft); border-radius:1px;
}
/* Équerres d'angle : huit barres de laiton, deux par coin. Un ornement dessiné
   en CSS ne demande ni image ni requête, et suit la taille du panneau. */
.card::after{
  content:''; position:absolute; inset:6px; pointer-events:none;
  background-image:
    linear-gradient(var(--gold),var(--gold)), linear-gradient(var(--gold),var(--gold)),
    linear-gradient(var(--gold),var(--gold)), linear-gradient(var(--gold),var(--gold)),
    linear-gradient(var(--gold),var(--gold)), linear-gradient(var(--gold),var(--gold)),
    linear-gradient(var(--gold),var(--gold)), linear-gradient(var(--gold),var(--gold));
  background-repeat:no-repeat;
  background-size:15px 1px, 1px 15px, 15px 1px, 1px 15px,
                  15px 1px, 1px 15px, 15px 1px, 1px 15px;
  background-position:left top, left top, right top, right top,
                      left bottom, left bottom, right bottom, right bottom;
  opacity:.55;
}

/* --------------------------------------------------------------------------
   4. Composants
   -------------------------------------------------------------------------- */

/* --- Boutons --------------------------------------------------------------
   Des plaques vissées, pas des pastilles : angles francs, capitales gravées,
   biseau en `inset` clair au-dessus et sombre en dessous. Le grain casse
   l'aplat du dégradé, sans quoi le laiton redevient du plastique.
   -------------------------------------------------------------------------- */
.btn{
  position:relative; display:block; width:100%; text-align:center;
  font-family:'Cinzel',Georgia,serif; font-weight:700;
  font-size:13px; letter-spacing:.16em; text-transform:uppercase;
  padding:15px 16px; border-radius:2px; border:1px solid transparent;
  cursor:pointer; margin-bottom:12px;
  background-size:140px 140px, 100% 100%;
  background-blend-mode:soft-light, normal;
  transition:transform .12s ease, filter .16s ease, box-shadow .2s ease;
  -webkit-tap-highlight-color:transparent;
}
.btn:active{transform:translateY(1px) scale(.995);}
.btn:disabled{opacity:.32; cursor:not-allowed;}

.btn--primary{
  color:#241704; border-color:#6b4a12;
  background-image:var(--grain),
    linear-gradient(180deg,#ecd49c 0%,#c9a24a 44%,#a37c2c 72%,#7d5a1c 100%);
  text-shadow:0 1px 0 rgba(255,244,218,.45);
  box-shadow:
    inset 0 1px 0 rgba(255,247,228,.62), inset 0 -3px 8px rgba(58,34,4,.42),
    0 8px 22px rgba(0,0,0,.5), 0 0 24px rgba(201,162,74,.14);
}
.btn--primary:not(:disabled):hover{filter:brightness(1.08); box-shadow:inset 0 1px 0 rgba(255,247,228,.62), inset 0 -3px 8px rgba(58,34,4,.42), 0 10px 28px rgba(0,0,0,.55), 0 0 34px rgba(201,162,74,.30);}

.btn--ghost{
  color:var(--parchment); border-color:var(--line);
  background-image:var(--grain),
    linear-gradient(180deg, rgba(52,42,31,.70) 0%, rgba(20,15,11,.84) 100%);
  box-shadow:inset 0 1px 0 rgba(240,215,154,.10), 0 6px 18px rgba(0,0,0,.45);
}
.btn--ghost:not(:disabled):hover{border-color:rgba(201,162,74,.45); filter:brightness(1.15);}

/* Contrôles de l'hôte : sang séché. Une décision qui fait tomber quelqu'un ne
   doit pas avoir la même couleur qu'un bouton de navigation. */
.btn--danger{
  color:#f5e2dc; border-color:#5e1710;
  background-image:var(--grain),
    linear-gradient(180deg,#a8271c 0%,#7d1a13 55%,#54100c 100%);
  text-shadow:0 1px 2px rgba(0,0,0,.6);
  box-shadow:inset 0 1px 0 rgba(255,160,140,.24), inset 0 -3px 8px rgba(30,4,2,.5), 0 7px 20px rgba(0,0,0,.5);
}
.btn--danger:not(:disabled):hover{filter:brightness(1.15); box-shadow:inset 0 1px 0 rgba(255,160,140,.24), 0 9px 26px rgba(0,0,0,.55), 0 0 26px rgba(194,42,32,.30);}

.btn--moss{
  color:#e9f1e8; border-color:#2f4636;
  background-image:var(--grain),
    linear-gradient(180deg,#6d9576 0%,#4e7058 56%,#33503c 100%);
  text-shadow:0 1px 2px rgba(0,0,0,.45);
  box-shadow:inset 0 1px 0 rgba(220,245,225,.22), inset 0 -3px 8px rgba(10,25,14,.42), 0 7px 20px rgba(0,0,0,.45);
}
.btn--moss:not(:disabled):hover{filter:brightness(1.12);}

.btn--sm{padding:11px 13px; font-size:11px; letter-spacing:.14em; margin-bottom:8px;}
.btn:focus-visible,.input:focus-visible,.slider:focus-visible,.voice:focus-visible,.rune-btn:focus-visible{
  outline:2px solid var(--gold); outline-offset:2px;
}

/* --- Saisie -------------------------------------------------------------- */
.input{
  width:100%; color:var(--parchment);
  background:rgba(8,6,4,.66); border:1px solid var(--line);
  padding:14px 13px; border-radius:2px;
  font-family:'Jost',sans-serif; font-size:16px; margin-bottom:12px;
  box-shadow:inset 0 2px 9px rgba(0,0,0,.55);
}
.input::placeholder{color:#6f6553;}
.input:focus{border-color:rgba(201,162,74,.55); outline:none;}
.input--code{
  font-family:'Cinzel',Georgia,serif; font-weight:700;
  letter-spacing:.34em; text-align:center; text-transform:uppercase;
  font-size:24px; color:var(--gold-hi);
}
.code-big{
  font-family:'Cinzel',Georgia,serif; font-size:46px; font-weight:700;
  letter-spacing:.26em; text-align:center; color:var(--gold-hi); margin:6px 0 4px;
  text-indent:.26em;   /* compense l'interlettrage, qui décale le mot à gauche */
  text-shadow:0 0 28px rgba(201,162,74,.5), 0 2px 4px rgba(0,0,0,.9);
}

/* --- Invitation -----------------------------------------------------------
   Le code seul ne suffit pas : il faut pouvoir l'envoyer. Lien, QR et code en
   clair, du plus commode au plus universel.
   -------------------------------------------------------------------------- */
.invite__code{
  font-family:'Cinzel',Georgia,serif; font-size:clamp(34px,11vw,44px); font-weight:700;
  letter-spacing:.26em; text-indent:.26em; text-align:center;
  color:var(--gold-hi); margin:2px 0 6px;
  text-shadow:0 0 26px rgba(201,162,74,.45), 0 2px 4px rgba(0,0,0,.9);
  /* Un code se lit à voix haute, mais se sélectionne aussi au doigt. */
  user-select:all; -webkit-user-select:all;
}
.invite__actions{display:flex; gap:8px; margin-bottom:8px;}
.invite__actions .btn{margin:0; flex:1;}
.invite__done{color:var(--gold); margin:0 0 8px;}

/* Fond clair et modules noirs : c'est le contraste qu'attendent les lecteurs.
   Le QR est le seul endroit clair de l'interface, et c'est volontaire. */
.invite__qr{
  max-width:232px; margin:14px auto 0; padding:10px;
  background:#f2ece0; border:1px solid var(--line); border-radius:2px;
  box-shadow:0 8px 22px rgba(0,0,0,.5);
}
.invite__qr svg{display:block; width:100%; height:auto;}

/* --- Actions de la nuit ---------------------------------------------------
   Un panneau distinct du reste : quand c'est ton tour d'agir, l'écran doit le
   dire sans ambiguïté, y compris à quelqu'un qui vient de récupérer le
   téléphone au milieu d'une partie.
   -------------------------------------------------------------------------- */
.action-panel{
  margin:16px 0 4px; padding:16px 14px;
  border:1px solid rgba(201,162,74,.42); border-radius:2px;
  background:linear-gradient(180deg, rgba(60,46,26,.52), rgba(16,12,8,.62));
  box-shadow:inset 0 1px 0 rgba(240,215,154,.12);
}
.action-panel .label{margin-bottom:10px;}
/* Icônes détourées : plus de cadre ni de fond, le dessin se pose directement
   sur le bois du panneau.

   Le liseré doré n'est pas une décoration, c'est ce qui remplace le fond
   parchemin qu'on a retiré. Cinq des douze planches sont de l'encre sombre —
   le voleur, le fusil, le bâton, la fourche, la fiole de mort — et le
   rectangle clair leur servait de faire-valoir. Posées à nu sur un fond en
   rgb(7,10,18), elles disparaissaient : luminance moyenne 57 pour le voleur,
   dont 0,6 % de pixels clairs. Le halo redessine leur silhouette sans rien
   réintroduire derrière elles.

   Les ombres s'appliquent l'une après l'autre — chacune prend en entrée le
   résultat de la précédente. D'où les deux passes identiques de 1 px à pleine
   opacité : elles épaississent le trait en le gardant net, là où un unique
   flou de 2 px se dilue et rend moins. Mesuré sur fond réel, en surface au
   contraste WCAG 3:1 ou mieux : voleur 80 → 399 px, fourche 110 → 512,
   fusil 69 → 304. Les icônes déjà lisibles bougent à peine — le croc passe de
   882 à 1301 — donc rien n'est noyé.

   Vient ensuite la nappe large et discrète, qui adoucit le contour, puis
   l'ombre sombre et décalée qui décolle toujours l'icône du bois. */
.icone,
.acte__geste-logo{
  filter:
    drop-shadow(0 0 1px rgba(240,215,154,1))
    drop-shadow(0 0 1px rgba(240,215,154,1))
    drop-shadow(0 0 4px rgba(240,215,154,.45))
    drop-shadow(0 3px 7px rgba(0,0,0,.75));
}
.icone{width:44px; height:44px; object-fit:contain;}

/* --- Trame d'une action ---------------------------------------------------
   Toujours le même ordre : qui est appelé, puis chaque geste — son
   illustration, sa consigne, sa liste. La Sorcière n'est qu'un cas à deux
   gestes, elle ne réclame pas une mise en page à part.
   -------------------------------------------------------------------------- */
.acte__role{
  display:flex; align-items:center; gap:14px;
  padding:0 0 14px; margin-bottom:14px;
  border-bottom:1px solid var(--line-soft);
}
.acte__portrait{
  width:64px; height:64px; flex:none; border-radius:50%; object-fit:cover;
  border:1px solid rgba(201,162,74,.5);
  box-shadow:0 4px 14px rgba(0,0,0,.6);
}
.acte__nom{
  font-family:'Cinzel',Georgia,serif; font-size:23px; font-weight:700;
  letter-spacing:.04em; color:var(--gold-hi); line-height:1.15;
  text-shadow:0 0 18px rgba(201,162,74,.35), 0 2px 4px rgba(0,0,0,.9);
}

.acte__geste{display:flex; align-items:center; gap:14px; margin:0 0 10px;}
/* Le `filter` est posé plus haut, en commun avec `.icone` : le redéclarer ici
   l'écraserait — même spécificité, c'est la dernière règle qui gagne. */
.acte__geste-logo{width:58px; height:58px; flex:none; object-fit:contain;}
.acte__geste-texte{
  font-family:'Cinzel',Georgia,serif; font-size:15.5px; color:var(--parchment);
  line-height:1.3;
}
.acte__geste--vie .acte__geste-texte{color:#a8dca8;}
.acte__geste--mort .acte__geste-texte{color:#d6a8e8;}
/* Fiole vidée : le geste reste visible, mais visiblement hors d'usage. */
.acte__geste--off{opacity:.4; filter:grayscale(.7);}
.vote-item.is-off{opacity:.45; pointer-events:none;}
.vote-item__note{
  font-size:10.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--mist); opacity:.75;
}

/* Qui est concerné : emblème et nom, en grand. Sur l'écran de rôle, c'est la
   première chose à lire — savoir de qui est la carte qu'on va retourner. */
.qui{display:flex; align-items:center; justify-content:center; gap:14px; margin:0 0 6px;}
.qui__seal{
  width:52px; height:52px; flex:none; border-radius:50%;
  display:flex; align-items:center; justify-content:center; font-size:28px; line-height:1;
  background:radial-gradient(circle at 34% 28%, rgba(122,81,64,.6) 0%, rgba(59,36,28,.8) 72%);
  border:1px solid rgba(201,162,74,.45);
  box-shadow:inset 0 1px 2px rgba(255,220,180,.25), 0 3px 10px rgba(0,0,0,.6);
}
.qui__nom{margin:0;}

/* Une liste suit toujours son geste : on la rapproche de lui, et on ménage
   l'écart avant le geste suivant. */
.acte__geste + .vote-list{margin-top:0; margin-bottom:18px;}

/* La victime des Loups, telle que la Sorcière la découvre. Le nom doit sauter
   aux yeux : c'est la seule information sur laquelle repose sa décision. */
.victim{
  display:flex; align-items:center; gap:12px;
  margin:0 0 14px; padding:12px 14px;
  border:1px solid rgba(194,42,32,.5); border-radius:2px;
  background:linear-gradient(180deg, rgba(80,20,14,.42), rgba(24,8,6,.55));
}
.victim__mark{font-size:26px; line-height:1; color:var(--blood-hi); flex:none;}
.victim__lead{
  font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--mist);
}
.victim__name{
  font-family:'Cinzel',Georgia,serif; font-size:21px; color:var(--parchment);
  text-shadow:0 1px 6px rgba(0,0,0,.8);
}

/* Ce que la Voyante découvre : un seul médaillon, en grand, et rien d'autre. */
.seer-reveal{display:flex; align-items:center; gap:14px; padding:6px 0;}
.seer-reveal__medal{
  width:78px; height:78px; flex:none; border-radius:50%;
  border:1px solid rgba(201,162,74,.5); object-fit:cover;
  box-shadow:0 6px 18px rgba(0,0,0,.5);
}
.seer-reveal__name{font-family:'Cinzel',Georgia,serif; font-size:17px; color:var(--parchment);}
.seer-reveal__role{color:var(--gold); font-size:14px; letter-spacing:.06em;}

/* --- Révélation d'un mort -------------------------------------------------
   Le moment que la table attend. Le médaillon porte l'information, la couleur
   du camp la confirme.
   -------------------------------------------------------------------------- */
.reveal-card{border-color:rgba(201,162,74,.4);}
/* Le bloc est centré comme le titre qui le surmonte. Aligné à gauche sous un
   « Il n'est plus » centré, il partait de travers dans le rectangle. */
.reveal{display:flex; align-items:center; justify-content:center; gap:14px; padding:8px 0;}
.reveal__text{text-align:center;}
.reveal + .reveal{border-top:1px solid var(--line-soft); margin-top:4px;}
.reveal__medal{
  width:66px; height:66px; flex:none; border-radius:50%;
  border:1px solid rgba(201,162,74,.45); object-fit:cover;
  filter:grayscale(.35) brightness(.85);   /* il est mort : la couleur s'en va */
  box-shadow:0 6px 18px rgba(0,0,0,.5);
}
.reveal__name{font-family:'Cinzel',Georgia,serif; font-size:17px; color:var(--parchment);}
.reveal__role{font-size:14px; letter-spacing:.06em;}
.reveal__role--loups{color:var(--blood-hi);}
.reveal__role--village{color:var(--moss);}

/* Onglets de votant, quand un téléphone porte plusieurs joueurs. */
.voter-tabs{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:12px;}
.voter-tab{
  font-family:'Jost',sans-serif; font-size:12px; padding:7px 12px;
  border-radius:2px; cursor:pointer; color:var(--mist);
  border:1px solid var(--line-soft); background:rgba(20,15,11,.7);
}
.voter-tab.is-active{color:var(--parchment); border-color:rgba(201,162,74,.55); background:rgba(201,162,74,.14);}

/* --- Illustration de fin de partie ---------------------------------------
   Le camp vainqueur en grand, sous le titre. Cadre gravé et liseré doré, comme
   les médaillons de rôle : l'image est peinte, le cadre la rattache au reste.

   `aspect-ratio` réserve la place avant le chargement — sans lui, le titre et
   le tableau des rôles sauteraient quand l'image arrive.
   -------------------------------------------------------------------------- */
.finale{
  position:relative; margin:0 0 16px; padding:8px;
  border:1px solid var(--line); border-radius:var(--radius);
  background-image:var(--grain),
    linear-gradient(168deg, rgba(46,35,24,.72) 0%, rgba(12,9,6,.86) 100%);
  background-size:180px 180px, 100% 100%;
  background-blend-mode:soft-light, normal;
  box-shadow:0 18px 44px rgba(0,0,0,.55), inset 0 1px 0 rgba(240,215,154,.10);
}
/* 4/5, le format des deux illustrations une fois séparées : le villageois
   bras levés et le loup en approche sont tous deux en pied, il leur faut de
   la hauteur. Un cadre paysage les aurait décapités. */
.finale__art{
  display:block; width:100%; aspect-ratio:4 / 5;
  object-fit:cover; border-radius:2px;
}

/* Repli sans illustration dédiée : le médaillon du camp, à sa taille native.
   L'étirer le rendrait flou — 176 px ne font pas une grande image. */
.finale--medaillon{
  display:flex; align-items:center; justify-content:center;
  padding:26px 8px;
}
.finale__medaillon{
  width:176px; height:176px; border-radius:50%;
  border:1px solid rgba(201,162,74,.5);
  box-shadow:0 10px 26px rgba(0,0,0,.6), 0 0 34px rgba(201,162,74,.16);
}

/* Réglage des minuteurs. Les incréments reprennent la rune du compteur de
   loups plutôt que des pastilles rondes : c'est le même geste, il doit avoir
   la même forme. Le nombre est gravé en Cinzel, l'unité en retrait. */
.timer-row{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:9px 0; border-bottom:1px solid var(--line-soft);
}
.timer-row:last-child{border-bottom:none;}
.timer-row__label{font-size:14px; color:var(--parchment); min-width:0;}
.timer-row__set{display:flex; align-items:center; gap:10px; flex:none;}
.timer-row__value{
  font-family:'Cinzel',Georgia,serif; font-weight:700; font-size:21px; line-height:1;
  color:var(--gold-hi); min-width:56px; text-align:center;
  text-shadow:0 0 16px rgba(201,162,74,.4), 0 1px 3px rgba(0,0,0,.9);
}
.timer-row__value em{font-style:normal; font-size:12px; color:var(--gold); margin-left:2px;}

/* Saisie du code sur l'accueil : le champ prend la place, le bouton le reste. */
.join-row{display:flex; gap:8px; align-items:stretch;}
.join-row .input{margin:0; flex:1; min-width:0;}
.join-row .btn{margin:0; width:auto; flex:none; padding-left:20px; padding-right:20px;}

/* --- Registre du village --------------------------------------------------
   Les joueurs ne sont plus des pastilles mais des lignes de registre : un
   sceau de cire, un nom gravé, une mention. Deux colonnes dès qu'il y a la
   place, pour qu'une table de dix tienne encore à l'écran.
   -------------------------------------------------------------------------- */
/* Une case par joueur, plus large qu'avant : l'emblème et le nom doivent se
   lire d'un coup d'oeil, y compris par le voisin d'en face. */
.roster{display:grid; grid-template-columns:repeat(auto-fill,minmax(170px,1fr)); gap:6px;}
.roster__item{
  position:relative;
  display:flex; align-items:center; gap:10px;
  padding:9px 11px 9px 10px; border-radius:2px;
  border:1px solid var(--line-soft);
  border-left:2px solid rgba(201,162,74,.45);
  background:linear-gradient(90deg, rgba(46,35,24,.58), rgba(14,11,8,.38));
  min-width:0; text-align:left; font:inherit; color:inherit;
}
.roster__seal{
  width:34px; height:34px; flex:none; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:'Cinzel',Georgia,serif; font-size:14px; font-weight:700; color:#f6ecd4;
  background:radial-gradient(circle at 34% 28%, #7a5140 0%, #3b241c 72%);
  border:1px solid rgba(201,162,74,.38);
  box-shadow:inset 0 1px 2px rgba(255,220,180,.28), 0 2px 6px rgba(0,0,0,.6);
}
/* L'emblème est un dessin, pas une lettre : il lui faut la place, et le fond
   gravé s'efface pour ne pas l'assombrir. */
.roster__seal--embleme{
  font-size:19px; line-height:1;
  background:radial-gradient(circle at 34% 28%, rgba(122,81,64,.55) 0%, rgba(59,36,28,.75) 72%);
}
/* Le nom porte un cadre, et ce cadre porte l'information « cette personne est
   encore devant son écran ».

   Le liseré reprend le détourage des icônes d'action : deux traits serrés qui
   posent le contour, une nappe large et douce qui l'accroche au bois, l'ombre
   sombre qui l'en décolle. En `box-shadow` et non en `filter:drop-shadow` :
   sur un rectangle les deux donnent la même image, mais le filtre suivrait
   aussi l'alpha des lettres et poserait un halo doré sur le nom — illisible à
   quinze pixels.

   Rien ici n'est positionné. `.roster__pick` est une surface transparente
   posée sur toute la case, en `z-index:0` ; donner un `position:relative` au
   nom le ferait passer devant, et l'on ne pourrait plus changer d'emblème en
   touchant le nom — c'est-à-dire la moitié de la case. */
.roster__name{
  font-family:'Cinzel',Georgia,serif; font-size:15.5px; letter-spacing:.02em;
  flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  padding:4px 9px; border-radius:2px;
  border:1px solid rgba(201,162,74,.5);
  background:rgba(8,6,4,.3);
  box-shadow:
    0 0 0 1px rgba(240,215,154,.26),
    0 0 7px rgba(240,215,154,.2),
    0 2px 5px rgba(0,0,0,.55);
  transition:border-color .3s ease, box-shadow .3s ease, color .3s ease, background-color .3s ease;
}

/* Plus personne devant la page : l'or se retire, le cadre reste.

   Un état, pas une alerte — d'où le gris et non le rouge, et d'où la
   transition d'un tiers de seconde : quelqu'un qui consulte une notification
   pendant la nuit revient dix secondes plus tard, et un registre qui
   clignoterait à chaque aller-retour serait pire que pas d'indication du tout.

   La bascule ne touche que l'affichage. Aucune règle de jeu ne la lit : une
   table ne doit pas s'arrêter parce qu'un joueur a verrouillé son téléphone. */
.roster__item--absent .roster__name{
  border-color:rgba(150,146,138,.4);
  background:rgba(8,6,4,.2);
  color:var(--mist);
  box-shadow:
    0 0 0 1px rgba(190,186,178,.14),
    0 0 6px rgba(190,186,178,.09),
    0 2px 5px rgba(0,0,0,.5);
}
.roster__item--absent .roster__seal{opacity:.5; filter:grayscale(.85);}
.roster__item--absent .roster__tag{color:var(--mist); opacity:.85;}

/* Ses propres joueurs se touchent pour changer d'emblème. La surface cliquable
   est un bouton transparent posé sur toute la case, et non la case elle-même :
   celle-ci contient aussi la croix de renvoi, et un bouton ne se niche pas
   dans un autre bouton. */
.roster__item--mien{cursor:pointer; transition:border-color .15s ease, filter .15s ease;}
.roster__item--mien:hover{border-color:rgba(201,162,74,.5); filter:brightness(1.12);}
.roster__item--mien:active{transform:translateY(1px);}
.roster__pick{
  position:absolute; inset:0; z-index:0;
  border:0; background:none; padding:0; cursor:pointer;
}

/* --- Retirer quelqu'un de la table ----------------------------------------
   Réservé à l'hôte, et au salon seulement.

   La croix ne se montre qu'au survol, comme demandé — mais un téléphone n'a
   pas de survol, et elle y resterait invisible pour toujours. Elle est donc
   affichée en permanence par défaut, discrète, et le masquage au repos est
   réservé aux pointeurs qui savent survoler.
   -------------------------------------------------------------------------- */
.roster__kick{
  position:relative; z-index:1; flex:none;
  width:22px; height:22px; padding:0; margin:-4px -4px -4px 0;
  display:flex; align-items:center; justify-content:center;
  font:400 18px/1 'Jost',system-ui,sans-serif;
  color:var(--mist); opacity:.55; cursor:pointer;
  background:none; border:1px solid transparent; border-radius:50%;
  transition:opacity .15s ease, color .15s ease, background-color .15s ease;
}
.roster__kick:hover,
.roster__kick:focus-visible{
  opacity:1; color:var(--blood-hi);
  background:rgba(194,42,32,.16); border-color:rgba(194,42,32,.5);
}
@media (hover:hover) and (pointer:fine){
  .roster__kick{opacity:0;}
  .roster__item:hover .roster__kick,
  .roster__kick:focus-visible{opacity:1;}
}

/* Confirmation. Un seul appui suffirait, mais sur tactile la croix est
   toujours là : un doigt égaré renverrait quelqu'un sans un mot. */
.roster__item--kicking{
  border-color:rgba(194,42,32,.55);
  border-left-color:rgba(194,42,32,.8);
  background:linear-gradient(90deg, rgba(58,24,20,.6), rgba(14,8,7,.4));
}
.roster__ask{position:relative; z-index:1; display:flex; gap:4px; flex:none;}
.roster__ask-btn{
  padding:3px 7px; border-radius:2px; cursor:pointer;
  font:400 10px/1.2 'Jost',system-ui,sans-serif;
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--mist); background:rgba(8,6,4,.5);
  border:1px solid var(--line-soft);
}
.roster__ask-btn--oui{color:var(--blood-hi); border-color:rgba(194,42,32,.5);}
.roster__ask-btn:hover{filter:brightness(1.25);}

.avatar-grid{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(52px,1fr)); gap:8px;
}
.avatar-choice{
  aspect-ratio:1; display:flex; align-items:center; justify-content:center;
  font-size:26px; line-height:1; cursor:pointer; border-radius:2px;
  border:1px solid var(--line-soft);
  background:linear-gradient(180deg, rgba(52,42,31,.7), rgba(16,12,9,.85));
  transition:border-color .15s ease, filter .15s ease;
}
.avatar-choice:hover{border-color:rgba(201,162,74,.5); filter:brightness(1.2);}
.avatar-choice.is-active{
  border-color:var(--gold); background:rgba(201,162,74,.16);
  box-shadow:0 0 14px rgba(201,162,74,.28);
}
/* Déjà porté par quelqu'un d'autre : estompé, mais pas interdit — deux
   joueurs qui tiennent au même sanglier sauront s'arranger. */
.avatar-choice.is-taken{opacity:.32;}
.roster__tag{
  font-size:9px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--gold); opacity:.7; flex:none;
}
/* Récapitulatif de fin : une colonne, pour que « Petite Fille » tienne à côté
   du nom sans se couper. */
.roster--wide{grid-template-columns:1fr;}
.medal--seal{width:28px; height:28px;}
.medal--seal.medal--rune{font-size:14px;}
.roster__item--wolf{
  border-left-color:rgba(194,42,32,.6);
  background:linear-gradient(90deg, rgba(58,24,20,.55), rgba(14,8,7,.38));
}
.roster__item--wolf .roster__tag{color:var(--blood-hi);}
.roster__item--bot .roster__seal{background:radial-gradient(circle at 34% 28%, #46545e 0%, #1b2227 72%);}
.roster__item--bot .roster__tag{color:var(--mist);}
.roster__item--dead{
  border-left-color:rgba(194,42,32,.55); opacity:.46; filter:grayscale(.55);
}
.roster__item--dead .roster__name{text-decoration:line-through; text-decoration-color:rgba(194,42,32,.8);}
.roster__item--dead .roster__tag{color:var(--blood-hi); opacity:.9;}
.roster__item--dead .roster__seal{background:radial-gradient(circle at 34% 28%, #4d2c26 0%, #190d0b 72%);}

/* --- Choix des loups ------------------------------------------------------
   Le bloc le plus lourd de la page de composition, et c'est voulu : c'est le
   réglage qui décide de la partie. Il est encadré par deux filets gravés,
   « Loups-Garous » puis « Le village », posés à la même place et centrés de la
   même façon : le panneau se lit alors comme le village coupé en deux.
   -------------------------------------------------------------------------- */
.wolf-pick{display:flex; align-items:center; justify-content:center; gap:14px; padding:4px 0 20px;}
.wolf-pick__mark{width:62px; height:62px; flex:none; position:relative;}
.wolf-pick__mark::before{
  content:''; position:absolute; inset:-16%; border-radius:50%;
  background:radial-gradient(circle, rgba(194,42,32,.24) 0%, rgba(194,42,32,0) 68%);
}
/* Le loup est du camp d'en face : son liseré vire au rouge, comme le compteur
   qu'il accompagne, et la lueur rouge du halo passe sous la pastille. */
.medal--mark{
  position:relative; width:100%; height:100%;
  border-color:rgba(194,42,32,.55);
  box-shadow:0 2px 12px rgba(0,0,0,.65), 0 0 18px rgba(194,42,32,.26), inset 0 0 0 1px rgba(0,0,0,.35);
}
.wolf-pick__count{
  font-family:'Cinzel',Georgia,serif; font-weight:700; font-size:46px; line-height:1;
  color:var(--blood-hi); min-width:46px; text-align:center;
  text-shadow:0 0 20px rgba(232,65,44,.55), 0 0 46px rgba(232,65,44,.28), 0 2px 3px rgba(0,0,0,.9);
}
.rune-btn{
  width:38px; height:38px; flex:none; border-radius:2px; cursor:pointer;
  border:1px solid var(--line);
  background:linear-gradient(180deg, rgba(52,42,31,.72), rgba(16,12,9,.86));
  color:var(--gold-hi); font-family:'Cinzel',Georgia,serif; font-size:19px; line-height:1;
  box-shadow:inset 0 1px 0 rgba(240,215,154,.10);
  transition:border-color .15s ease, filter .15s ease;
}
.rune-btn:hover{border-color:rgba(201,162,74,.5); filter:brightness(1.2);}
.rune-btn:active{transform:translateY(1px);}

/* --- Médaillons de rôles --------------------------------------------------
   Les illustrations sont livrées carrées : c'est le rayon qui les découpe en
   pastille. Un JPEG carré pèse le dixième d'un PNG détouré, et le fond sombre
   du jeu ne laisse rien deviner de la découpe.

   Le liseré doré et l'ombre portée sont posés ici, une fois : ce sont eux qui
   rattachent une image peinte au reste de l'habillage gravé, sans quoi elle
   flotterait comme une vignette collée sur la page.
   -------------------------------------------------------------------------- */
.medal{
  flex:none; display:block; border-radius:50%; object-fit:cover;
  background:#14100a;
  border:1px solid rgba(201,162,74,.42);
  box-shadow:0 2px 9px rgba(0,0,0,.6), inset 0 0 0 1px rgba(0,0,0,.35);
}
/* Repli pour un rôle sans illustration (le Capitaine) : même pastille, mais
   remplie d'un emblème typographique plutôt que d'un dessin. */
.medal--rune{
  display:flex; align-items:center; justify-content:center; line-height:1;
  background:radial-gradient(circle at 34% 28%, #3a2e20 0%, #14100a 72%);
}

/* --- Rôles optionnels ---------------------------------------------------- */
.role-toggle{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:13px 0; border-bottom:1px solid var(--line-soft);}
.role-toggle:last-child{border-bottom:none;}
.role-toggle .info{display:flex; gap:12px; align-items:flex-start; min-width:0;}
.medal--row{width:44px; height:44px; margin-top:1px;}
.medal--row.medal--rune{font-size:21px;}
/* Le Villageois est là pour information, pas pour être réglé.

   Un « Toujours » gravé en tout petit occupait la colonne de l'interrupteur
   sans rien dire d'utile : que le rôle soit systématique se voyait déjà à
   l'absence de bascule. Le nombre, lui, répond à la question qu'on se pose
   vraiment devant cet écran — combien de joueurs n'auront aucun pouvoir.

   Même gravure que le compteur de Loups, en or plutôt qu'en sang : ce sont les
   deux moitiés du même partage, et le bloc se lit d'un coup comme un village
   coupé en deux. La taille descend de 46 à 27 px, la ligne d'un rôle n'ayant
   pas la hauteur du bloc des Loups. */
.role-toggle__count{
  flex:none; width:44px; text-align:center;
  font-family:'Cinzel',Georgia,serif; font-weight:700; font-size:27px; line-height:1;
  color:var(--gold-hi);
  text-shadow:0 0 18px rgba(201,162,74,.5), 0 2px 3px rgba(0,0,0,.9);
}
.role-toggle .name{font-family:'Cinzel',Georgia,serif; font-size:16px; letter-spacing:.02em; margin-bottom:3px;}
/* Pas de largeur maximale : c'est le flex qui borne la colonne, et un plafond
   fixe laisserait une gouttière vide sur les écrans larges. */
.role-toggle .desc{font-size:13.5px; color:var(--mist); line-height:1.5;}
.switch{position:relative; width:44px; height:24px; flex:none;}
.switch input{opacity:0; width:100%; height:100%; position:absolute; margin:0; cursor:pointer; z-index:2;}
.switch .track{position:absolute; inset:0; border-radius:999px; transition:.18s;
  background:rgba(8,6,4,.85); border:1px solid var(--line); box-shadow:inset 0 1px 4px rgba(0,0,0,.6);}
.switch .thumb{position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:999px; transition:.18s;
  background:radial-gradient(circle at 34% 30%, #a3a099 0%, #6c6a63 72%);}
.switch input:checked + .track{
  background:linear-gradient(180deg, rgba(201,162,74,.55), rgba(125,90,28,.6));
  border-color:rgba(201,162,74,.7);
}
.switch input:checked + .track + .thumb{
  transform:translateX(20px);
  background:radial-gradient(circle at 34% 30%, #fff3d6 0%, #c9a24a 78%);
  box-shadow:0 0 10px rgba(201,162,74,.6);
}

/* --- Voix du narrateur --------------------------------------------------- */
.voices{display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:4px;}
.voice{
  display:block; text-align:left; cursor:pointer; color:var(--parchment);
  padding:12px 11px; border-radius:2px; border:1px solid var(--line-soft);
  background:linear-gradient(180deg, rgba(46,35,24,.58), rgba(14,11,8,.5));
  transition:border-color .15s ease, box-shadow .2s ease, background .2s ease;
}
.voice:hover{border-color:rgba(201,162,74,.4);}
.voice.is-active{
  border-color:var(--gold);
  background:linear-gradient(180deg, rgba(201,162,74,.20), rgba(201,162,74,.04));
  box-shadow:inset 0 0 0 1px rgba(201,162,74,.28), 0 0 22px rgba(201,162,74,.18);
}
.voice__name{
  display:block; font-family:'Cinzel',Georgia,serif; font-size:13px;
  letter-spacing:.05em; margin-bottom:3px;
}
.voice__hint{display:block; font-size:11px; color:var(--mist); line-height:1.4;}
/* Pastille de laiton sur les timbres dont les répliques sont enregistrées :
   c'est la seule différence qui compte vraiment entre les quatre choix. */
.voice__tag{
  display:inline-block; margin-left:7px; padding:1px 6px 2px; border-radius:2px;
  font-family:'Jost',sans-serif; font-size:9px; font-weight:600;
  letter-spacing:.1em; text-transform:uppercase; color:#1c1206;
  background:linear-gradient(180deg,#ecd49c,#c9a24a);
  vertical-align:1px; white-space:nowrap;
}
/* Mention technique sous le choix des timbres : lisible, mais en retrait. */
.hint--tight{font-size:11.5px; margin:12px 0 18px; opacity:.8;}
.hint--tight b{color:var(--gold); font-weight:600;}

/* --- Volumes --------------------------------------------------------------
   Deux réglages, toujours accessibles, jamais de bouton marche/arrêt : couper
   le son se fait en glissant à zéro, ce qui laisse le curseur là où l'oeil l'a
   appris. */
.mixer{display:flex; flex-direction:column; gap:11px;}
.mixer__row{display:flex; align-items:center; gap:11px;}
.mixer__icon{width:20px; flex:none; text-align:center; font-size:14px;}
.mixer__name{
  font-family:'Cinzel',Georgia,serif; font-size:10px; letter-spacing:.2em;
  text-transform:uppercase; color:var(--gold); width:62px; flex:none;
}
.slider{
  -webkit-appearance:none; appearance:none; flex:1; min-width:0;
  height:3px; border-radius:2px; background:rgba(201,162,74,.22);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.4); cursor:pointer;
}
.slider::-webkit-slider-thumb{
  -webkit-appearance:none; width:17px; height:17px; border-radius:50%;
  background:radial-gradient(circle at 34% 30%, #f7e6bd 0%, #a37c2c 78%);
  border:1px solid var(--gold-lo); box-shadow:0 0 10px rgba(201,162,74,.5); cursor:pointer;
}
.slider::-moz-range-thumb{
  width:15px; height:15px; border-radius:50%;
  background:radial-gradient(circle at 34% 30%, #f7e6bd 0%, #a37c2c 78%);
  border:1px solid var(--gold-lo); box-shadow:0 0 10px rgba(201,162,74,.5); cursor:pointer;
}
.mixer__val{
  font-family:'JetBrains Mono',monospace; font-size:10px; color:var(--mist);
  width:32px; flex:none; text-align:right;
}

/* --- Narration ----------------------------------------------------------- */
.phase-tag{
  font-family:'Cinzel',Georgia,serif; font-size:12.5px; text-transform:uppercase;
  letter-spacing:.34em; text-align:center; color:var(--gold); margin-bottom:10px;
  text-shadow:0 0 18px rgba(201,162,74,.4), 0 1px 6px rgba(0,0,0,.9);
}
.narrator{
  position:relative;
  font-family:'Cinzel',Georgia,serif; font-size:16px; font-weight:500; line-height:1.65;
  text-align:center; color:#f4ead4; padding:10px 14px 6px; min-height:64px;
  text-shadow:0 1px 12px rgba(0,0,0,.75);
}
.narrator::before,.narrator::after{
  position:absolute; font-size:30px; line-height:1; color:var(--gold); opacity:.35;
}
.narrator::before{content:'“'; left:-2px; top:2px;}
.narrator::after{content:'”'; right:-2px; bottom:-6px;}

.role-card{
  position:relative;
  background-image:var(--grain),
    linear-gradient(160deg, rgba(52,40,27,.86), rgba(12,9,6,.92));
  background-size:180px 180px, 100% 100%;
  background-blend-mode:soft-light, normal;
  -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border:1px solid rgba(201,162,74,.5); border-radius:var(--radius);
  padding:32px 22px; text-align:center;
  box-shadow:0 22px 52px rgba(0,0,0,.6), 0 0 44px rgba(201,162,74,.12), inset 0 0 60px rgba(0,0,0,.4);
}
.role-card::before{
  content:''; position:absolute; inset:7px; pointer-events:none;
  border:1px solid rgba(201,162,74,.20); border-radius:1px;
}
/* Seule pastille qu'on regarde vraiment : elle est centrée et assez grande
   pour qu'on lise le dessin, l'ombre la décollant du fond de la carte. */
.medal--card{
  width:104px; height:104px; margin:0 auto 14px;
  border-width:2px;
  box-shadow:0 8px 22px rgba(0,0,0,.72), 0 0 26px rgba(201,162,74,.16), inset 0 0 0 1px rgba(0,0,0,.35);
}
.medal--card.medal--rune{font-size:50px;}
.role-card .name{font-family:'Cinzel',Georgia,serif; font-size:24px; letter-spacing:.04em; margin-bottom:8px;}
.role-card .team{font-family:'Cinzel',Georgia,serif; font-size:10px; text-transform:uppercase; letter-spacing:.28em; margin-bottom:16px;}
.role-card .team.village{color:var(--moss);}
.role-card .team.loups{color:var(--blood-hi);}
.role-card .desc{font-size:14px; color:var(--mist); line-height:1.55;}

.timer-wrap{display:flex; justify-content:center; margin:16px 0 6px;}
.timer{position:relative; width:120px; height:120px;}
/* `overflow:visible` : sans lui, le SVG rogne à ses bords la lueur portée du
   cercle, et le halo s'arrête net sur un carré autour d'un anneau rond. */
.timer svg{transform:rotate(-90deg); width:100%; height:100%; overflow:visible;}
.timer .bg{fill:none; stroke:rgba(201,162,74,.14); stroke-width:6;}
/* Pas de `transition` sur le tracé : l'arc est repositionné à chaque image par
   app.js. Une transition d'une seconde par-dessus une mise à jour continue ne
   ferait que traîner derrière la valeur réelle. */
.timer .fg{fill:none; stroke:var(--gold); stroke-width:6; stroke-linecap:round; filter:drop-shadow(0 0 6px rgba(201,162,74,.6));}
.timer .num{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-family:'Cinzel',Georgia,serif; font-size:32px; font-weight:700; color:var(--gold-hi);
  text-shadow:0 0 18px rgba(201,162,74,.4);
}

/* Décompte d'avant-partie : seul élément de son écran, il doit se lire depuis
   l'autre bout de la table. Déclaré après `.timer`, sinon la règle de base
   l'emporterait — même spécificité, c'est la dernière qui gagne. */
.timer--grand{width:172px; height:172px;}
.timer--grand .num{font-size:58px;}

.vote-list{display:flex; flex-direction:column; gap:6px; margin-top:12px;}
.vote-item{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:12px 13px; border-radius:2px; cursor:pointer;
  border:1px solid var(--line-soft); border-left:2px solid rgba(201,162,74,.4);
  background:linear-gradient(90deg, rgba(46,35,24,.58), rgba(14,11,8,.38));
  font-family:'Cinzel',Georgia,serif; font-size:14px;
  transition:border-color .15s ease, background .18s ease;
}
.vote-item:hover{border-color:rgba(201,162,74,.42);}
.vote-item.is-selected{
  border-color:var(--gold);
  background:linear-gradient(90deg, rgba(201,162,74,.2), rgba(201,162,74,.05));
}
.vote-item .tally{font-family:'JetBrains Mono',monospace; color:var(--blood-hi); font-size:12px; flex:none;}

/* --- Le dépouillement -----------------------------------------------------
   Dès qu'une cible a des bulletins, sa case passe sur deux lignes : le nom et
   le compte au-dessus, les votants en dessous. Sans ce basculement en colonne,
   les emblèmes se battraient avec le compte pour la même place.
   -------------------------------------------------------------------------- */
.vote-item--depouille{flex-direction:column; align-items:stretch; gap:8px;}
.vote-item__ligne{display:flex; align-items:center; justify-content:space-between; gap:10px;}
/* Le récapitulatif d'après-vote ne se clique pas : le vote est clos. */
.vote-item--fige{cursor:default;}
.vote-item--fige:hover{border-color:var(--line-soft);}

.ballots{display:flex; flex-wrap:wrap; gap:5px;}
.ballot{
  display:inline-flex; align-items:center; gap:5px;
  padding:3px 8px 3px 4px; border-radius:11px;
  background:rgba(8,6,4,.5); border:1px solid var(--line-soft);
  font-family:'Jost',system-ui,sans-serif; font-size:11.5px; color:var(--mist);
}
.ballot__seal{font-size:14px; line-height:1;}
.ballot__nom{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:11ch;}
/* Le Capitaine pèse double : autant que ça se voie sur son bulletin. */
.ballot--capitaine{border-color:rgba(201,162,74,.45); color:var(--gold-hi);}
.ballot__x2{
  font-family:'JetBrains Mono',monospace; font-size:10px;
  color:var(--gold); opacity:.9;
}

.host-panel{
  margin-top:20px; padding-top:16px;
  border-top:1px solid var(--line-soft);
}
.host-panel .label{color:var(--blood-hi);}

/* Chronique : la trace de ce qui s'est passé, en marge du panneau. */
.log{
  max-height:120px; overflow-y:auto; font-size:12px; color:var(--mist);
  border-top:1px solid var(--line-soft); margin-top:16px; padding-top:12px; line-height:1.5;
}
.log div{margin-bottom:5px; padding-left:13px; position:relative;}
.log div::before{content:'·'; position:absolute; left:2px; color:var(--gold); opacity:.6;}

/* --------------------------------------------------------------------------
   Chat vocal

   Le vocabulaire du plateau, pas celui d'un logiciel de visioconférence : de
   l'or gravé pour le village, du sang séché pour la salle des Loups. Un joueur
   doit voir d'un coup d'oeil où sa voix part — c'est la seule chose qui compte
   ici, et se tromper de salle est précisément ce qu'on veut rendre impossible.
   -------------------------------------------------------------------------- */
.voice-bar{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  margin-bottom:12px; padding-bottom:11px;
  border-bottom:1px solid var(--line-soft);
}
.voice-state{
  font-family:'Cinzel',serif; font-size:12.5px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--gold-hi);
}
.voice-state--wolf{color:var(--blood-hi); text-shadow:0 0 18px rgba(194,42,32,.5);}

.voice-status{
  font-family:'JetBrains Mono',monospace; font-size:11px;
  color:var(--mist); letter-spacing:.04em;
}
/* Micro ouvert : une pastille qui respire. Sans elle, rien ne distingue
   « je parle dans le vide » de « on m'entend », et c'est la première question
   que se pose quelqu'un qui vient de cliquer. */
.voice-status.is-live{color:var(--moss);}
.voice-status.is-live::before{
  content:''; display:inline-block; width:7px; height:7px; margin-right:7px;
  border-radius:50%; background:var(--moss); vertical-align:middle;
  box-shadow:0 0 10px rgba(95,132,104,.9);
  animation:voice-pulse 1.9s ease-in-out infinite;
}
@keyframes voice-pulse{
  0%,100%{opacity:1; transform:scale(1);}
  50%    {opacity:.45; transform:scale(.82);}
}
@media (prefers-reduced-motion:reduce){
  .voice-status.is-live::before{animation:none;}
}

/* L'appuyer-pour-parler était une case à cocher ici même. Il est passé dans le
   tiroir des réglages, sur l'interrupteur `.switch` déjà employé par la
   composition des rôles : deux composants pour la même question, ça se voit. */

/* Qui parle, dans le registre. L'anneau ne s'allume que pour les voix qu'on
   entend vraiment : un Villageois ne voit jamais s'agiter un Loup en pleine
   concertation, puisque son navigateur ne reçoit pas ce flux. */
.roster__item--speaking{
  border-color:rgba(95,132,104,.72);
  box-shadow:0 0 0 1px rgba(95,132,104,.34), 0 0 22px rgba(95,132,104,.26);
}
.roster__item--speaking .roster__seal{
  box-shadow:0 0 0 2px rgba(95,132,104,.85), 0 0 16px rgba(95,132,104,.55);
}

/* Les autres salles vocales. Chacune a sa couleur du plateau : le sang pour
   les Loups, la cendre pour les morts, le rouge tendre pour les amoureux. */
.voice-state--dead{color:var(--mist); text-shadow:0 0 14px rgba(169,184,204,.35);}
.voice-state--lover{color:#e0708a; text-shadow:0 0 18px rgba(224,112,138,.45);}
.voice-echo{
  font-size:11px; color:var(--mist); opacity:.75; margin-left:8px;
  font-family:'JetBrains Mono',monospace; letter-spacing:.03em;
}
/* En veille : débranché exprès pendant la nuit. Ni alarmant, ni invisible. */
.voice-state--sleep{color:var(--mist); opacity:.8;}

/* Réglage individuel : une table de mixage, une ligne par voix entendue.

   C'était une rangée de pastilles à couper d'un appui. Un interrupteur ne
   laisse le choix qu'entre « je l'entends » et « je ne l'entends plus », alors
   que le cas courant est « celui-là parle trop fort ». Le curseur couvre les
   deux, zéro faisant la sourdine — même règle que le mixeur du narrateur, et
   la position retrouvée évite de redeviner le niveau qu'on avait mis.

   Le libellé sort de la casse du mixeur : ce sont des prénoms, pas des noms de
   pistes. Largeur fixe malgré tout, sinon les curseurs ne s'alignent plus d'une
   ligne à l'autre et la colonne se lit comme un escalier. */
.mixer--peers{
  margin:12px 0 14px; padding:12px 0;
  border-top:1px solid var(--line-soft);
  border-bottom:1px solid var(--line-soft);
}
.mixer__name--peer{
  width:86px;
  font-family:'Jost',sans-serif; font-size:12.5px; font-weight:500;
  letter-spacing:.01em; text-transform:none; color:var(--parchment);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* --------------------------------------------------------------------------
   Réglages — l'écrou et son tiroir

   Un seul endroit pour le timbre du narrateur, les volumes et la voix des
   joueurs, atteignable depuis n'importe quel écran. Avant, ces réglages
   n'existaient que dans le salon : une fois la nuit tombée, plus moyen de
   baisser l'ambiance ni de changer de timbre sans quitter la partie.
   ----------------------------------------------------------------------- */

/* Posé à côté du titre, et il défile avec lui.

   Il a été `fixed` un temps, et c'était une erreur de lecture : un élément fixe
   reste collé à la fenêtre, donc il *descend le long du contenu* dès qu'on fait
   défiler. On le voyait glisser sur le registre puis sur le journal, comme s'il
   suivait le doigt. Un écrou est un objet de l'en-tête, pas un objet flottant :
   il appartient au titre et s'en va avec lui.

   `absolute` et non `relative` : dans le flux, il pousserait le titre vers le
   bas et casserait l'alignement de tous les écrans. Le bloc conteneur est le
   bloc initial — ni `body` ni `.app` ne sont positionnés — donc `right` se
   calcule sur la largeur de la fenêtre, et `50% - 242px` continue de le caler
   sur le bord de la colonne de 460 px.

   Au-dessus du tiroir, et pas seulement à côté : l'écrou reste cliquable
   pendant que le panneau est ouvert, et sert donc aussi à le refermer. */
.gear{
  position:absolute; z-index:50;
  /* Deux déclarations, et l'ordre compte. `env()` n'existe pas partout ; là où
     il manque, tout le `calc()` devient invalide et le navigateur jette la
     déclaration entière. La première ligne est le filet. */
  top:22px;
  top:calc(10px + env(safe-area-inset-top));
  right:12px;
  right:max(12px, calc(50% - 242px));
  width:42px; height:42px; padding:0;
  display:flex; align-items:center; justify-content:center;
  border-radius:50%; cursor:pointer;
  border:1px solid var(--line);
  background:radial-gradient(circle at 34% 28%, rgba(46,35,24,.92) 0%, rgba(9,7,5,.88) 74%);
  -webkit-backdrop-filter:blur(10px);
  backdrop-filter:blur(10px);
  box-shadow:0 6px 20px rgba(0,0,0,.55);
  transition:border-color .15s ease, transform .25s ease;
}
.gear__ico{width:21px; height:21px; fill:var(--gold); opacity:.85;}
.gear:focus-visible,.sheet__close:focus-visible{
  outline:2px solid var(--gold); outline-offset:2px;
}
.gear:hover{border-color:rgba(201,162,74,.5);}
.gear:hover .gear__ico{opacity:1;}
/* Un quart de tour à l'ouverture, et rien d'autre : un engrenage qui tourne
   en boucle attirerait l'oeil pendant toute la partie, alors que le décor est
   fait pour qu'on regarde ailleurs. */
.gear.is-open{transform:rotate(90deg);}
.gear:active{transform:scale(.94);}
.gear.is-open:active{transform:rotate(90deg) scale(.94);}

/* Deux formes pour le même tiroir, et le défaut est celle du téléphone.

   Au doigt, c'est une feuille : elle monte du bas, occupe toute la largeur,
   garde le pouce au bord de l'écran — sur un grand téléphone, une boîte
   centrée demande de remonter la main jusqu'au milieu de la dalle pour
   atteindre un curseur. Elle se referme en la tirant vers le bas.

   À la souris, une feuille collée en bas d'un écran de vingt-sept pouces n'a
   plus de sens : le pointeur va où on veut, et le regard est au centre. Le
   tiroir y redevient une boîte centrée, sans poignée puisqu'on ne la tire pas.

   Le partage se fait sur le pointeur — même critère que `auClavier()` dans
   app.js — et non sur la largeur : une fenêtre réduite au quart d'un écran de
   bureau reste un bureau, un téléphone tenu à plat reste un téléphone. */
.sheet[hidden]{display:none;}
.sheet{
  position:fixed; inset:0; z-index:40;
  display:flex; align-items:flex-end; justify-content:center;
}
.sheet__veil{
  position:absolute; inset:0;
  background:rgba(3,5,9,.72);
  -webkit-backdrop-filter:blur(3px);
  backdrop-filter:blur(3px);
  animation:veil-in .26s ease-out;
}
.sheet__panel{
  position:relative;
  width:100%; max-width:520px;
  /* Deux déclarations, comme pour l'écrou : `vh` mesure la fenêtre barre
     d'adresse repliée, donc plus haut que ce qu'on voit vraiment tant qu'elle
     est déployée — la feuille dépasserait sous l'écran. `dvh` mesure ce qui
     est visible à l'instant ; là où il manque, la première ligne tient. */
  max-height:88vh;
  max-height:88dvh;
  display:flex; flex-direction:column;
  border:1px solid var(--line);
  /* Pas de bord en bas : la feuille sort de l'écran, elle ne s'y pose pas. */
  border-bottom:none;
  border-radius:14px 14px 0 0;
  background-image:var(--grain),
    linear-gradient(168deg, rgba(46,35,24,.94) 0%, rgba(20,15,10,.96) 48%, rgba(9,7,5,.98) 100%);
  background-size:180px 180px, 100% 100%;
  background-blend-mode:soft-light, normal;
  box-shadow:0 -8px 40px rgba(0,0,0,.7), 0 0 60px rgba(0,0,0,.5);
  animation:sheet-up .28s cubic-bezier(.22,.9,.26,1);
  /* Le retour en place après un glissement abandonné. La transition ne gêne
     pas l'animation d'ouverture : tant qu'une animation tourne, c'est elle qui
     tient la propriété. */
  transition:transform .2s ease;
}
/* Le panneau reçoit le focus à l'ouverture — pour les lecteurs d'écran et
   pour que la touche Échap parte de là. Ce focus-là est posé par le code, pas
   demandé par un doigt : il n'a pas à se voir. */
.sheet__panel:focus{outline:none;}

/* La poignée. Elle n'attrape rien à elle seule : c'est tout l'en-tête qui est
   glissant (voir `brancherGlissement`), sans quoi il faudrait viser trois
   millimètres de haut avec un pouce. */
.sheet__grab{
  flex:none; width:42px; height:4px; margin:10px auto 0;
  border-radius:999px; background:rgba(201,162,74,.4);
}

@keyframes sheet-up{
  from{transform:translateY(100%);}
  to  {transform:none;}
}
@keyframes veil-in{ from{opacity:0;} to{opacity:1;} }
@keyframes veil-out{ from{opacity:1;} to{opacity:0;} }

/* La fermeture est animée, et c'est la contrepartie du glissement : un panneau
   qu'on tire vers le bas et qui disparaît d'un coup à mi-chemin donne
   l'impression d'un plantage plutôt que d'un geste.

   La descente de la feuille n'est pas une `@keyframes` mais la transition
   ci-dessus, que `fermerReglages()` déclenche en posant `translateY(100%)`.
   Une animation aurait redémarré depuis zéro : la feuille tirée à cent pixels
   serait d'abord remontée d'un bond avant de redescendre. Une transition, elle,
   part d'où le doigt l'a laissée. Le voile, qui ne bouge pas, garde la sienne. */
.sheet.is-closing .sheet__veil{animation:veil-out .2s ease-in forwards;}

/* Au pointeur fin : une boîte centrée, et le vocabulaire du reste du site —
   angles nets, bord complet, pas de poignée. */
@media (hover:hover) and (pointer:fine){
  .sheet{align-items:center;}
  .sheet__grab{display:none;}
  .sheet__panel{
    max-width:460px;
    max-height:min(86vh, 720px);
    border:1px solid var(--line);
    border-radius:var(--radius);
    animation:sheet-in .22s ease-out;
  }
  .sheet.is-closing .sheet__panel{animation:sheet-out .16s ease-in forwards;}
  @keyframes sheet-in{
    from{opacity:0; transform:translateY(18px);}
    to  {opacity:1; transform:none;}
  }
  @keyframes sheet-out{
    from{opacity:1; transform:none;}
    to  {opacity:0; transform:translateY(12px);}
  }
}

@media (prefers-reduced-motion:reduce){
  .sheet__panel,.sheet__veil,
  .sheet.is-closing .sheet__panel,.sheet.is-closing .sheet__veil{animation:none;}
  .sheet__panel{transition:none;}
  .gear{transition:none;}
}

/* `touch-action:none` : sans lui, le navigateur interprète le glissement
   vertical comme un défilement de page et ne nous envoie plus de
   `pointermove` — la feuille resterait collée pendant qu'on tire dessus. */
.sheet__head{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:14px 18px 13px;
  border-bottom:1px solid var(--line-soft);
  flex:none; touch-action:none;
}
.sheet__title{
  margin:0; font-family:'Cinzel',Georgia,serif; font-weight:500;
  font-size:15px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--gold-hi);
}
.sheet__close{
  width:32px; height:32px; flex:none; padding:0; cursor:pointer;
  border:1px solid var(--line-soft); border-radius:2px;
  background:rgba(0,0,0,.3); color:var(--mist); font-size:13px; line-height:1;
}
.sheet__close:hover{border-color:rgba(201,162,74,.45); color:var(--parchment);}
/* `overscroll-behavior` : sans lui, arriver en bas du tiroir fait repartir la
   page de dessous, et le décor défile derrière un panneau immobile. */
.sheet__body{
  padding:18px; overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  /* Sur un téléphone couché, le tiroir est collé en bas : la barre d'accueil
     d'iOS mangerait la dernière ligne. */
  padding-bottom:calc(18px + env(safe-area-inset-bottom));
}
/* Le tiroir remplace les cartes : ses sections n'ont pas besoin d'un second
   cadre par-dessus celui du panneau. */
.sheet__body .label{margin-bottom:10px;}
.sheet__part + .sheet__part{margin-top:22px;}

/* Pendant que le tiroir est ouvert, la page de dessous ne défile plus : sur
   téléphone, un doigt qui rate le panneau emportait tout l'écran. */
.is-tiroir-ouvert{overflow:hidden;}

/* --- Lignes de réglage ----------------------------------------------------
   Un libellé à gauche, son contrôle à droite. Les interrupteurs et les touches
   partagent la même gouttière : sans elle, chaque réglage se plaçait à la
   largeur de son propre texte et la colonne de droite ondulait. */
.set-row{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding:12px 0; border-bottom:1px solid var(--line-soft);
}
.set-row:last-of-type{border-bottom:none;}
.set-row__txt{min-width:0; font-size:14px; color:var(--parchment);}
.set-row__aide{
  display:block; font-size:11.5px; color:var(--mist); line-height:1.45; margin-top:3px;
}

/* --- Choix d'un appareil audio --------------------------------------------
   Le libellé au-dessus et la liste dessous, à l'inverse des `set-row`. Un nom
   d'appareil est long — « Casque avec micro (Realtek(R) Audio) » — et dans la
   gouttière de droite il se serait retrouvé tronqué au tiers, c'est-à-dire
   illisible là où c'est précisément le nom qu'on vient lire. */
.pick-row{padding:12px 0; border-bottom:1px solid var(--line-soft);}
.pick-row:last-of-type{border-bottom:none;}
.pick-row__lab{display:block; font-size:14px; color:var(--parchment); margin-bottom:8px;}
.pick-row__aide{
  display:block; font-size:11.5px; color:var(--mist); line-height:1.45; margin-top:7px;
}
/* `appearance:none` retirerait aussi la flèche, et un menu déroulant sans
   flèche ne se lit plus comme un menu déroulant : on garde le chevron du
   système et on ne repeint que le fond. 16px sur mobile, sinon Safari zoome
   la page à l'ouverture de la liste. */
.pick{
  width:100%; max-width:100%; color:var(--parchment);
  background:rgba(8,6,4,.66); border:1px solid var(--line);
  padding:11px 12px; border-radius:2px;
  font-family:'Jost',sans-serif; font-size:16px;
  box-shadow:inset 0 2px 9px rgba(0,0,0,.55);
}
.pick:focus{border-color:rgba(201,162,74,.55); outline:none;}
/* Le menu déroulé est peint par le système : sans couleur explicite, un thème
   clair y écrirait du parchemin sur du blanc. */
.pick option{background:#17120c; color:var(--parchment);}

/* Capuchon de touche. Le monospace n'est pas décoratif : « I » et « l » se
   distinguent, et c'est exactement ce qu'on vient vérifier sur ce bouton. */
.key{
  flex:none; min-width:74px; padding:8px 12px; cursor:pointer;
  font-family:'JetBrains Mono',monospace; font-size:12px; letter-spacing:.04em;
  color:var(--parchment); text-align:center;
  border:1px solid var(--line); border-radius:3px;
  background:linear-gradient(180deg, rgba(46,35,24,.7), rgba(14,11,8,.6));
  box-shadow:inset 0 1px 0 rgba(240,215,154,.14), 0 2px 0 rgba(0,0,0,.5);
}
.key:hover{border-color:rgba(201,162,74,.5);}
.key:active{transform:translateY(1px); box-shadow:inset 0 1px 0 rgba(240,215,154,.1);}
.key:focus-visible{outline:2px solid var(--gold); outline-offset:2px;}
/* En attente d'une frappe. Le clignotement dit « c'est à toi », et il s'arrête
   de lui-même : la capture ne dure que jusqu'à la touche suivante. */
.key.is-capturing{
  color:var(--gold-hi); border-color:var(--gold);
  animation:key-wait 1.1s ease-in-out infinite;
}
@keyframes key-wait{
  0%,100%{box-shadow:inset 0 0 0 1px rgba(201,162,74,.3), 0 0 0 rgba(201,162,74,0);}
  50%    {box-shadow:inset 0 0 0 1px rgba(201,162,74,.6), 0 0 18px rgba(201,162,74,.35);}
}
@media (prefers-reduced-motion:reduce){
  .key.is-capturing{animation:none; box-shadow:inset 0 0 0 1px rgba(201,162,74,.6);}
}

/* Le rappel du raccourci sur les boutons du micro. Discret : c'est une aide,
   pas l'étiquette du bouton. */
.btn__key{
  display:inline-block; margin-left:8px; padding:1px 6px 2px;
  font-family:'JetBrains Mono',monospace; font-size:9.5px; letter-spacing:.02em;
  border:1px solid rgba(240,232,214,.28); border-radius:2px;
  opacity:.72; vertical-align:1px;
}

/* La sortie, au fond du tiroir : détachée du reste par un filet, pour qu'on ne
   la rencontre jamais en cherchant un volume. */
.sheet__part--sortie{
  margin-top:26px; padding-top:20px;
  border-top:1px solid var(--line-soft);
}
.sheet__part--sortie .label{color:var(--blood-hi);}
