/* ==========================================================================
   KarstGames — Système de design
   Direction : "dossier d'enquête" — fond encre, papier crème, tampon ambre.
   Les jeux du site tournent tous autour d'une identité cachée : l'interface
   emprunte au vocabulaire du dossier de police (fiches, tampons, code, écarts
   de police typographique) plutôt qu'à celui d'une app générique.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');

:root {
  /* Couleurs */
  --ink-950: #0F1119;
  --ink-900: #171B27;
  --ink-800: #1F2433;
  --ink-700: #2A2F42;
  --line: #383F56;
  --line-soft: #2A2F42;

  --paper: #EDE6D6;
  --paper-dim: #B9B2A0;
  --paper-faint: #7E7A6E;

  --amber: #E8A33D;
  --amber-dim: #C98A2E;
  --amber-ink: #3A2A10;

  --red: #C1443C;
  --red-dim: #9C332C;

  --green: #6E9B6B;
  --green-dim: #557A53;

  /* Le bleu ne sert qu'à une chose : dire qu'un jeu est en chantier. Désaturé
     comme les autres — un bleu vif sur ce fond encre attirerait plus l'œil que
     l'ambre, et une mention « Bêta » n'a pas à passer devant « Créer ». */
  --blue: #5E93B8;
  --blue-dim: #46738F;

  /* Et le violet ne sert qu'à une chose : dire qu'un jeu est à l'essai, et
     qu'il peut disparaître. Un cran plus fort que le bleu de la bêta, parce
     qu'il annonce quelque chose de plus — mais toujours moins que l'ambre, qui
     reste ce qu'on doit regarder en premier. */
  --violet: #8A6FBF;
  --violet-dim: #6B4F9E;

  /* Typo */
  --font-display: 'Fraunces', 'Iowan Old Style', serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'IBM Plex Mono', 'SF Mono', monospace;

  /* Échelle */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  --radius-s: 6px;
  --radius-m: 12px;
  --radius-l: 20px;

  --shadow-card: 0 12px 32px rgba(10, 12, 20, 0.45);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--ink-950);
  color: var(--paper);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

body {
  background-image:
    radial-gradient(circle at 15% 0%, rgba(232, 163, 61, 0.08), transparent 45%),
    radial-gradient(circle at 85% 100%, rgba(193, 68, 60, 0.07), transparent 40%);
  background-attachment: fixed;
}

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; margin: 0; letter-spacing: -0.01em; }
p { margin: 0; line-height: 1.5; }
a { color: inherit; }

.wrap {
  max-width: 480px;
  margin: 0 auto;
  padding: var(--space-5) var(--space-4) var(--space-8);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* -------------------------------------------------------------------- */
/* En-tête de marque                                                    */
/* -------------------------------------------------------------------- */

.brand {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-6);
}

.brand__mark {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.brand__mark b { color: var(--amber); font-weight: 600; }
/* L'en-tête est déjà une rangée espacée : l'étiquette n'y ajoute pas sa marge. */
.brand__mark .tag { margin-left: 0; }

.brand__stamp {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--paper-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
}

/* Sur la home, l'étiquette mène à l'espace administrateur. Elle garde son air
   d'ornement — c'est le but : rien n'annonce une porte — mais elle doit se
   toucher au doigt, d'où la zone plus généreuse que les 3px du tampon inerte. */
.brand__stamp--link {
  display: inline-block;
  padding: 7px 12px;
  margin: -4px -2px -4px 0;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.brand__stamp--link:hover,
.brand__stamp--link:focus-visible {
  color: var(--amber);
  border-color: var(--amber);
}

.brand__stamp--link:active { color: var(--amber); }

/* -------------------------------------------------------------------- */
/* Carte "dossier" — l'élément signature du site                        */
/* Bord perforé + languette de tampon, réutilisée pour : la home,       */
/* les révélations de rôle, les résultats, les fiches de mots admin.    */
/* -------------------------------------------------------------------- */

.dossier {
  position: relative;
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: var(--space-6) var(--space-5) var(--space-5);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.dossier::before {
  /* liseré perforé en haut, façon fiche détachable */
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background-image: repeating-linear-gradient(90deg, var(--line) 0 6px, transparent 6px 12px);
}

.dossier__eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: var(--space-2);
  display: block;
}

.dossier__title {
  font-size: 28px;
  line-height: 1.15;
  color: var(--paper);
}

.dossier__title--lg { font-size: 34px; }

.dossier__body {
  color: var(--paper-dim);
  font-size: 15px;
  margin-top: var(--space-3);
}

.stamp {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--red);
  border: 1.5px solid var(--red);
  border-radius: var(--radius-s);
  padding: 4px 8px;
  transform: rotate(4deg);
  opacity: 0.85;
}

/* -------------------------------------------------------------------- */
/* Boutons                                                              */
/* -------------------------------------------------------------------- */

.btn {
  appearance: none;
  border: none;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 16px;
  border-radius: var(--radius-m);
  padding: 15px 20px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  transition: transform 0.12s ease, filter 0.12s ease, background 0.12s ease;
  width: 100%;
}
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn--primary { background: var(--amber); color: var(--amber-ink); }
.btn--primary:hover:not(:disabled) { filter: brightness(1.08); }

.btn--ghost { background: transparent; color: var(--paper); border: 1px solid var(--line); }
.btn--ghost:hover:not(:disabled) { border-color: var(--paper-dim); }

.btn--danger { background: var(--red); color: var(--paper); }
.btn--danger:hover:not(:disabled) { filter: brightness(1.08); }

.btn--sm { padding: 10px 14px; font-size: 14px; width: auto; }

/* -------------------------------------------------------------------- */
/* Champs                                                               */
/* -------------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }

.field__label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-faint);
}

.input {
  background: var(--ink-900);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 16px;
  padding: 13px 14px;
  width: 100%;
}
.input:focus { outline: none; border-color: var(--amber); }
.input--code {
  font-family: var(--font-mono);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-align: center;
  font-size: 22px;
}

/* -------------------------------------------------------------------- */
/* Divers                                                               */
/* -------------------------------------------------------------------- */

/* Carte de jeu de l'accueil.
   Le cadre porte DEUX boutons — créer la partie, et lire les règles — donc il
   ne peut pas être un bouton lui-même : un bouton ne s'imbrique pas dans un
   autre. C'est `.game-card__go` qui prend tout ce qui déclenche la création. */
.game-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 14px 16px;
}
.game-card.is-soon { opacity: 0.5; }

/* Le halo d'un jeu en chantier.
   Il RESPIRE plutôt que d'être fixe : une lueur immobile se lit comme une
   sélection — « c'est celui-là qu'il faut prendre » — alors qu'un souffle lent
   se lit comme un état. Lent (5 s) et de faible amplitude, parce que cette
   carte reste à l'écran tant qu'on choisit, et qu'un clignotement dans une
   liste de neuf jeux fatiguerait avant d'informer.

   C'est une boucle d'ambiance, l'une des trois familles d'animation sûres du
   site : ni début ni fin visibles, donc un redessin ne se remarque pas. */
.game-card.is-beta {
  border-color: var(--blue-dim);
  animation: kg-aura 5s ease-in-out infinite;
}

/* L'alpha respire aussi, mais en violet, et un peu plus fort : elle dit une
   chose de plus que la bêta — non seulement il reste des bugs, mais ce jeu est
   à l'essai et peut disparaître. Deux promesses différentes, deux couleurs. */
.game-card.is-alpha {
  border-color: var(--violet-dim);
  animation: kg-aura-alpha 5s ease-in-out infinite;
}

@keyframes kg-aura {
  0%, 100% { box-shadow: var(--shadow-card), 0 0 0 1px rgba(94, 147, 184, 0.16), 0 0 14px -4px rgba(94, 147, 184, 0.30); }
  50%      { box-shadow: var(--shadow-card), 0 0 0 1px rgba(94, 147, 184, 0.34), 0 0 26px -2px rgba(94, 147, 184, 0.55); }
}

@keyframes kg-aura-alpha {
  0%, 100% { box-shadow: var(--shadow-card), 0 0 0 1px rgba(138, 111, 191, 0.18), 0 0 16px -4px rgba(138, 111, 191, 0.34); }
  50%      { box-shadow: var(--shadow-card), 0 0 0 1px rgba(138, 111, 191, 0.40), 0 0 30px -2px rgba(138, 111, 191, 0.62); }
}

@media (prefers-reduced-motion: reduce) {
  /* Le halo reste, il cesse seulement de respirer : c'est une information, pas
     une décoration. */
  .game-card.is-beta {
    animation: none;
    box-shadow: var(--shadow-card), 0 0 0 1px rgba(94, 147, 184, 0.30), 0 0 22px -3px rgba(94, 147, 184, 0.45);
  }
  .game-card.is-alpha {
    animation: none;
    box-shadow: var(--shadow-card), 0 0 0 1px rgba(138, 111, 191, 0.34), 0 0 24px -3px rgba(138, 111, 191, 0.50);
  }
}

/* Un bouton n'hérite pas de la couleur de son parent : il prend celle du
   navigateur. Sans cette règle, le nom du jeu s'affichait en noir sur un
   ordinateur et en bleu système sur iOS — le même oubli, deux valeurs par
   défaut différentes. */
.game-card__go {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
  background: none;
  border: none;
  padding: 4px 0;
  color: var(--paper);
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.game-card__go:disabled { cursor: default; }
.game-card__go:focus-visible { outline: 2px solid var(--amber); outline-offset: 4px; border-radius: var(--radius-s); }

/* `min-width: 0` sur les deux : sans lui, un élément de flex refuse de passer
   sous la largeur de son contenu et la longue accroche pousse la pastille
   « Créer » hors de la carte sur un petit téléphone. */
.game-card__text { flex: 1; min-width: 0; }
.game-card__name {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
}
.game-card__tagline { display: block; color: var(--paper-dim); font-size: 13px; margin-top: 2px; }
.game-card__meta {
  display: block;
  color: var(--paper-faint);
  font-family: var(--font-mono);
  font-size: 11px;
  margin-top: 5px;
}

/* Le « ? » : rond, discret, et assez large pour un pouce. En retrait de la
   pastille « Créer », qui reste l'action de cette page. */
.game-card__help {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--paper-faint);
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.game-card__help:hover { color: var(--paper); border-color: var(--paper-dim); }
.game-card__help:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--paper-faint);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: var(--space-5) 0;
}
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--line); }

.player-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line-soft);
}
.player-row:last-child { border-bottom: none; }
.player-row__name { font-weight: 600; flex: 1; }
.player-row__tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--paper-faint);
  text-transform: uppercase;
}

/* Le cadre d'un personnage. Il porte la pastille crème et la découpe ronde ;
   l'illustration, elle, est transparente et se contente d'être centrée dedans.
   Elle est CONTENUE, pas rognée : ces dessins vont jusqu'au bord de leur carré
   (antennes du robot, pointes du chapeau de pirate) et un détourage circulaire
   net les amputerait. D'où la marge, qui rentre le carré dans le disque. */
.avatar-img {
  width: var(--avatar-size, 44px);
  height: var(--avatar-size, 44px);
  border-radius: 50%;
  background: var(--paper);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
  overflow: hidden;
}
/* La marge se prend sur l'image, pas en `padding` sur le cadre : un padding en
   pourcentage se calcule sur la largeur du PARENT, pas sur la sienne. Dans une
   ligne de joueur large de 400 px, 6 % faisaient 24 px de chaque côté d'un
   cadre de 36 px — l'image tombait à zéro pixel et il ne restait que le disque
   crème. Ici les 88 % se rapportent bien au cadre. */
.avatar-img img {
  width: 88%;
  height: 88%;
  object-fit: contain;
  display: block;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--paper-dim);
}
.pill--amber { border-color: var(--amber-dim); color: var(--amber); }
.pill--red { border-color: var(--red-dim); color: var(--red); }
.pill--green { border-color: var(--green-dim); color: var(--green); }
.pill--blue { border-color: var(--blue-dim); color: var(--blue); }

/* Une étiquette collée à un titre — plus petite qu'une pastille, qui est une
   action ou un statut de ligne. Elle se lit avec le nom, pas à côté. */
.tag {
  display: inline-block;
  vertical-align: middle;
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid currentColor;
  /* Le nom du jeu est en display 18 px : sans ça l'étiquette hériterait de son
     interlignage et décalerait toute la ligne. */
  line-height: 1.4;
}
.tag--beta { color: var(--blue); }
.tag--alpha { color: var(--violet); }

.center-msg {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  text-align: center;
  color: var(--paper-dim);
  padding: var(--space-6) 0;
}

.spinner {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 2.5px solid var(--line);
  border-top-color: var(--amber);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Le toast — la même précaution que .net-banner et .host-banner, qu'il était
   le seul à ne pas prendre.

   Il se cachait par une simple translation de 120 % de SA PROPRE HAUTEUR, soit
   une cinquantaine de pixels, alors qu'il lui fallait franchir sa hauteur PLUS
   les 24 px de `bottom` pour sortir du cadre. Il restait donc en permanence à
   cheval sur le bord bas de l'écran — un bandeau tronqué visible pendant toute
   la partie. Et faute de `pointer-events: none`, cette lisière invisible
   avalait les touchers qui la visaient.

   D'où `visibility`, une translation qui tient compte de l'écart au bord, et
   un élément définitivement transparent au doigt. */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%) translateY(calc(100% + var(--space-5) + 12px));
  background: var(--ink-800);
  border: 1px solid var(--line);
  color: var(--paper);
  font-size: 14px;
  padding: 12px 18px;
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-card);
  visibility: hidden;
  opacity: 0;
  transition: transform 0.25s ease, opacity 0.2s ease, visibility 0s linear 0.25s;
  z-index: 50;
  max-width: 90vw;
  pointer-events: none;
}
.toast.show {
  transform: translateX(-50%) translateY(0);
  visibility: visible;
  opacity: 1;
  transition-delay: 0s;
}

/* Bandeau de reconnexion — posé par scripts/live.js.
   Il ne remplace jamais la partie à l'écran : sur téléphone une coupure dure
   quelques secondes et se répare toute seule, ce serait absurde de renvoyer le
   joueur sur un écran d'erreur à chaque fois qu'il déverrouille son écran. */
.net-banner {
  position: fixed;
  top: 0; left: 50%;
  transform: translateX(-50%) translateY(-140%);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: calc(var(--space-2) + env(safe-area-inset-top, 0px));
  background: var(--ink-800);
  border: 1px solid var(--amber-dim);
  color: var(--paper-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  padding: 8px 14px;
  border-radius: 999px;
  box-shadow: var(--shadow-card);
  /* Même précaution que pour .host-banner : ne jamais compter sur la seule
     translation pour faire disparaître un élément. */
  visibility: hidden;
  transition: transform 0.25s ease, visibility 0s linear 0.25s;
  z-index: 60;
  pointer-events: none;
}
.net-banner.is-on {
  transform: translateX(-50%) translateY(0);
  visibility: visible;
  transition-delay: 0s;
}
.net-banner__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--amber);
  animation: pulse 1.2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: 0.25; } }

/* Demande de reprise de la main — posé par scripts/host.js.
   En bas plutôt qu'en haut : le bandeau de reconnexion occupe déjà le haut de
   l'écran, et les deux peuvent très bien apparaître ensemble — c'est même
   quand le réseau flanche qu'on croit l'hôte parti. Placé au-dessus du bouton
   « Ma carte » et du toast, il n'en recouvre aucun. */
.host-banner {
  position: fixed;
  left: 50%;
  bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%) translateY(160%);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: min(92vw, 420px);
  background: var(--ink-800);
  border: 1px solid var(--amber-dim);
  color: var(--paper-dim);
  font-size: 13px;
  line-height: 1.4;
  padding: 12px 14px;
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-card);
  /* `visibility` plutôt qu'une simple translation : un bandeau court ne sort
     pas de l'écran en se translatant de sa propre hauteur, et resterait
     affiché en barre vide. */
  visibility: hidden;
  opacity: 0;
  transition: transform 0.25s ease, opacity 0.2s ease, visibility 0s linear 0.25s;
  z-index: 55;
}
.host-banner.is-on {
  transform: translateX(-50%) translateY(0);
  visibility: visible;
  opacity: 1;
  transition-delay: 0s;
}
.host-banner b { color: var(--paper); }
.host-banner .btn { width: auto; flex-shrink: 0; }

@media (prefers-reduced-motion: reduce) {
  .btn, .toast, .net-banner, .host-banner { transition: none; }
  .spinner { animation-duration: 1.6s; }
  .net-banner__dot { animation: none; }
}

.grid-avatars {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-2);
}
.avatar-pick {
  border: 2px solid transparent;
  border-radius: 50%;
  padding: 2px;
  cursor: pointer;
  background: none;
  line-height: 0;
  transition: transform 0.15s ease;
  /* Sans ça le bouton s'étire à la largeur de sa colonne alors que l'avatar
     est carré : `border-radius: 50%` dessinerait alors un ovale au lieu d'un
     anneau. On le laisse à la taille de son contenu, centré dans la case. */
  justify-self: center;
}
.avatar-pick:active { transform: scale(0.92); }
.avatar-pick.is-selected { border-color: var(--amber); }
/* Le personnage choisi se présente d'un petit ressort. C'est le seul moment
   où l'on regarde vraiment ces dessins : autant que le geste réponde. */
.avatar-pick.is-selected .avatar-img { animation: kg-pop 0.34s cubic-bezier(.2,.9,.3,1.4); }

/* ==========================================================================
   Proposer un mot depuis le lobby
   Le panneau se déplie SOUS les réglages et au-dessus de rien : replié, il ne
   pèse qu'un bouton discret, pour ne pas s'interposer entre l'hôte et son
   bouton « Lancer la partie ».
   ========================================================================== */

.propose-card { border-style: dashed; }

.propose-hint {
  color: var(--paper-dim);
  font-size: 13px;
  line-height: 1.6;
  margin: 10px 0 16px;
}

.propose-area {
  min-height: 120px;
  resize: vertical;
  font-family: var(--font-body);
  line-height: 1.5;
}

.propose-actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.propose-actions .btn { width: 100%; }

.propose-note {
  color: var(--paper-faint);
  font-size: 12px;
  line-height: 1.6;
  margin: 12px 0 0;
}

/* ==========================================================================
   Animations
   ⚠ L'écran entier est reconstruit à chaque message reçu du salon. Une
   animation « à l'apparition » rejouerait donc au moindre geste de n'importe
   quel joueur, ce qui devient vite insupportable sur un téléphone posé au
   milieu de la table. On s'en tient à trois familles sûres :
     · celles déclenchées par un geste du joueur lui-même (:active, sélection) ;
     · les boucles d'ambiance, qui n'ont ni début ni fin visibles ;
     · celles qu'un jeu arme explicitement — classe `kg-enter` — et seulement
       quand la valeur affichée a réellement changé.
   ========================================================================== */

@keyframes kg-pop {
  0% { transform: scale(0.86); }
  60% { transform: scale(1.07); }
  100% { transform: scale(1); }
}

/* Une entrée franche mais brève : on annonce du nouveau, on ne fait pas
   attendre. Posée par le jeu, jamais par le rendu. */
@keyframes kg-rise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.kg-enter { animation: kg-rise 0.32s cubic-bezier(.2,.8,.2,1) both; }

/* Boucle d'ambiance : « c'est à toi ». Un téléphone qu'on ne regarde que du
   coin de l'œil doit pouvoir le dire sans qu'on lise quoi que ce soit. */
@keyframes kg-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(232, 163, 61, 0); }
  50% { box-shadow: 0 0 0 4px rgba(232, 163, 61, 0.16); }
}
.is-turn { border-color: var(--amber-dim); animation: kg-breathe 2.6s ease-in-out infinite; }

/* Le tampon qui s'abat sur le dossier — le verdict d'une partie.

   C'est l'élément lui-même qui tombe, et non un effet peint par-dessus : un
   tampon, ça porte un texte, et le redessiner sur une toile pour l'agrandir
   reviendrait à le dessiner deux fois. Il arrive de loin, trop gros, et vient
   se poser exactement sur sa zone — avec le petit dépassement d'un geste qui
   appuie. Rien d'autre : ni onde de choc, ni éclaboussure.

   Armé par le jeu, jamais par le rendu : la classe n'est posée qu'une fois,
   sinon le tampon retomberait à chaque message reçu du salon. */
/* L'amplitude est bornée par le cadre : la carte est en `overflow: hidden`, et
   au-delà de deux fois sa taille le tampon se fait rogner par le bord au lieu
   d'arriver de loin. */
@keyframes kg-stamp {
  0%   { transform: rotate(-13deg) scale(2); opacity: 0; }
  55%  { transform: rotate(4deg) scale(0.94); opacity: 1; }
  75%  { transform: rotate(4deg) scale(1.04); }
  100% { transform: rotate(4deg) scale(1); opacity: 0.85; }
}
.stamp.kg-stamped { animation: kg-stamp 0.5s cubic-bezier(.3,.9,.4,1) both; }

/* La toile des effets d'écran — posée par scripts/fx.js, une seule par page.
   Elle vit sur <body> et non dans #app : c'est ce qui lui permet de survivre
   aux redessins, qui remplacent l'écran entier à chaque message du salon. Même
   raison que pour le bandeau de reprise de main.

   Au-dessus du jeu (statique), sous tout le mobilier fixe : carte secrète 40,
   feuille 45, toast 50, reprise de main 55, reconnexion 60. Et transparente au
   doigt de bout en bout : un effet ne doit jamais coûter une place. */
.fx-layer {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 30;
  pointer-events: none;
  touch-action: none;
}

@media (prefers-reduced-motion: reduce) {
  .avatar-pick, .avatar-pick.is-selected .avatar-img,
  .kg-enter, .is-turn, .stamp.kg-stamped { animation: none; transition: none; }
  /* Par sécurité : fx.js ne joue déjà rien dans ce mode. */
  .fx-layer { display: none; }
}

footer.note {
  margin-top: auto;
  padding-top: var(--space-6);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--paper-faint);
  letter-spacing: 0.04em;
}
footer.note a { color: var(--paper-dim); text-decoration: underline; }
/* Le crédit des illustrations. La licence Flaticon demande qu'il soit visible
   là où les dessins servent — donc sur toutes les pages — mais il n'a aucune
   raison de peser autant que le reste du pied de page. */
footer.note .credit { color: var(--paper-faint); text-decoration: none; }
footer.note .credit:hover { text-decoration: underline; }

/* ==========================================================================
   Briques communes aux jeux
   Tous les jeux du site partagent la même colonne vertébrale : on annonce la
   phase en cours, on retourne une carte pour découvrir son rôle, on vote, on
   compare les scores. Ces blocs vivent donc ici et non dans un jeu précis.
   ========================================================================== */

/* --- Fil des phases ---------------------------------------------------- */

.phase-strip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.phase-strip::-webkit-scrollbar { display: none; }
.phase-strip__step {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-faint);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: 4px 10px;
  white-space: nowrap;
  flex-shrink: 0;
}
.phase-strip__step.is-done { color: var(--paper-dim); border-color: var(--line); }
.phase-strip__step.is-current {
  color: var(--amber-ink);
  background: var(--amber);
  border-color: var(--amber);
}

/* --- Ajouter à l'écran d'accueil --------------------------------------- */
/* Posée par scripts/pwa.js, dans le flux de l'accueil et jamais par-dessus :
   le bas de l'écran appartient déjà au toast, à « Ma carte » et à « Règles ».
   Bord tiret comme le panneau de propositions — c'est une suggestion, pas une
   partie du site. */

.install-card {
  border-style: dashed;
  margin-bottom: 24px;
}
.install-card__body {
  color: var(--paper-dim);
  font-size: 14px;
  line-height: 1.6;
  margin-top: 10px;
}
.install-card__body b { color: var(--paper); }
.install-card__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
/* Le chemin de retour, annoncé sur la carte elle-même : sans cette ligne, un
   « non merci » a l'air définitif — et il l'était. */
.install-card__note {
  color: var(--paper-faint);
  font-size: 12px;
  line-height: 1.5;
  margin-top: var(--space-3);
}

/* Sous-titre d'une rubrique de l'accueil. */
.section-note {
  color: var(--paper-faint);
  font-size: 13px;
  padding-left: 4px;
  margin-top: 4px;
}

/* --- Carte de partage du salon (code + QR) ----------------------------- */

.share-card { text-align: center; }
.share-card__code {
  font-family: var(--font-mono);
  font-size: 40px;
  font-weight: 600;
  letter-spacing: 0.22em;
  /* La lettre-espacement décale le texte vers la droite : on compense pour
     que le code reste optiquement centré sous son intitulé. */
  text-indent: 0.22em;
  color: var(--paper);
  margin: var(--space-2) 0 var(--space-4);
}
.share-qr {
  display: inline-block;
  line-height: 0;
  border-radius: var(--radius-m);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.share-qr svg { display: block; max-width: 100%; height: auto; }
.share-card__hint {
  color: var(--paper-faint);
  font-size: 13px;
  margin: var(--space-3) 0 var(--space-4);
}

/* --- Carte secrète, rappelable à tout moment --------------------------- */

.secret-fab {
  position: fixed;
  right: var(--space-4);
  /* Au-dessus de la barre système des navigateurs mobiles. */
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--amber);
  color: var(--amber-ink);
  border: none;
  border-radius: 999px;
  padding: 12px 18px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  z-index: 40;
}
.secret-fab:active { transform: scale(0.97); }

.secret-sheet {
  position: fixed;
  inset: 0;
  background: rgba(15, 17, 25, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  z-index: 45;
}
.secret-sheet__panel {
  width: 100%;
  max-width: 330px;
  text-align: center;
}
/* La carte de la feuille modale est la même que celle des trois jeux, juste
   un peu plus courte pour laisser respirer le bouton en dessous. */
.flip-card--sheet { height: 230px; margin: 0 0 var(--space-4); cursor: pointer; }

/* --- Les règles, à portée de main -------------------------------------- */
/* Posées par scripts/rules.js. Le texte vit dans ce module et nulle part
   ailleurs : c'est le seul fichier à toucher pour corriger une règle.

   La liste à puces vivait jusqu'ici recopiée à l'identique dans sept CSS de
   jeu, pour un bloc de lobby que le panneau remplace désormais. Elle reste
   ici parce que le panneau s'en sert, et elle n'est plus écrite qu'une fois. */

.rules {
  margin: var(--space-3) 0 0;
  padding-left: 18px;
  color: var(--paper-dim);
  font-size: 14px;
  line-height: 1.55;
}
.rules li { margin-bottom: 8px; }
.rules li:last-child { margin-bottom: 0; }
.rules b { color: var(--paper); }

/* Le bouton qui ne quitte jamais l'écran pendant une partie.
   À GAUCHE : « Ma carte » occupe déjà le coin droit dans plusieurs jeux, et
   les deux doivent pouvoir cohabiter. Volontairement en retrait — c'est un
   recours, pas l'action principale de l'écran. */
.rules-fab {
  position: fixed;
  left: var(--space-4);
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--ink-800);
  color: var(--paper-dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 11px 16px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  z-index: 40;
}
.rules-fab:active { transform: scale(0.97); }
.rules-fab:hover { border-color: var(--paper-faint); color: var(--paper); }
.rules-fab:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.rules-sheet {
  position: fixed;
  inset: 0;
  background: rgba(15, 17, 25, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  z-index: 46;
}
.rules-sheet[hidden] { display: none; }

.rules-sheet__panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 440px;
  /* Le panneau ne dépasse jamais l'écran : c'est son corps qui défile, pas la
     page du dessous. Sans ça, arriver au bas du texte sur un téléphone rend la
     main à la partie et on perd sa lecture. */
  max-height: min(84dvh, 720px);
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.rules-sheet__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--line-soft);
  flex-shrink: 0;
}
.rules-sheet__head h2 { font-size: 22px; color: var(--paper); }

.rules-sheet__close {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 50%;
  width: 32px;
  height: 32px;
  color: var(--paper-dim);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.rules-sheet__close:hover { color: var(--paper); border-color: var(--paper-dim); }
.rules-sheet__close:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.rules-sheet__body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--space-4) var(--space-5) var(--space-5);
}

.rules-pitch {
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.35;
  color: var(--paper);
}

.rules-sec { margin-top: var(--space-5); }
.rules-sec h3 {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: var(--space-2);
}
.rules-sec p {
  color: var(--paper-dim);
  font-size: 14px;
  line-height: 1.55;
}
.rules-sec p b { color: var(--paper); }

/* Le barème. Même trame que .score-row, dont il est le pendant écrit. */
.rules-score { margin-top: var(--space-2); }
.rules-score__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
  color: var(--paper-dim);
  font-size: 14px;
}
.rules-score__row:last-child { border-bottom: none; }
.rules-score__row b {
  font-family: var(--font-mono);
  color: var(--amber);
  flex-shrink: 0;
}

.rules-note {
  color: var(--paper-faint);
  font-size: 12.5px;
  line-height: 1.6;
  margin-top: var(--space-3);
}
.rules-note b { color: var(--paper-dim); }

/* La page du dessous ne défile plus tant que les règles sont ouvertes. */
body.rules-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .rules-fab { transition: none; }
}

/* --- Compte à rebours -------------------------------------------------- */

.countdown {
  font-family: var(--font-mono);
  font-size: 46px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--paper);
  margin: var(--space-3) 0 var(--space-2);
  font-variant-numeric: tabular-nums;
}
.countdown.is-urgent { color: var(--red); }
/* Variante compacte, glissée dans le bandeau « à qui le tour ». */
.countdown--inline { font-size: 24px; margin: 0; flex-shrink: 0; }

/* --- Carte à retourner (révélation de rôle) ---------------------------- */

.flip-card {
  position: relative;
  height: 220px;
  perspective: 1200px;
  margin: var(--space-5) 0;
}
.flip-card__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.5s cubic-bezier(.2,.8,.2,1);
  transform-style: preserve-3d;
}
.flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }
.flip-card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: var(--radius-l);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-5);
}
.flip-card__face--front {
  background: repeating-linear-gradient(135deg, var(--ink-800) 0 14px, var(--ink-700) 14px 28px);
}
.flip-card__face--back {
  background: var(--ink-800);
  transform: rotateY(180deg);
}

/* --- Vote -------------------------------------------------------------- */

.vote-list { display: flex; flex-direction: column; gap: 8px; margin: var(--space-4) 0; }
.vote-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: var(--ink-800);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-m);
  padding: 12px 14px;
  color: var(--paper);
  cursor: pointer;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
}
.vote-option.is-selected { border-color: var(--red); background: rgba(193,68,60,0.12); }
.vote-option:disabled { opacity: 0.5; cursor: not-allowed; }
.vote-option__done {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--green);
  font-weight: 500;
}

/* --- Scores ------------------------------------------------------------ */

.score-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.score-row:last-child { border-bottom: none; }
.score-row__pts { font-family: var(--font-mono); font-weight: 600; color: var(--amber); }
.score-row__rank {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--paper-faint);
  width: 14px;
  flex-shrink: 0;
}

/* --- Réglages de salon ------------------------------------------------- */

.settings-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--line-soft); gap: 12px; }
.settings-row:last-child { border-bottom: none; }
.stepper { display: flex; align-items: center; gap: 10px; }
.stepper b { font-family: var(--font-mono); min-width: 46px; text-align: center; }
.stepper button {
  width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--ink-900); color: var(--paper);
  font-size: 16px; cursor: pointer; line-height: 1;
  flex-shrink: 0;
}
.switch {
  width: 42px; height: 24px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--ink-900); position: relative; cursor: pointer; flex-shrink: 0;
}
.switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--paper-dim); transition: transform .15s ease;
}
.switch.is-on { background: var(--amber-dim); border-color: var(--amber); }
.switch.is-on::after { transform: translateX(18px); background: var(--amber-ink); }

@media (prefers-reduced-motion: reduce) {
  .flip-card__inner, .switch::after { transition: none; }
}

/* --------------------------------------------------------------------- */
/* Le nuancier — posé par scripts/palette.js                             */
/* Deux jeux attribuent une couleur à chacun, pour la même raison : savoir */
/* qui a fait quoi sans lire un nom. Le curseur de Wavelength, le trait du */
/* Faussaire.                                                             */
/* --------------------------------------------------------------------- */

.palette {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: var(--space-3);
}

/* Assez large pour un pouce : c'est un bouton, pas une pastille de légende. */
.swatch {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 2px solid transparent;
  background: var(--sw);
  cursor: pointer;
  padding: 0;
  transition: transform .12s ease, box-shadow .12s ease;
}
.swatch:active { transform: scale(0.9); }
/* La sienne se reconnaît à son cerne, pas à sa taille : une pastille plus
   grosse bousculerait toute la ligne à chaque changement d'avis. */
.swatch.is-mine {
  border-color: var(--paper);
  box-shadow: 0 0 0 3px var(--ink-950), 0 0 0 5px var(--sw);
}
/* Prise par quelqu'un d'autre : barrée et éteinte, mais toujours là — savoir
   quelle couleur a déjà été choisie fait partie de l'information. */
.swatch.is-taken { opacity: 0.3; cursor: not-allowed; position: relative; }
.swatch.is-taken::after {
  content: '';
  position: absolute;
  inset: 45% -2px;
  border-top: 2px solid var(--ink-950);
  transform: rotate(-45deg);
}

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