/* style.css - Feuille de style "maison" de SoveJako (aucune dependance CDN).
   Palette + composants (clavier embosse, plateau encastre) + mise en page responsive.

   Sommaire :
     1. Variables de couleur (:root)
     2. Reset / base (html, body)
     3. Structure generale (.app-shell, grille principale)
     4. Zone Jako (perroquet, bulle, perchoir, animations)
     5. Scoreboard (logo, panneaux OBJEKTIF / ERÈ)
     6. Message de statut
     7. Clavier virtuel
     8. Plateau de jeu (categorie, endis, mot a deviner)
     9. Pied de page
    10. Overlays (fin de manche / victoire finale)
    11. Responsive telephone (<760px)
    12. Responsive telephone moyen (400px-<760px, surcharge la section 11)
    13. Responsive petit telephone (<=400px, surcharge la section 11)

   Les sections 11 a 13 sont des media queries qui se surchargent : la section 11
   (max-width:759.98px, soit "<760px", voir l'explication detaillee section 11) est la base
   commune a tout le format telephone ; les sections 12 (min-width:400px et max-width:
   759.98px) et 13 (max-width:400px) ciblent des plages disjointes et affinent certains
   reglages pour leur tranche d'ecran respective. Elles doivent rester APRES la section 11
   dans le fichier pour l'emporter sur elle (cascade CSS = ordre + specificite). */

:root {
  --navy: #0d2c4e;
  --navy-light: #12406e;
  --navy-dark: #081c33;

  --gold: #f2a81d;
  --gold-light: #ffc94d;
  --gold-dark: #c9860a;

  --board-bg: #a3510e;
  --board-bg-dark: #7a3b0c;

  --cell-empty: #c97c3f;
  --cell-empty-dark: #8a4e22;

  --key-blue: #1f6fb2;
  --key-blue-light: #3c90d6;
  --key-blue-dark: #123f63;

  --key-green: #2fa84f;
  --key-green-light: #4fcb6e;
  --key-green-dark: #1b6e31;

  --key-red: #b5342a;
  --key-red-light: #d65c4c;
  --key-red-dark: #7a2018;

  --white: #fffdf7;
}

/* ===================== 2. RESET / BASE ===================== */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  font-family: "Segoe UI", "Trebuchet MS", Verdana, sans-serif;
}

/* Fond de rue en plein ecran ; .app-shell (la "carte" du jeu) est centree par-dessus. */
body.app-body {
  background-image: url("../assets/img/bg-street.png");
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  background-attachment: fixed;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
}

/* Calque du fond "propre" (rue rangee, sans les tonneaux/poubelles) : superpose en plein
   ecran par-dessus le fond "sinistre" de body.app-body, invisible par defaut. A la victoire
   finale (10/10), playVictorySequence() dans game.js lui ajoute la classe is-visible pour
   le faire apparaitre en fondu pendant que scoreboard/plateau/clavier glissent hors ecran. */
.bg-clean {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-image: url("../assets/img/bg-street-clean.png");
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  opacity: 0;
  transition: opacity 1.4s ease;
  pointer-events: none;
}
.bg-clean.is-visible {
  opacity: 1;
}

/* ===================== 3. STRUCTURE GENERALE ===================== */

/* Grille principale (desktop) : Jako occupe toute la colonne de gauche sur 5 rangees,
   la colonne de droite empile scoreboard / statut / clavier / plateau / pied de page.
   Le format telephone redefinit entierement ces colonnes/rangees (voir sections 11-12).
   position+z-index : garantit qu'elle reste au-dessus de #bg-clean (position:fixed), qui
   sinon se retrouverait peint par-dessus en tant qu'element positionne (cf. regles
   d'empilement CSS : un enfant positionne passe devant un enfant non-positionne). */
.app-shell {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1200px;
  display: grid;
  gap: 14px;
  grid-template-columns: minmax(220px, 280px) 1fr;
  grid-template-rows: auto auto auto 1fr auto;
  grid-template-areas:
    "jako scoreboard"
    "jako status"
    "jako keyboard"
    "jako board"
    "jako footer";
  background: rgba(111, 211, 232, 0.18);
  border-radius: 28px;
  padding: 18px;
  backdrop-filter: blur(1px);
  transition: background-color 1.4s ease;
}

/* A la victoire finale, la carte perd sa teinte bleutee et son flou pour laisser voir les
   vraies couleurs du fond "propre" derriere (cf. playVictorySequence() dans game.js). */
.app-shell.is-victory-clear {
  background-color: transparent;
  backdrop-filter: none;
}

/* Lien vers le back-office admin : masque en permanence (display:none), reste accessible
   uniquement via son URL directe (admin.html). */
.utility-link {
  display: none;
  grid-area: utility;
  justify-self: end;
  text-decoration: none;
  font-weight: bold;
  font-size: 0.85rem;
  color: var(--navy);
  background: rgba(255, 255, 255, 0.65);
  padding: 6px 14px;
  border-radius: 999px;
  transition: background-color 0.15s ease;
}
.utility-link:hover {
  background: rgba(255, 255, 255, 0.9);
}

/* Utilitaire partage par .scoreboard-row / .board-panel / .keyboard-panel : glisse vers le
   bas et s'estompe. Ajoute par playVictorySequence() dans game.js a la 10e victoire (le
   "transform"/"opacity" de depart et la "transition" sont definis sur chaque composant). */
.is-victory-hidden {
  transform: translateY(120%);
  opacity: 0;
  pointer-events: none;
}

/* ===================== 4. ZONE JAKO (perroquet) ===================== */

/* Colonne de gauche (desktop) : bulle en haut, Jako au milieu, perchoir en dessous. */
.jako-sidebar {
  grid-area: jako;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: 10px;
  min-height: 340px;
}

/* Le "transform" (translateY / scale) est pilote en JS selon les erreurs de la manche
   (cf. applyErrorPose/flyAway/resetJakoPose dans game.js) : la transition ici assure que
   ces changements de position/taille soient anime en douceur plutot qu'instantanes. */
.jako-visual {
  position: relative;
  z-index: 1;
  width: 170px;
  transition:
    transform 0.6s cubic-bezier(0.25, 0.1, 0.25, 1),
    opacity 0.5s ease;
  transform: translateY(0);
}
.jako-sprite {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 6px 8px rgba(0, 0, 0, 0.35));
  user-select: none;
  pointer-events: none;
}

/* Tige du perchoir : part du bas de #jako-visual (top calcule en JS, cf. positionPerchPole)
   et descend jusqu'a la base grace a bottom:0. Tous les sprites (idling/talking/swearing/
   rejoicing) partagent maintenant le meme canevas 350x452 avec le perchoir au meme endroit
   (~40% de la largeur, mesure par balayage de pixels) : un seul "left" fixe suffit. */
.perch-pole {
  position: absolute;
  left: 47.6%;
  bottom: 3px;
  width: 14px;
  transform: translateX(-50%);
  background-image: url("../assets/img/tige.png");
  background-repeat: repeat-y;
  background-size: 14px 1px;
  /*border-radius: 2px;*/
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
  z-index: 0;
}

.speech-bubble {
  position: relative;
  background: var(--white);
  border-radius: 18px;
  padding: 10px 16px;
  margin-bottom: 6px;
  max-width: 220px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25);
}
/* Carre pivote 45deg dont un coin depasse de la bulle : c'est la languette qui pointe
   vers le bas par defaut (voir section 11 pour la version mobile, qui pointe vers le haut). */
.speech-bubble .bubble-tail {
  position: absolute;
  bottom: -7px;
  left: 30px;
  width: 14px;
  height: 14px;
  background: var(--white);
  transform: rotate(45deg);
}
.speech-bubble p {
  margin: 0;
  font-weight: bold;
  font-size: 0.9rem;
  color: var(--navy);
  text-align: center;
}

/* Petit tremblement joue sur le sprite a chaque mauvaise reponse (voir applyErrorPose). */
@keyframes jako-shake {
  10%,
  90% {
    transform: translateX(-3px) rotate(-2deg);
  }
  20%,
  80% {
    transform: translateX(5px) rotate(2deg);
  }
  30%,
  50%,
  70% {
    transform: translateX(-7px) rotate(-3deg);
  }
  40%,
  60% {
    transform: translateX(7px) rotate(3deg);
  }
}
.jako-sprite.is-shaking {
  animation: jako-shake 0.5s;
}

/* Applique a la 4e erreur (flyAway dans game.js) : Jako s'estompe pendant qu'il retrecit/glisse. */
.jako-visual.is-leaving {
  transition:
    transform 0.9s ease-in,
    opacity 0.9s ease-in;
  opacity: 0;
}

/* ===================== 5. SCOREBOARD ===================== */

/* Contient le logo (desktop OU mobile selon la largeur d'ecran) et les 2 panneaux
   OBJEKTIF / ERÈ, cote a cote et de meme hauteur (voir .score-panel plus bas). En format
   telephone (section 11) elle passe en display:contents, donc l'animation de sortie a la
   victoire finale cible .score-panel--obj/--err directement plutot que cette rangee (voir
   .is-victory-hidden et playVictorySequence() dans game.js). */
.scoreboard-row {
  grid-area: scoreboard;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 10px;
}
/* Style commun aux deux logos (desktop et mobile) ; chacun est ensuite affiche/masque
   selon la largeur d'ecran par .logo-desktop / .logo-mobile. */
.logo {
  display: block;
  filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.35));
  user-select: none;
  pointer-events: none;
}
/* Desktop : devant la tige (z-index au-dessus), centre sur le meme axe qu'elle (left:47.6%),
   "top" recalcule en JS (cf. positionLogo dans game.js) pour aligner le bas du logo sur le
   bas de .board-panel. */
.logo-desktop {
  position: absolute;
  left: 47.6%;
  top: 200px; /* valeur de secours avant le premier calcul JS */
  transform: translateX(-50%);
  z-index: 2;
  width: 150px;
  height: auto;
}
/* Masque par defaut (desktop) ; reapparait en format telephone (section 11), ou il prend
   la place du logo desktop en haut a gauche du scoreboard. */
.logo-mobile {
  display: none;
}
/* Panneaux "givres" (objektif / erè) : cote a cote, meme hauteur, fond transparent leger.
   La transition sert a l'animation de sortie a la victoire finale (voir .is-victory-hidden
   et playVictorySequence() dans game.js, qui cible .score-panel--obj/--err individuellement). */
.score-panel {
  flex: 1 1 220px;
  background: rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(10px) saturate(160%);
  -webkit-backdrop-filter: blur(10px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 16px;
  padding: 12px 16px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
  transition:
    transform 0.8s ease-in,
    opacity 0.8s ease-in;
}

/* Texte d'explication ("Genyen 10 pati..." / "Pa fè plis ke 4..."), masque en format
   telephone (section 11) pour gagner de la place. Entre 400px et <760px (section 12), seul
   le label en gras reapparait, le reste du texte restant invisible. */
.score-panel-title {
  margin: 0 0 10px;
  font-size: 0.88rem;
  color: #2b3648;
}
.score-panel-title strong {
  color: var(--navy);
  font-weight: 800;
}
/* Variante du panneau ERÈ : le label ("ERÈ :") est rouge plutot que bleu marine. */
.score-panel-title--erè strong {
  color: #c0392b;
}

/* Ligne "N/10" + barre de progression, generee par updateScoreboard() dans game.js. */
.score-panel-body {
  display: flex;
  align-items: center;
  gap: 10px;
}
.score-count {
  font-weight: 800;
  font-size: 1.1rem;
  color: #2b3648;
  min-width: 40px;
}

/* Barre de progression du streak : un segment par victoire visee (OBJECTIF), vert = acquis,
   rouge = restant a faire. */
.streak-bar {
  flex: 1;
  display: flex;
  gap: 5px;
}
.streak-seg {
  flex: 1;
  height: 11px;
  border-radius: 6px;
  background: var(--key-red);
}
.streak-seg.is-done {
  background: var(--key-green);
}

/* Cases d'erreur : 4 cases carrees, rouge plein + croix blanche quand l'erreur est utilisee,
   gris clair + croix grise sinon. */
.error-marks-row {
  display: flex;
  gap: 8px;
}
.error-box {
  flex: 1;
  aspect-ratio: 1 / 1;
  max-width: 46px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.35);
  border: 2px solid rgba(255, 255, 255, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.error-box::before {
  content: "\2715";
  font-size: 1.4rem;
  font-weight: 800;
  color: rgba(43, 54, 72, 0.45);
}
.error-box.is-used {
  background: var(--key-red);
  border-color: var(--key-red-dark);
}
.error-box.is-used::before {
  color: var(--white);
}

/* ===================== 6. MESSAGE DE STATUT ===================== */

/* Bandeau de consignes/feedback ("Deziyen yon son...", "Se pa jwèt, non!", etc.),
   texte mis a jour par setStatusMessage() dans game.js. Masque en format telephone
   (section 11), sauf entre 400px et <760px ou il reapparait (section 12). La transition
   sert a l'animation de sortie a la victoire finale (voir .is-victory-hidden plus bas et
   playVictorySequence() dans game.js). */
.status-bar {
  grid-area: status;
  background: #1a5c86;
  color: var(--white);
  font-weight: 800;
  text-align: center;
  border-radius: 12px;
  padding: 10px;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
  transition:
    transform 0.8s ease-in,
    opacity 0.8s ease-in;
}

/* ===================== 7. CLAVIER (look embosse) ===================== */

/* La transition sert a l'animation de sortie a la victoire finale (voir .is-victory-hidden
   plus bas et playVictorySequence() dans game.js). */
.keyboard-panel {
  grid-area: keyboard;
  background: var(--navy-light);
  border: 4px solid var(--gold);
  border-radius: 18px;
  padding: 14px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25);
  transition:
    transform 0.8s ease-in,
    opacity 0.8s ease-in;
}

/* Genere par renderKeyboard() dans game.js (une touche par grapheme kreyol : KREYOL_KEYS). */
.keyboard {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

/* Touche par defaut ("non jouee") : degrade bleu + relief embosse (inset + ombre portee). */
.key-btn {
  width: 54px;
  height: 54px;
  border-radius: 12px;
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--white);
  cursor: pointer;
  border: 2px solid var(--key-blue-dark);
  background: linear-gradient(180deg, var(--key-blue-light), var(--key-blue));
  box-shadow:
    inset 0 2px 1px rgba(255, 255, 255, 0.45),
    inset 0 -3px 4px rgba(0, 0, 0, 0.25),
    0 3px 0 var(--key-blue-dark),
    0 5px 7px rgba(0, 0, 0, 0.35);
  transition:
    transform 0.08s ease,
    box-shadow 0.08s ease,
    background 0.15s ease;
}
.key-btn:hover:not(:disabled) {
  filter: brightness(1.08);
}
.key-btn:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow:
    inset 0 2px 3px rgba(0, 0, 0, 0.35),
    0 1px 0 var(--key-blue-dark);
}

.key-btn:disabled {
  cursor: not-allowed;
  transform: translateY(3px);
  box-shadow:
    inset 0 3px 5px rgba(0, 0, 0, 0.4),
    0 1px 0 rgba(0, 0, 0, 0.3);
}
/* Touche desactivee apres avoir ete jouee (cf. guess() dans game.js) : verte si la lettre
   etait dans le mot, rouge sinon. Le style ":disabled" ci-dessus donne l'effet "enfoncee". */
.key-btn.is-correct {
  background: linear-gradient(180deg, var(--key-green-light), var(--key-green));
  border-color: var(--key-green-dark);
}
.key-btn.is-correct:disabled {
  box-shadow:
    inset 0 3px 5px rgba(0, 0, 0, 0.35),
    0 1px 0 var(--key-green-dark);
}
.key-btn.is-wrong {
  background: linear-gradient(180deg, var(--key-red-light), var(--key-red));
  border-color: var(--key-red-dark);
}
.key-btn.is-wrong:disabled {
  box-shadow:
    inset 0 3px 5px rgba(0, 0, 0, 0.35),
    0 1px 0 var(--key-red-dark);
}

/* ===================== 8. PLATEAU (look encastre) ===================== */

/* La transition sert a l'animation de sortie a la victoire finale (voir .is-victory-hidden
   plus bas et playVictorySequence() dans game.js). */
.board-panel {
  grid-area: board;
  background: var(--board-bg);
  border: 4px solid var(--gold);
  border-radius: 18px;
  padding: 18px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25);
  transition:
    transform 0.8s ease-in,
    opacity 0.8s ease-in;
}

/* Ligne "categorie + endis" au-dessus du mot a deviner. */
.riddle-meta {
  /* display: flex; */
  /* align-items: stretch; */
  gap: 10px;
  text-align: center;
  margin-bottom: 14px;
  margin-top: 0px;
}
/* Etiquette de categorie (ex. "PWOVÈB"). La version en ruban vertical (writing-mode +
   rotation) est desactivee pour le moment : elle reste en commentaire ci-dessous au cas
   ou on voudrait la reactiver plus tard. */
.riddle-category {
  display: flex;
  align-items: center;
  justify-content: center;
  /* writing-mode: vertical-rl; */
  /* transform: rotate(180deg); */
  background: var(--navy);
  color: var(--gold);
  font-weight: 800;
  font-size: 0.8rem;
  letter-spacing: 0.03em;
  padding: 10px 6px;
  border-radius: 10px;
  white-space: nowrap;
}
.riddle-hint-row {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-top: 2px solid rgba(255, 201, 77, 0.6);
  border-bottom: 2px solid rgba(255, 201, 77, 0.6);
  padding: 6px 0;
  color: var(--white);
}
.riddle-hint-row .label {
  font-weight: 800;
  color: var(--gold-light);
}
.riddle-hint-row .value {
  font-style: italic;
}

/* Genere par renderBoard() dans game.js : un .word-group par mot, contenant une .cell par
   lettre a deviner et un .literal-char pour chaque caractere affiche d'office (espace,
   apostrophe, tiret...). */
.word-board {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 22px 26px;
}
.word-group {
  display: flex;
  gap: 4px;
}

.cell {
  width: 48px;
  height: 56px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 1.4rem;
}
/* Case pas encore devinee : creusee (ombres internes), lettre invisible. */
.cell.is-empty {
  background: linear-gradient(
    180deg,
    var(--cell-empty-dark),
    var(--cell-empty)
  );
  border: 2px solid var(--board-bg-dark);
  box-shadow:
    inset 0 3px 6px rgba(0, 0, 0, 0.45),
    inset 0 -1px 0 rgba(255, 255, 255, 0.08);
  color: transparent;
}
/* Case devinee : doree et en relief, avec une petite animation d'apparition (pop). */
.cell.is-revealed {
  background: linear-gradient(180deg, #ffdd86, var(--gold));
  border: 2px solid var(--gold-dark);
  box-shadow:
    inset 0 2px 1px rgba(255, 255, 255, 0.6),
    inset 0 -3px 3px rgba(0, 0, 0, 0.15),
    0 3px 0 var(--gold-dark),
    0 4px 6px rgba(0, 0, 0, 0.3);
  color: var(--navy);
  animation: pop 0.25s ease-out;
}
.literal-char {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 56px;
  color: var(--white);
  font-weight: 800;
  font-size: 1.4rem;
}

@keyframes pop {
  from {
    transform: scale(0.5);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* Devinèt long (beaucoup de cases) : le plateau retrecit d'environ 12% pour tout faire tenir.
   La classe is-compact est ajoutee par renderBoard() dans game.js selon le nombre de cases. */
.word-board.is-compact .cell {
  width: 42px;
  height: 49px;
  font-size: 1.2rem;
}
.word-board.is-compact .literal-char {
  width: 14px;
  height: 49px;
  font-size: 1.2rem;
}
.word-board.is-compact {
  gap: 18px 20px;
}

/* ===================== 9. PIED DE PAGE ===================== */

/* La transition sert a l'animation de sortie a la victoire finale (voir .is-victory-hidden
   plus bas et playVictorySequence() dans game.js). */
.app-footer {
  grid-area: footer;
  text-align: center;
  font-weight: 800;
  font-size: 0.75rem;
  color: rgba(13, 44, 78, 0.65);
  padding-top: 4px;
  transition:
    transform 0.8s ease-in,
    opacity 0.8s ease-in;
}

/* ===================== 10. OVERLAYS (resultat / victoire) ===================== */

/* Fond plein ecran assombri, partage par les deux popups (#result-overlay et
   #match-overlay dans index.html). Affiche/masque via la classe is-hidden. */
.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 16px;
}
.overlay.is-hidden {
  display: none;
}

/* Boite CHAMPYON (victoire finale) : contrairement a #result-overlay (instantane), le fond
   noir ET la carte s'affichent chacun en fondu tres lent (10s), independamment l'un de
   l'autre. Le delai d'attente de 60s avant meme de commencer ce fondu est gere en JS
   (VICTORY_WAIT_MS, cf. playVictorySequence()/revealMatchWon() dans game.js). Tant que
   is-visible n'est pas ajoutee, opacity:0 + pointer-events:none la rendent invisible et
   non interactive meme si is-hidden a deja ete retiree (display:flex). */
#match-overlay {
  opacity: 0;
  pointer-events: none;
  transition: opacity 10s linear;
}
#match-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
}
#match-overlay .overlay-card {
  opacity: 0;
  transition: opacity 10s linear;
}
#match-overlay.is-visible .overlay-card {
  opacity: 1;
}

.overlay-card {
  background: var(--navy);
  border: 4px solid var(--gold);
  border-radius: 24px;
  padding: 34px 40px;
  text-align: center;
  max-width: 420px;
}
.overlay-card h2 {
  color: var(--gold);
  font-size: 1.8rem;
  margin: 0 0 12px;
}
.overlay-card p {
  color: var(--white);
  font-weight: bold;
  margin: 0 0 22px;
  white-space: pre-line;
}
.overlay-btn {
  border: 2px solid var(--key-blue-dark);
  background: linear-gradient(180deg, var(--key-blue-light), var(--key-blue));
  color: var(--white);
  font-weight: 800;
  font-size: 1rem;
  padding: 12px 28px;
  border-radius: 14px;
  cursor: pointer;
  box-shadow:
    inset 0 2px 1px rgba(255, 255, 255, 0.45),
    inset 0 -3px 4px rgba(0, 0, 0, 0.25),
    0 3px 0 var(--key-blue-dark),
    0 5px 7px rgba(0, 0, 0, 0.35);
}
.overlay-btn.is-win {
  background: linear-gradient(180deg, var(--key-green-light), var(--key-green));
  border-color: var(--key-green-dark);
  box-shadow:
    inset 0 2px 1px rgba(255, 255, 255, 0.45),
    inset 0 -3px 4px rgba(0, 0, 0, 0.25),
    0 3px 0 var(--key-green-dark),
    0 5px 7px rgba(0, 0, 0, 0.35);
}
.overlay-btn:active {
  transform: translateY(2px);
}

/* ===================== 11. RESPONSIVE : telephone (<760px) ===================== */
/* Coupure stricte a 760px, alignee avec la section 12 : en dessous de 760px on est en mode
   telephone, a 760px et au-dela on repasse en desktop (base sans media query) - exactement
   les 2 groupes voulus (<760px / >=760px), sans zone intermediaire ambigue.
   Le seuil utilise "759.98px" plutot que "759px" ou "760px" pour deux raisons :
   1) avant ce correctif, cette section utilisait max-width:760px alors que la section 12
      s'arretait a 759px, ce qui creait un etat bancal exactement a 760px (mobile
      partiellement applique, sans les surcharges de la section 12) ;
   2) certains environnements (zoom navigateur, DevTools "Responsive", devicePixelRatio non
      entier) peuvent produire une largeur CSS fractionnaire tres proche de 760px (ex.
      759.4px) que Math.round() affiche comme "759px" dans les devtools mais qui echoue
      quand meme un test "max-width:759px" strict. "759.98px" absorbe cette zone grise tout
      en excluant 760px pile. Le seuil doit rester identique dans TOUTES les media queries
      "telephone" ci-dessous (sections 11 et 12) et dans isCompactLayout() (game.js). */

@media (max-width: 759.98px) {
  html,
  body {
    height: 100%;
  }
  body.app-body {
    padding: 8px;
    align-items: stretch;
    height: 100dvh;
    overflow: hidden;
  }

  /* La page elle-meme ne defile plus : seul .board-panel (rangee "board", 1fr) est scrollable,
     pour garder le scoreboard et le clavier toujours visibles (voir plus bas).

     Grille du bloc logo/scoreboard (colonne de gauche = logo + Jako, colonne de droite =
     panneaux OBJEKTIF/ERÈ) : le logo s'etend sur 2 rangees ("logo obj" + "logo err"), le
     panneau ERÈ s'etend lui aussi sur 2 rangees ("logo err" + "jako err"). Resultat : Jako
     (rangee "jako err") s'aligne avec la moitie inferieure du panneau ERÈ, a cote et en
     dessous du logo, plutot que sur toute la hauteur du bloc. */
  .app-shell {
    height: 100%;
    overflow: hidden;
    grid-template-columns: minmax(84px, 110px) 1fr;
    grid-template-rows: auto auto auto auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "utility  utility"
      "logo     obj"
      "logo     err"
      "jako     err"
      "status   status"
      "board    board"
      "keyboard keyboard"
      "footer   footer";
    border-radius: 18px;
    padding: 12px;
    gap: 8px;
  }

  /* La rangee scoreboard n'est plus un conteneur flex propre : ses 3 enfants (logo + 2
     panneaux) deviennent des cellules independantes de la grille app-shell, ce qui permet
     de placer Jako a gauche du panneau ERÈ, sous le logo. */
  .scoreboard-row {
    display: contents;
  }
  .logo-mobile {
    grid-area: logo;
    display: block;
    width: 100%;
    height: auto;
  }
  /* Le panneau OBJEKTIF n'a plus besoin d'etirer sur toute la hauteur allouee par la grille
     (logo au-dessus) : il garde sa hauteur naturelle et se cale en bas de sa cellule. */
  .score-panel--obj {
    grid-area: obj;
    align-self: end;
  }
  .score-panel--err {
    grid-area: err;
  }

  .jako-sidebar {
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    min-height: 0;
    padding-top: 0;
    gap: 4px;
  }
  /* Jako est agrandi (position absolute, en dehors du flux) : il peut deborder de sa propre
     cellule de grille et recouvrir le bas du logo au-dessus, sans perturber la hauteur de
     sa rangee. left/bottom/margin-left/width sont des valeurs ajustees manuellement au pixel
     pres pour ce format d'ecran. */
  .jako-visual {
    position: absolute;
    left: 30%;
    bottom: -130px;
    margin-left: -45px;
    width: 135px;
    z-index: 2;
    order: 1;
    transform-origin: bottom center;
  }
  /* Bulle descendue sous Jako (voir le bubble-tail vers le haut plus bas) ; z-index superieur
     a .jako-visual pour rester lisible meme quand Jako la chevauche. */
  .speech-bubble {
    position: relative;
    z-index: 4;
    order: 0;
    top: 85px;
    margin-bottom: -50px;
    max-width: 100%;
    padding: 5px 8px;
  }
  .speech-bubble p {
    font-size: 0.68rem;
  }
  /* Bulle descendue sous Jako : la languette pointe donc vers le haut (au lieu du bas). */
  .speech-bubble .bubble-tail {
    left: 50%;
    margin-left: -7px;
    top: -7px;
    bottom: auto;
  }
  /* La tige reste masquee sur ce format tres compact : sur si peu de hauteur, elle serait
     tronquee et la bulle recouvrirait son extremite basse. */
  .perch-pole {
    display: none;
  }
  .logo-desktop {
    display: none;
  }

  .status-bar {
    display: none;
  }

  /* Les explications OBJEKTIF/ERÈ sont masquees (gain de place) ; seuls compteur/barre et
     cases d'erreur restent visibles, justifies a droite. */
  .score-panel-title {
    display: none;
  }
  .score-panel-body {
    justify-content: flex-end;
  }
  .error-marks-row {
    justify-content: flex-end;
  }

  /* Categorie + endis simplement empiles et centres (au lieu de la ligne flex du desktop). */
  .riddle-meta {
    display: block;
    text-align: center;
    margin-bottom: 10px;
  }
  /* Simple bloc centre au-dessus de l'endis (pas de chevauchement de bordure sur ce format). */
  .riddle-category {
    display: block;
    width: fit-content;
    margin: 0 auto 8px;
    writing-mode: horizontal-tb;
    padding: 4px 14px;
  }

  /* Seule rangee qui defile reellement (voir le commentaire sur .app-shell plus haut). */
  .board-panel {
    overflow-y: auto;
    min-height: 0;
  }

  /* "sticky" ceinture-et-bretelles : reste en bas meme si un calcul de hauteur imprevu
     laissait echapper un debordement. */
  .keyboard-panel {
    position: sticky;
    bottom: 0;
    z-index: 5;
  }
  .key-btn {
    width: 42px;
    height: 42px;
    font-size: 0.85rem;
    border-radius: 9px;
  }

  .cell {
    width: 36px;
    height: 44px;
    font-size: 1.05rem;
  }
  .word-board {
    gap: 14px 16px;
  }
  .word-board.is-compact .cell {
    width: 30px;
    height: 38px;
    font-size: 0.9rem;
  }
  .word-board.is-compact .literal-char {
    width: 11px;
    height: 38px;
    font-size: 0.9rem;
  }
}

/* ===================== 12. RESPONSIVE : telephone moyen (400px-<760px) ===================== */
/* Surcharge ciblee de la section 11, seulement entre 400px et <760px (meme seuil "759.98px"
   qu'en section 11, voir son commentaire) : en dessous de 400px (section 13), la barre de
   statut reste masquee et le scoreboard reste sans aucun texte. */
@media (min-width: 400px) and (max-width: 759.98px) {
  /* Fait reapparaitre la barre de statut, masquee par defaut en section 11. */
  
  .score-panel--err{
      display: flex;
      align-items: center; /* Aligne verticalement le texte et les carrés */
      /* Espace les éléments (texte à gauche, carrés à droite) */
      /* justify-content: space-between;  */
      gap: 8px; /* Espace optionnel entre le texte et les carrés */
      height: 60px;
  }

  /* La colonne de gauche (logo/Jako) fait minmax(84px, 110px) + gap:8px (section 11), donc
     le panneau ERÈ (colonne de droite) demarre a ~118px du bord gauche de .app-shell. La
     barre de statut (qui occupe toute la largeur) reprend ce meme decalage pour que son
     texte s'aligne avec le panneau plutot qu'avec Jako. */
  .status-bar {
      margin-left: 118px;
  }

  /* Optionnel : pour s'assurer que le paragraphe ne garde pas de marges par défaut gênantes */
  .score-panel-title {
      margin: 0;
  }

  .error-marks-row { 
      display: flex;
      /* Espace entre chaque carré d'erreur */
      gap: 4px;
      min: width 80px; 
  }
    
  .error-box {
      flex: 1;
      aspect-ratio: 1 / 1;
      max-width: 46px;
      min-width:30px;
      border-radius: 8px;
      background: rgba(255, 255, 255, 0.35);
      border: 2px solid rgba(255, 255, 255, 0.5);
      display: flex;
      align-items: center;
      justify-content: center;
  }
  
  .status-bar {
    display: block;
  }

  /* N'affiche que le label en gras du scoreboard ("OBJEKTIF :" / "ERÈ :") : le texte
     d'explication qui suit n'est pas un element separe (simple noeud texte a la suite du
     <strong> dans le <p>), donc impossible de le cibler directement en CSS. Astuce :
     font-size:0 sur le paragraphe rend tout son texte invisible, puis on restaure la
     taille uniquement sur <strong>. */
  .score-panel-title {
    display: block;
    font-size: 0;
  }
  .score-panel-title strong {
    font-size: 0.88rem;
  }

  .speech-bubble {
    position: relative;
    z-index: 4;
    order: 0;
    top: 60px;
  }

}

/* ===================== 13. RESPONSIVE : petit telephone (<=400px) ===================== */
/* Les reglages ci-dessous ne s'appliquent qu'en dessous de 400px (ex. iPhone SE) : au-dela,
   la mise en page "telephone" standard de la section 11 suffit largement. Ce bloc ne fait
   que surcharger certaines proprietes de la section 11 (padding/border reduits, logo/Jako/
   clavier redimensionnes) ; grid-template-areas y est plus simple (chaque zone occupe une
   seule rangee, pas de chevauchement logo/err comme en section 11). */
@media (max-width: 400px) {
  body.app-body {
    padding: 4px;
  }

  /* Le lien admin est masque (display:none) donc sa rangee "utility" est carrement retiree de
     la grille pour ne pas gaspiller un "gap" de marge en haut pour rien. La colonne de gauche
     (logo + Jako) est exprimee en vw pour suivre "occupe un peu moins d'un tiers de l'ecran". */
  .app-shell {
    grid-template-columns: minmax(90px, 32vw) 1fr;
    grid-template-rows: auto auto auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "logo     obj"
      "jako     err"
      "status   status"
      "board    board"
      "keyboard keyboard"
      "footer   footer";
    border-radius: 14px;
    padding: 6px;
    gap: 5px;
  }

  /* Logo agrandi (2x la version precedente) ; Jako un peu moins d'un tiers de l'ecran. */
  .logo-mobile {
    width: 30vw;
    margin: 0 auto;
  }
  .score-panel {
    padding: 8px 10px;
  }
  .jako-visual {
    width: 31vw;
    margin-left: -15.5vw;
    bottom: -54px;
    left: 50%;
  }

  .board-panel {
    padding: 9px;
    border-width: 2px;
  }

  .keyboard-panel {
    padding: 6px;
    border-width: 2px;
  }
  .keyboard {
    gap: 7px;
  }
  .key-btn {
    width: 35px;
    height: 35px;
    font-size: 0.75rem;
    border-radius: 7px;
  }

  .app-footer {
    padding-top: 2px;
    font-size: 0.62rem;
  }
}
