/* -------------------------------------------------------------------------
   WordsGame — feuille de style principale
   Les sections suivent l'ordre visuel de la page : base, en-tête, plateau,
   chevalet, commandes, puis adaptations pour les petits écrans.
   ------------------------------------------------------------------------- */

/*
  Courier Prime est distribuée avec le jeu (licence OFL dans assets/fonts).
  Le navigateur n'a donc rien à télécharger et le dessin des lettres reste
  identique sur macOS, Windows, iOS et Android.
*/
@font-face {
  font-family: "WordZeu Typewriter";
  src: url("assets/fonts/courier-prime-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "WordZeu Typewriter";
  src: url("assets/fonts/courier-prime-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

/*
  Archivo SemiCondensed est embarquée avec le jeu : la lettre utilise Medium,
  plus présente, tandis que sa valeur utilise Regular pour rester discrète.
*/
@font-face {
  font-family: "WordZeu Tile";
  src: url("assets/fonts/archivo-semicondensed-regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

/* Variante légère réservée au mot « WORDS » du titre. */
@font-face {
  font-family: "WordZeu Tile";
  src: url("assets/fonts/archivo-semicondensed-light.ttf") format("truetype");
  font-weight: 300;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "WordZeu Tile";
  src: url("assets/fonts/archivo-semicondensed-medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "WordZeu Tile";
  src: url("assets/fonts/archivo-semicondensed-bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

/* Archivo 500 est la police unique des lettres et valeurs de toutes les tuiles. */
@font-face {
  font-family: "WordZeu Archivo";
  src: url("assets/fonts/archivo-500.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: block;
}

/* Variante très grasse réservée à « RORO » dans le titre. */
@font-face {
  font-family: "WordZeu Archivo";
  src: url("assets/fonts/archivo-800.ttf") format("truetype");
  font-weight: 800;
  font-style: normal;
  font-display: block;
}

/* Version étendue et très grasse : le mot RORO reste lisible à petite taille. */
@font-face {
  /* Famille dédiée : aucun repli vers une autre graisse Archivo n'est possible. */
  font-family: "WordZeu Roro Display";
  /* Le paramètre de version force aussi le rechargement du fichier de police. */
  src: url("assets/fonts/archivo-expanded-black.ttf?v=20260819-roro-font") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

:root {
  /*
    THÈME « TABLE DE CASINO »
    Ces variables regroupent toutes les couleurs de l'interface. Pour essayer
    une autre ambiance, il suffit généralement de modifier ce seul bloc.

    - felt-* : le feutre sombre du fond de page ;
    - surface-* : les surfaces plus claires (panneaux, menus et boutons) ;
    - ink / white : les couleurs de texte à fort contraste.
  */
  --felt-dark: #075b3b;
  --felt-deep: #034329;
  --felt-light: #119661;
  --surface: #28a66f;
  --surface-hover: #3cbd82;
  --surface-line: rgba(234, 255, 244, .64);
  /* Panneaux flottants : assez sombres pour laisser le texte blanc lisible
     même lorsqu'ils passent devant le plateau clair. */
  --info-panel-background: rgba(3, 67, 41, .62);
  --ink: #053b29;
  --white: #f5fff9;
  /* Crème plus clair et plus saturé : les tuiles restent nettes sur le feutre. */
  --cream: #ffe4a3;
  --cream-deep: #f3c96d;
  --grid: #fff;
  --ordinary-cell: #edf8f1;
  --dialog-surface: #fff;
  --dialog-input-line: #bcd1e8;
  --line: var(--surface-line);
  --highlight: #f9ce45;
  --cpu-highlight: #75e59c;
  --cpu-glow: rgba(117, 229, 156, .95);
  --cpu-word-frame: #e8ff32;
  --dialog-backdrop: rgba(0, 30, 20, .7);
  --dialog-shadow: rgba(0, 0, 0, .35);
  --cell-inner-shadow: inset 0 1px 2px rgba(5, 59, 41, .30);
  --placed-tile-shadow: 1px 2px 2px rgba(0, 25, 15, .60);
  /* Même relief que sur le plateau, agrandi pour les tuiles du chevalet. */
  --rack-tile-shadow: 2px 3px 3px rgba(0, 25, 15, .60);
  --title-tile-shadow: 2px 3px 12px rgba(0, 25, 15, .7);
  --cpu-search: #ff3b30;
  --cpu-search-glow: rgba(255, 59, 48, .92);
  --title-brick: #e6372f;
  /* Lettres compactes et très lisibles, proches des tuiles Scrabble classiques. */
  --tile-font: "WordZeu Tile", "Avenir Next Condensed", "Helvetica Neue Condensed", "Arial Narrow", sans-serif;
  --tile-letter-font: "WordZeu Tile", "Avenir Next Condensed", "Helvetica Neue Condensed", "Arial Narrow", sans-serif;
  --title-shadow: rgba(0, 25, 15, .46);
  /* Cadre blanc pur et ombre douce pour détacher le plateau du tapis vert. */
  --board-frame-color: var(--grid);
  --board-outline: var(--surface);

  /* Ancien nom conservé pour les quelques composants qui l'emploient. */
  --blue: var(--felt-dark);

  /* Palette des bonus du plateau. */
  --bonus-word-3: #b77a80;
  --bonus-word-2: #e7c7cb;
  --bonus-letter-3: #6f95b7;
  --bonus-letter-2: #b7d6e2;

  /* Réglages de mise en page les plus utiles à ajuster. */
  --game-width: 720px;
  --panel-height: 52px;
  --profile-height: 96px;
  --profile-menu-width: 154px;
  --panel-padding: 8px;
  --panel-gap: 8px;
  --section-gap: 20px;
  --board-line: 3px;
  --board-frame: 16px;
  --cell-radius: clamp(1.5px, .65vw, 5px);
}

* { box-sizing: border-box; }

/* Le tapis doit couvrir la totalité de la fenêtre, même si le contenu du jeu
   devient plus haut qu'un écran après une rotation ou un dialogue. */
html {
  min-height: 100%;
  background: linear-gradient(180deg, var(--felt-light) 0%, var(--felt-dark) 66%, var(--felt-deep) 100%);
}

body {
  display: grid;
  grid-template-rows: 2fr auto 3fr;
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  /* Le fond s'assombrit progressivement dans le dernier tiers de la fenêtre. */
  background-color: var(--felt-dark);
  background-image: linear-gradient(to bottom, var(--felt-light), var(--felt-dark) 66%, var(--felt-deep));
  color: var(--white);
  font-family: "Avenir Next", Avenir, "Nunito Sans", "Segoe UI", sans-serif;
}

/* Indicateur discret, figé dans le coin inférieur droit de la fenêtre. */
.performance-meter {
  position: fixed;
  /* 20 px depuis le bord vertical droit de la fenêtre. */
  right: 20px;
  bottom: 20px;
  z-index: 20;
  display: grid;
  gap: 1px;
  min-width: 230px;
  padding: 8px 10px;
  border-radius: 7px;
  /* Ce vert profond isole les chiffres blancs du plateau sous-jacent. */
  background: var(--info-panel-background);
  box-shadow: 0 4px 12px rgba(0, 25, 15, .32);
  color: var(--white);
  font-family: "Avenir Next", Avenir, "Nunito Sans", "Segoe UI", sans-serif;
  font-weight: 400;
  font-size: clamp(9px, .9vw, 13px);
  line-height: 1.15;
  opacity: 1;
  pointer-events: auto;
  text-align: right;
  text-shadow: none;
}

.performance-meter span { display: block; }

/* -------------------------------------------------------------------------
   Interface SVG fournie : les illustrations portent maintenant les couleurs,
   contours et typographies. Aucun ombrage n'est ajouté ici afin de garder la
   main sur ce réglage dans une étape ultérieure.
   ------------------------------------------------------------------------- */
body {
  /* Le SVG de fond contenait une trame qui créait des irrégularités visibles
     dans les dégradés. Un aplat dégradé CSS reste parfaitement lisse, quelle
     que soit la taille de la fenêtre. */
  background-color: var(--felt-dark);
  background-image: linear-gradient(
    180deg,
    var(--felt-light) 0%,
    var(--felt-dark) 66%,
    var(--felt-deep) 100%
  );
  background-attachment: fixed;
}

/* Le tapis vert arrive immédiatement. La scène de jeu est préparée hors vue,
   puis révélée seulement lorsque sa géométrie SVG définitive est calculée :
   aucun premier dessin décalé ne peut alors être aperçu. */
body:not(.ui-ready) main { visibility: hidden; }

/* Sac de lettres : invisible hors d'un tirage, puis glissé depuis la gauche.
   L'ombre très modérée le détache du tapis sans alourdir la composition. */
.letter-bag {
  position: fixed;
  z-index: 25;
  bottom: 0;
  left: 0;
  width: clamp(145px, 22vw, 260px);
  height: auto;
  pointer-events: none;
  opacity: 0;
  transform: translate(-105%, 8%);
  filter: drop-shadow(4px 5px 8px rgba(0, 30, 17, .26));
  transition: transform .42s cubic-bezier(.22, .8, .28, 1), opacity .20s ease;
}

.letter-bag.is-visible {
  opacity: 1;
  transform: translate(0, 0);
}

.board {
  container-type: inline-size;
  width: min(100%, 728.16px);
  aspect-ratio: 1;
  padding: 2.975%;
  gap: .41cqw;
  border: 0;
  border-radius: 0;
  background: url("assets/ui/plateau.png") center / 100% 100% no-repeat;
  box-shadow: none;
}

.cell,
.cell.center,
.cell.tw,
.cell.dw,
.cell.tl,
.cell.dl {
  z-index: 1;
  background: transparent;
  box-shadow: none;
}

.cell.center::after,
.cell small { display: none; }

.tile,
.board-tile,
.title-tile {
  overflow: visible;
  border: 0;
  background: transparent;
  box-shadow: none;
}

.tile::after,
.board-tile::after { display: none; }

.tile-art {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.board-tile .tile-art,
.tile .tile-art,
.title-tile .tile-art { object-fit: contain; }

.rack {
  width: var(--rack-width);
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  aspect-ratio: 502 / 80.8477;
  background: url("assets/ui/chevalet.png") center / 100% 100% no-repeat;
}

.rack-slot { z-index: 1; }
.last-definition {
  position: fixed;
  z-index: 15;
  top: 20px;
  left: 20px;
  width: min(300px, calc(100vw - 40px));
  color: #fff8d6;
  font-size: clamp(13px, 1.25vw, 17px);
  line-height: 1.25;
  text-shadow: none;
}
.last-definition strong { display: block; font-weight: 700; }
.last-definition em { display: block; margin-top: 3px; font-style: italic; }

/*
  Historique des mots de l'ordinateur. Il reste volontairement sans panneau
  opaque afin de conserver l'impression d'un simple carnet posé sur le tapis.
*/
.cpu-word-history {
  position: fixed;
  z-index: 15;
  top: 20px;
  /* 20 px depuis le bord vertical gauche de la fenêtre. */
  left: 20px;
  width: min(280px, calc(100vw - 80px));
  bottom: 20px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: 8px 10px;
  border-radius: 7px;
  background: var(--info-panel-background);
  box-shadow: 0 4px 12px rgba(0, 25, 15, .32);
  /* Le panneau lui-même reste ouvert afin de laisser voir son triangle externe. */
  overflow: visible;
  color: var(--white);
  font-size: clamp(11px, 1.1vw, 16px);
  line-height: 1.15;
  text-shadow: none;
}

/* L'en-tête reste fixe ; la liste seule se déroule jusqu'en bas de la fenêtre. */
.cpu-word-history h2 {
  flex: 0 0 auto;
  margin: 0 0 8px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--white);
  color: var(--white);
  font-size: 1em;
  font-weight: 700;
  line-height: 1.1;
  text-align: left;
}
#cpuWordHistoryContent {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.cpu-word-entry { margin: 0 0 9px; }

.cpu-word-entry strong {
  display: block;
  font-weight: 700;
  text-transform: uppercase;
}

.cpu-word-entry em {
  display: block;
  margin-top: 0;
  font-style: italic;
  font-weight: 400;
}

/* Sur les navigateurs récents, le feutre reste perceptible sous le panneau. */
@supports ((-webkit-backdrop-filter: blur(8px)) or (backdrop-filter: blur(8px))) {
  .performance-meter,
  .cpu-word-history {
    /* Le flou adoucit le plateau sous le panneau, le vert maintient le contraste. */
    background: var(--info-panel-background);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
  }
}

/* Un triangle transparent reste accroché au haut du panneau et indique son état. */
.info-panel {
  /* Les informations ne doivent jamais intercepter une tuile ou un tap du jeu. */
  pointer-events: none;
  transition: width .2s ease, min-width .2s ease, padding .2s ease, background .2s ease;
}
/* Avant la première partie, les indicateurs ne distraient pas du choix de profil. */
.pre-game .info-panel { display: none; }
.info-toggle {
  position: absolute;
  z-index: 1;
  display: grid;
  width: 22px;
  height: 20px;
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--white);
  cursor: pointer;
  pointer-events: auto;
}
.info-toggle::before { font-size: 15px; line-height: 1; }
.cpu-word-history .info-toggle { top: 0; right: -26px; transform: none; }
.cpu-word-history .info-toggle::before { content: "◀"; }
.performance-meter .info-toggle { top: 0; left: -26px; transform: none; }
.performance-meter .info-toggle::before { content: "▶"; }
/*
  Réduit, seul le triangle existe visuellement. Le panneau conserve toutefois
  son encombrement transparent : pour le panneau fixé en bas, cela empêche le
  triangle de remonter ou descendre entre les deux états.
*/
.info-panel.is-collapsed {
  overflow: visible;
  background: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
/* Le contenu reste dans le flux pour conserver la position du triangle. */
.info-panel.is-collapsed > :not(.info-toggle) { visibility: hidden; }
/* L'historique se replie vers la gauche : son triangle revient près du bord. */
.cpu-word-history.is-collapsed .info-toggle { right: auto; left: 0; }
.cpu-word-history.is-collapsed .info-toggle::before { content: "▶"; }
.performance-meter.is-collapsed .info-toggle { left: auto; right: 0; }
.performance-meter.is-collapsed .info-toggle::before { content: "◀"; }

button,
select,
input,
dialog { font: inherit; }

main {
  grid-row: 2;
  width: min(1100px, 96vw);
  margin: 0 auto;
  padding: 0;
  text-align: center;
}

/* Les boutons d'action restent des fichiers SVG vectoriels directs : Safari les
   restitue ainsi nettement, même lorsque la scène entière est redimensionnée. */
.actions,
.rack-line,
.game-header,
.top,
.game,
.board,
.rack-wrap {
  font-size: 1em;
}

.actions button {
  background-image: none !important;
  overflow: visible;
  font-size: 1em;
}

.action-art {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* -------------------------------------------------------------------------
   En-tête, profil et panneaux de score
   ------------------------------------------------------------------------- */

.game-header {
  position: relative;
  display: flex;
  align-items: flex-end;
  /* Le titre se cale sur le bord droit du même espace que le plateau. */
  justify-content: flex-end;
  width: min(100%, var(--game-width));
  min-height: var(--profile-height);
  margin: 0 auto;
  padding-bottom: 4px;
  overflow: visible;
}

h1 {
  position: relative;
  z-index: 2;
  display: flex;
  /* La rangée de tuiles est centrée sur la hauteur du mot RORO. */
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  margin: 0;
  color: #ff2d21;
  /* Le titre reste compact, mais avec deux graisses distinctes. */
  font-size: clamp(32px, 7vw, 56px);
  line-height: 1;
  font-family: "WordZeu Roro Display", sans-serif;
  font-weight: 400;
  letter-spacing: 0;
  white-space: nowrap;
  text-transform: none;
  text-align: right;
  text-shadow: 1px 2px 4px var(--title-shadow);
  transform: translateY(5px);
  overflow: visible;
}
/* Pendant la pluie, les tuiles passent au premier plan, y compris devant RORO. */
h1.title-rain-active { z-index: 1000; }

/* « RORO » est typographique ; « WORDS GAME » est fabriqué avec des tuiles. */
.title-roro {
  position: absolute;
  left: 50%;
  /* Le sommet suit le centre de WORD ; la base rejoint celle du nom joueur. */
  /* Relèvement de 3 px pour resserrer l'alignement avec le nom du joueur. */
  top: calc(24% - 3px);
  width: auto;
  height: clamp(31px, 8.4vw, 69px);
  /* Évite que du rembourrage soit inclus dans la hauteur du dessin. */
  box-sizing: content-box;
  padding: 0;
  margin: 0;
  /* Le dessin fourni est déjà rouge vif : aucune police du poste n'intervient. */
  /* Relèvement optique d'un pixel : sa base rejoint celle du nom du joueur. */
  /* Son centre reste celui du plateau, quelle que soit la largeur des tuiles. */
  /* Les variables sont changées par JavaScript pendant la pluie de tuiles.
     Elles conservent ensuite le dernier angle obtenu, sans modifier la taille. */
  transform: translateX(-50%) rotate(var(--roro-angle, 0deg)) scale(var(--roro-scale, 1));
  transform-origin: center;
  transition: transform 80ms ease-out;
  /* L'illustration fournie reste volontairement sans ombrage ajouté. */
  filter: none;
}

/* Les tuiles de « WORDS GAME » suivent directement RORO, à droite du plateau. */
.title-tiles {
  position: relative;
  z-index: 3;
  display: grid;
  gap: 4px;
  align-items: center;
  justify-items: end;
  margin-left: clamp(6px, 1.2vw, 14px);
  /* Les deux rangées restent légèrement plus hautes que le logo. */
  transform: translateY(-5px);
  pointer-events: none;
}
.title-tiles.is-raining { z-index: 1001; }

/* Deux lignes alignées à droite : WORDS puis GAME. */
.title-word {
  display: flex;
  gap: 2px;
  align-items: center;
  justify-content: flex-end;
}

.title-tile {
  position: relative;
  flex: 0 0 clamp(18px, 5.6vw, 43px);
  width: clamp(18px, 5.6vw, 43px);
  height: clamp(18px, 5.6vw, 43px);
  border: 2px solid transparent;
  border-radius: 6px;
  background: var(--cream);
  box-shadow: var(--title-tile-shadow);
  color: var(--ink);
  font-family: var(--tile-letter-font);
  font-size: clamp(12px, 3vw, 25px);
  font-weight: 500;
  letter-spacing: -.045em;
  line-height: 1;
  /* Le pourcentage de recouvrement est relatif à la tuile elle-même. */
  margin-left: 0;
  transform: translate(calc(var(--tile-x, 0px) + var(--tile-overlap, 0px)), var(--tile-y, 0px)) rotate(var(--tile-angle, 0deg));
  animation: none;
  /* Les tuiles sont dans le h1 : elles ne doivent pas hériter de l'ombre du
     logo, car seule leur ombre portée extérieure est souhaitée. */
  text-shadow: none;
}

.title-tile .tile-letter,
.title-tile i { text-shadow: none; }

/* La phase invisible évite d'afficher les tuiles déjà rangées avant leur chute. */
.title-tile.title-rain-pending { opacity: 0; }
.title-tile.title-raining {
  animation: title-tile-rain .85s cubic-bezier(.18, .78, .28, 1.05) both;
  animation-delay: var(--rain-delay, 0s);
}
.title-tile.title-assembling {
  animation: title-tile-assemble .52s cubic-bezier(.18, .82, .24, 1) both;
  animation-delay: var(--assemble-delay, 0s);
}
/* Les douze tuiles de trop existent seulement pendant la présentation. */
.title-rain-extra {
  position: fixed;
  z-index: 1002;
  margin: 0;
  pointer-events: none;
}
.title-tile.title-sweeping {
  animation: title-tile-sweep .42s cubic-bezier(.2, .78, .32, 1) both;
  animation-delay: var(--sweep-delay, 0s);
}
@keyframes title-tile-rain {
  from { opacity: 0; transform: translate(var(--rain-start-x), var(--rain-start-y)) rotate(var(--rain-start-angle)); }
  to { opacity: 1; transform: translate(var(--rain-x), var(--rain-y)) rotate(var(--rain-angle)); }
}
@keyframes title-tile-assemble {
  from { opacity: 1; transform: translate(var(--rain-x), var(--rain-y)) rotate(var(--rain-angle)); }
  to { opacity: 1; transform: translate(0, 0) rotate(var(--tile-angle, 0deg)); }
}
/* Les tuiles superflues sont balayées vers le bas, hors de la fenêtre. */
@keyframes title-tile-sweep {
  from { opacity: 1; transform: translate(var(--rain-x), var(--rain-y)) rotate(var(--rain-angle)); }
  to { opacity: 0; transform: translate(var(--sweep-x), var(--sweep-y)) rotate(var(--sweep-angle)); }
}

.title-tile i {
  position: absolute;
  right: 2px;
  bottom: 0;
  font-family: var(--tile-font);
  font-size: clamp(5px, calc(1.1vw - 1px), 9px);
  font-style: normal;
  font-weight: 500;
  letter-spacing: .04em;
}

.title-tile {
  display: grid;
  place-items: center;
}

.top {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) repeat(2, minmax(0, 1.6fr));
  gap: var(--panel-gap);
  width: min(100%, var(--game-width));
  /* Marge haute réduite : les panneaux remontent légèrement sous le titre. */
  margin: 10px auto var(--section-gap);
}

.score,
.level {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  height: var(--panel-height);
  padding: var(--panel-padding);
  /* Même aplat vert que les boutons d'action, sans contour. */
  border: 0;
  border-radius: 5px;
  background: var(--surface);
  font-size: 13px;
  line-height: 1.15;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.score { text-align: center; }

.score strong {
  display: block;
  margin-top: 1px;
  font-size: 23px;
  line-height: 1;
}

/* L'augmentation d'un score reste lisible, même lorsque le total monte vite. */
.score strong.is-animating { animation: score-total-pop .46s ease-in-out; }

@keyframes score-total-pop {
  0%, 100% { transform: scale(1); }
  45% { transform: scale(1.32); }
}

.level { text-align: left; }

/* Les intitulés des deux réglages restent centrés au-dessus de leur menu. */
.top .level { text-align: center; }
.top .level select { text-align: left; }

.level select {
  width: 100%;
  margin: 3px 0 0;
  border: 0;
  background: transparent;
  color: var(--white);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

.level option {
  background: var(--surface);
  color: var(--white);
  font-size: 13px;
  font-weight: 700;
}

/* Le bouton affiche uniquement le dernier profil utilisé et ouvre son dialogue. */
.game-header .profile-panel {
  position: absolute;
  z-index: 4;
  bottom: 5px;
  left: 0;
  width: var(--profile-menu-width);
  text-align: left;
}

.profile-button {
  /* Son bas suit celui de RORO, même avec une taille volontairement discrète. */
  display: flex;
  align-items: flex-end;
  transform: translateY(5px);
  width: 100%;
  height: 27px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--cream);
  cursor: pointer;
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1;
  text-align: left;
  text-overflow: ellipsis;
  text-transform: none;
  white-space: nowrap;
  overflow: hidden;
}

#profileChoice {
  display: block;
  width: calc(100% - 56px);
  margin: 0 auto 12px;
  /* Flèche placée à 10 px du bord droit, comme le texte l'est à gauche. */
  padding: 10px 34px 10px 10px;
  border: 1px solid var(--dialog-input-line);
  border-radius: 6px;
  -webkit-appearance: none;
  appearance: none;
  background: var(--dialog-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m3 6 5 5 5-5' fill='none' stroke='%23062f22' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'/%3E%3C/svg%3E") no-repeat right 10px center / 16px;
  color: var(--ink);
}

#profileChoice option {
  background: var(--surface);
  color: var(--white);
  /* Les choix reprennent exactement la typographie du libellé sélectionné. */
  font-family: inherit;
  font-size: 16px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
}

/* Le bouton plein écran reste dans le coin de la fenêtre. */
.fullscreen-toggle {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 20;
  display: grid;
  width: 38px;
  height: 38px;
  padding: 0;
  place-items: center;
  border: 1px solid var(--white);
  border-radius: 50%;
  background: var(--white);
  color: var(--surface);
  cursor: pointer;
}

.fullscreen-toggle svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.fullscreen-toggle:hover {
  background: var(--white);
  color: var(--felt-dark);
}
.fullscreen-toggle .exit-fullscreen { display: none; }
.fullscreen-toggle.is-active .enter-fullscreen { display: none; }
.fullscreen-toggle.is-active .exit-fullscreen { display: block; }

/* -------------------------------------------------------------------------
   Plateau et tuiles
   ------------------------------------------------------------------------- */

.game { display: block; }

/* Plateau et chevalet forment une seule surface de déplacement au pointeur. */
.play-surface {
  position: relative;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Les tuiles peuvent se déplacer sous le pointeur : garder un curseur unique
   pendant le geste évite l'alternance visuelle entre main et flèche. */
.play-surface.is-dragging,
.play-surface.is-dragging * { cursor: grabbing !important; }

/* Copie flottante créée par JavaScript : elle ne masque jamais la cible sous le doigt. */
.drag-ghost {
  position: fixed;
  z-index: 100;
  pointer-events: none;
  /* Référence des unités cqi de la tuile copiée elle-même (rayon, bordure,
     corps de lettre). Sans ce conteneur parent, la tuile du chevalet peut
     prendre les proportions de la fenêtre pendant le survol. */
  container-type: inline-size;
  /* Les board-tile héritent de ce rayon lorsqu'elles deviennent flottantes. */
  border-radius: 6px;
  --drag-scale: 1;
  /* L'inclinaison est alimentée par le script selon la direction du geste. */
  --drag-tilt: 0deg;
  transform: translate(-50%, calc(-50% - 16px)) rotate(var(--drag-tilt)) scale(var(--drag-scale));
  transform-origin: center;
  /* Pendant le geste, la copie suit immédiatement le pointeur. L'animation
     n'intervient qu'au dépôt : une transition sur left/top crée sinon 100 ms
     de retard perceptible, particulièrement entre les cases du chevalet. */
  will-change: transform, left, top;
  transition: none;
  /* Une simple pression ne doit jamais masquer la tuile d'origine. */
  opacity: 0;
  /* L'ombre de la tuile tenue est volontairement plus large : elle donne
     l'impression qu'elle flotte au-dessus du plateau ou du chevalet. */
  filter: drop-shadow(0 15px 20px rgba(0, 25, 15, .56));
  scale: 1;
}

/* La copie ne devient visible qu'après le petit déplacement qui confirme le geste. */
.drag-ghost.is-active,
.drag-ghost.is-settling { opacity: 1; }

/* L'agrandissement ne touche que l'échelle individuelle CSS : le transform
   principal reste entièrement piloté par le pointeur, sans retard de suivi. */
.drag-ghost.is-active:not(.is-settling) {
  animation: drag-ghost-hover-grow .18s cubic-bezier(.18, .82, .28, 1) both;
}

@keyframes drag-ghost-hover-grow {
  from { scale: 1; }
  to { scale: 1.06; }
}

/* À l'arrivée, la tuile quitte son décalage aérien pour coïncider exactement avec la cible. */
.drag-ghost.is-settling {
  animation: none;
  scale: 1;
  transform: translate(-50%, -50%) rotate(0deg) scale(var(--drag-scale));
  transition: transform .20s cubic-bezier(.42, 0, 1, 1), left .20s cubic-bezier(.42, 0, 1, 1), top .20s cubic-bezier(.42, 0, 1, 1);
  /* Départ doux, puis accélération vers la taille exacte de la cible. */
}

/* Après un très bref arrêt, la tuile revient délicatement à l'horizontale. */
.drag-ghost.is-resting { transform-origin: center; }

/* La typographie de la copie suit la proportion de la cible, pas celle de la tuile de départ. */
.drag-ghost.over-board .tile,
.drag-ghost.over-board .board-tile,
.drag-ghost.over-rack .tile,
.drag-ghost.over-rack .board-tile { font-size: var(--drag-letter-size); }

.drag-ghost.over-board .tile i,
.drag-ghost.over-board .board-tile i,
.drag-ghost.over-rack .tile i,
.drag-ghost.over-rack .board-tile i { font-size: var(--drag-value-size); }

.drag-ghost .tile,
.drag-ghost .board-tile {
  position: relative;
  inset: auto;
  display: grid;
  width: 100%;
  height: 100%;
  /* Les décalages de la lettre et de sa valeur utilisent des unités cqi.
     La copie flottante doit donc devenir elle-même leur conteneur : sans cela,
     ces unités se réfèrent à une taille extérieure et le texte sort de la tuile. */
  container-type: inline-size;
  place-items: center;
  /* La copie flottante porte déjà son ombre aérienne : celle de la tuile
     posée ne doit pas se cumuler pendant le survol. */
  box-shadow: none;
}

/* La copie aérienne possède déjà sa propre ombre diffuse. */
.drag-ghost .tile-art { filter: none; }

/* La tuile réellement saisie conserve son emplacement pour le calcul du
   chevalet, mais elle ne doit jamais rester visible derrière sa copie mobile. */
.drag-source { opacity: 0 !important; visibility: hidden !important; }

.board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(15, 1fr);
  gap: var(--board-line);
  width: min(100%, var(--game-width));
  margin: auto;
  /* Cadre extérieur distinct de la grille blanche qui sépare les 225 cases. */
  border: var(--board-frame) solid var(--board-frame-color);
  border-radius: 14px;
  background: var(--grid);
  /* Liseré extérieur sombre, puis même ombre douce que le titre. */
  box-shadow: 0 0 0 4px var(--board-outline), 4px 8px 18px var(--title-shadow);
}

.cell {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1;
  overflow: visible;
  padding: 0;
  border: 0;
  border-radius: var(--cell-radius);
  background: var(--ordinary-cell);
  box-shadow: var(--cell-inner-shadow);
  color: var(--ink);
  font-size: clamp(9px, 1.7vw, 19px);
  font-weight: 400;
  cursor: pointer;
  touch-action: none;
}


.cell.center { container-type: inline-size; background: var(--bonus-word-2); }
.cell.center::after {
  content: "★";
  color: var(--white);
  font-size: clamp(11px, 56cqi, 27px);
  opacity: 1;
  pointer-events: none;
}

/* Cases bonus : MT, MD, LT et LD. */
.cell.tw { background: var(--bonus-word-3); }
.cell.dw { background: var(--bonus-word-2); }
.cell.tl { background: var(--bonus-letter-3); }
.cell.dl { background: var(--bonus-letter-2); }

.cell small {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  color: var(--white);
  font-family: "Avenir Next", Avenir, "Nunito Sans", "Segoe UI", sans-serif;
  font-size: 0;
  font-weight: 700;
  line-height: 1;
  opacity: 1;
}

.cell small::after {
  display: block;
  font-size: clamp(6px, 31cqi, 15px);
  line-height: 1;
}

.cell.tw small::after { content: "MT"; }
.cell.dw small::after { content: "MD"; }
.cell.tl small::after { content: "LT"; }
.cell.dl small::after { content: "LD"; }

.board-tile {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: inherit;
  background: var(--cream);
  box-shadow: var(--placed-tile-shadow);
  color: var(--ink);
  font-family: var(--tile-letter-font);
  /* Le dessin du plateau part de cette proportion, quelle que soit sa taille. */
  font-size: 74cqi;
  font-weight: 500;
  letter-spacing: -.045em;
  /* Chaque tuile posée reçoit sa valeur depuis le script, entre -2 et +2°. */
  transform: rotate(var(--placed-tile-angle, 0deg));
}

.board-tile i,
.tile i {
  position: absolute;
  /* Valeur et décalages suivent la même échelle que la tuile. */
  right: 9cqi;
  bottom: 5cqi;
  font-size: 23cqi;
  font-family: var(--tile-font);
  font-style: normal;
  /* Archivo Medium pour la valeur chiffrée de la tuile. */
  font-weight: 500;
  letter-spacing: .04em;
}

/* La lettre, séparée de sa valeur, peut être réglée sans déplacer le chiffre. */
.title-tile > .tile-letter,
.board-tile > .tile-letter,
.tile > .tile-letter {
  display: inline-block;
  font: inherit;
  font-weight: inherit;
  line-height: 1;
}

/* Le léger relèvement de la lettre diminue avec la tuile au lieu de rester
   fixé à un pixel : l'équilibre typographique reste identique à toute taille. */
.title-tile > .tile-letter { transform: translateY(-1px); }
.board-tile > .tile-letter { transform: translateY(-2.3cqi); }
.tile > .tile-letter { transform: translateY(-1.75cqi); }

/* Filet interne commun aux tuiles du plateau et du chevalet. Le masque ne
   conserve qu'une ligne de 1 px : le dégradé part du crème très clair en haut
   à gauche et se réchauffe jusqu'au bas droit, sans réduire la surface utile. */
.board-tile::after,
.tile::after {
  content: "";
  position: absolute;
  /* inset: 0 place le filet exactement sur le bord, sans modifier la taille. */
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(115deg, #fffdf2 0%, #fff0c4 25%, #d5a854 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

/* Les tuiles du chevalet ont une bordure transparente de 2 px (réservée à la
   sélection). Leur pseudo-élément doit donc remonter ces 2 px pour atteindre
   exactement le bord extérieur, comme une tuile du plateau. */
.tile::after { inset: -3.5cqi; }

.board-tile.cpu-new { animation: cpu-tile-in .24s cubic-bezier(.18, .82, .28, 1.25) both; }
.board-tile.joker,
.tile.joker {
  color: var(--accent);
}

/* La valeur du chevalet grandit avec ses tuiles, plus grandes que celles du plateau. */
.tile i {
  /* Les grandes tuiles gardent leur valeur dans le coin, proportionnellement. */
  right: 3.5cqi;
  bottom: 0;
  font-size: 23cqi;
}

.move-score {
  position: absolute;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  min-height: 30px;
  padding: 5px 9px;
  transform: translate(-12%, -72%);
  border: 2px solid var(--grid);
  border-radius: 999px;
  background: var(--highlight);
  color: var(--ink);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.last-move-frame {
  position: absolute;
  z-index: 5;
  box-sizing: border-box;
  border: 5px solid var(--highlight);
  border-radius: 11px;
  pointer-events: none;
}

.last-move-frame.cpu { border-color: var(--highlight); }
.last-move-frame.is-pulsing { animation: cpu-word-frame-pulse 1.1s ease-in-out infinite; }

@keyframes cpu-word-frame-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.025); }
}

.cpu-search-marker {
  position: absolute;
  z-index: 6;
  background: var(--cpu-search);
  /* Un petit carré rouge représente précisément la case en cours d'examen. */
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(82, 0, 0, .80);
  transform: translate(-50%, -50%);
  transition: left .12s ease-in-out, top .12s ease-in-out, width .10s ease-in-out, height .10s ease-in-out;
  pointer-events: none;
}

/* -------------------------------------------------------------------------
   Chevalet et commandes
   ------------------------------------------------------------------------- */

.rack-wrap {
  max-width: var(--game-width);
  margin: var(--section-gap) auto;
}

.rack-line {
  /* Le chevalet contient sept tuiles et conserve la largeur visuelle d'une
     huitième tuile libre : chaque tuile fait donc exactement 1/8 du bandeau. */
  --rack-width: min(502px, 100%);
  /* 10 px de marge de chaque côté et huit emplacements dans le dessin PDF. */
  --rack-tile-size: calc(var(--rack-width) / 8.33195);
  /* Rapport lettre/tuile rapproché de celui des tuiles du plateau. */
  --rack-letter-size: 45px;
  --rack-value-size: 13px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  width: 100%;
}

.rack {
  position: relative;
  display: grid;
  grid-column: 2;
  grid-template-columns: repeat(7, var(--rack-tile-size));
  gap: 7px;
  width: max-content;
  min-height: calc(var(--rack-tile-size) + 20px);
  padding: 10px;
  border: 1px solid #fff;
  border-radius: 12px;
  /* Dégradé linéaire continu : le bas volontairement plus brun fait mieux
     ressortir les tuiles crème. */
  background-color: var(--cream);
  background-image: linear-gradient(to bottom, var(--cream) 0%, #9b6b29 100%);
}

/* Pendant la validation du dépôt, aucun retour transitoire des tuiles ne doit
   être visible entre l'aperçu de permutation et leur ordre définitif. */
.rack.is-finalizing .tile {
  transition: none !important;
  animation: none !important;
}

/* Pendant le survol, l'ordre aperçu doit suivre le pointeur sans traînée.
   L'animation du dépôt final reste, elle, intacte. */
.rack.is-previewing .tile {
  transition: transform .18s cubic-bezier(.2, .82, .28, 1);
}

/* Les sept emplacements restent visibles : une lettre posée laisse donc sa place. */
.rack-slot {
  position: relative;
  container-type: inline-size;
  width: var(--rack-tile-size);
  height: var(--rack-tile-size);
  border-radius: 6px;
}

.rack-slot.empty {
  border: 0;
  background: transparent;
}


.tile {
  position: relative;
  flex: 0 0 var(--rack-tile-size);
  width: var(--rack-tile-size);
  height: var(--rack-tile-size);
  padding: 0;
  border: 3.5cqi solid transparent;
  border-radius: 10.5cqi;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--tile-letter-font);
  font-size: 79cqi;
  /* Archivo 500, comme les valeurs, pour une seule identité typographique. */
  font-weight: 500;
  letter-spacing: -.045em;
  /* La permutation n'anime que ce calque de composition. */
  will-change: transform;
  box-shadow: var(--rack-tile-shadow);
  transform: translateX(var(--rack-preview-shift, 0));
  transition: transform .16s ease-out, box-shadow .16s ease-out;
  cursor: pointer;
  touch-action: none;
}


/* Les tuiles arrivent en douceur dans leur nouvelle case du chevalet. */
.tile.rack-tile-landed { animation: rack-tile-land .25s ease-out both; }

@keyframes rack-tile-land {
  from { transform: translateY(-7px) scale(.92); opacity: .45; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

.tile.selected { border-color: var(--highlight); }

/* En mode échange, le repère reste entièrement à l'extérieur : il ne modifie
   ni la taille ni la position de la tuile sélectionnée. */
.tile.exchange-selected {
  border-color: transparent;
  box-shadow: var(--rack-tile-shadow);
}

.tile.exchange-selected::before {
  content: "";
  position: absolute;
  inset: -5px;
  pointer-events: none;
  border: 5px solid var(--highlight);
  border-radius: 11px;
}

.rack-shuffle {
  grid-column: 3;
  justify-self: start;
  /* Symétrie exacte avec le groupe musique / son placé à gauche. */
  margin-left: 20px;
}

/* La pointe de la flèche pèse visuellement en haut à droite : ce léger décalage
   redonne un centrage optique dans le cercle. */
.rack-shuffle svg { transform: translate(-1px, -1px); }

.actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 7px;
  width: min(100%, var(--game-width));
  margin: 0 auto;
}

.actions button {
  padding: 8px 12px;
  border: 0;
  border-radius: 999px;
  background: var(--surface);
  color: var(--white);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .055em;
  text-transform: uppercase;
  cursor: pointer;
}

/* Pendant le choix des lettres, le bouton invite doucement à valider l'échange. */
.actions #exchange.is-selecting {
  animation: exchange-validation-pulse 1.15s ease-in-out infinite;
  transform-origin: center;
}

@keyframes exchange-validation-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.045); }
}

.play-feedback {
  display: grid;
  justify-items: center;
  margin-top: 15px;
}

#status,
#status .dictionary-info {
  min-height: 1.5em;
  margin: 0;
  font-size: 15px;
  /* 600 correspond au demi-gras dans la famille Avenir Next. */
  font-weight: 600;
}

/* -------------------------------------------------------------------------
   Dialogues et animations
   ------------------------------------------------------------------------- */

dialog {
  /* Tous les dialogues partagent le même gabarit et restent centrés par showModal(). */
  box-sizing: border-box;
  width: min(390px, 88vw);
  max-width: min(390px, 88vw);
  min-height: 300px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: var(--dialog-surface);
  box-shadow: 0 20px 70px var(--dialog-shadow);
  color: var(--ink);
  text-align: center;
  overflow: hidden;
}

dialog::backdrop { background: var(--dialog-backdrop); }
dialog[open] { display: flex; flex-direction: column; }
/* Bandeau identique sur toutes les boîtes : les quatre marges du titre sont nulles. */
dialog h2 {
  display: grid;
  flex: 0 0 40px;
  width: 100%;
  height: 40px;
  margin: 0;
  padding: 0;
  place-items: center;
  background: var(--surface);
  color: var(--white);
  font-size: 20px;
  font-weight: 600;
  line-height: 1;
}
/* Le texte principal démarre toujours 80 px sous le bandeau de titre. */
dialog p { margin: 80px 28px 12px; font-size: 16px; line-height: 1.45; }
dialog > div,
dialog > select,
dialog > input { margin-left: 28px; margin-right: 28px; }
.joker-letters {
  display: grid;
  grid-template-columns: repeat(7, minmax(34px, 1fr));
  gap: 8px;
  width: calc(100% - 56px);
  margin: 0 auto 16px;
}
.joker-letters button {
  min-width: 0;
  padding: 8px 4px;
  font-family: "WordZeu Typewriter", "Courier New", monospace;
  font-size: 18px;
}
/* Groupe aligné à droite, avec les mêmes marges que les contenus du dialogue. */
.dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  width: calc(100% - 56px);
  margin: auto 28px 30px;
}
/* Boutons de confirmation : même langage visuel que les actions, sans contour. */
.dialog-actions > button,
.joker-letters button {
  min-height: 44px;
  padding: 10px 18px;
  border: 0;
  border-radius: 999px;
  background: var(--surface);
  color: var(--white);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: .055em;
  text-transform: none;
  cursor: pointer;
}
.dialog-actions > button:hover,
.joker-letters button:hover { background: var(--surface-hover); }

/* Résultat de manche : les scores reprennent la lecture des panneaux du jeu,
   en noir sur blanc pour rester nets dans le dialogue. */
.end-scoreboard {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin: 0 0 16px;
}

.end-scoreboard span {
  display: grid;
  gap: 2px;
  padding: 8px;
  border: 1px solid #111;
  border-radius: 8px;
  color: #111;
  background: #fff;
  font-size: 13px;
  font-weight: 700;
}

.end-scoreboard strong { font-size: 25px; line-height: 1; }

#endMessage p {
  margin: 0 0 12px;
  color: #111;
  line-height: 1.35;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
}

#roundMessage {
  margin: 80px 28px 12px;
  color: #111;
  line-height: 1.35;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
  white-space: pre-line;
}

#endMessage {
  width: calc(100% - 56px);
  margin: 80px auto 12px;
}

#endMessage .end-question { margin-top: 18px; font-weight: 700; }

#profileDialog input {
  display: block;
  width: calc(100% - 56px);
  margin: 0 auto 12px;
  height: 44px;
  padding: 10px;
  border: 1px solid var(--dialog-input-line);
  border-radius: 6px;
  color: var(--ink);
}

#profileDialog input,
#profileChoice {
  box-sizing: border-box;
  height: 44px;
}

@keyframes cpu-tile-in {
  from { transform: scale(.25) rotate(-9deg); opacity: 0; }
  to { transform: scale(1) rotate(0); opacity: 1; }
}


/* -------------------------------------------------------------------------
   Petits écrans
   ------------------------------------------------------------------------- */

/* iPad : mêmes espaces réguliers, avec des libellés légèrement plus compacts. */
@media (min-width: 601px) and (max-width: 1024px) {
  .top { gap: 8px; margin-top: 8px; }
  .score, .level, .level select { font-size: 12px; }
  .fullscreen-toggle { display: none; }
}

@media (max-width: 600px) {
  main { width: 100%; padding: 0 8px; }

  .cpu-word-history {
    top: 12px;
    left: 20px;
    width: min(230px, calc(100vw - 58px));
    max-height: 24dvh;
    padding: 6px 8px;
  }

  .performance-meter {
    right: 20px;
    bottom: 12px;
    min-width: 0;
    width: min(230px, calc(100vw - 58px));
    padding: 6px 8px;
  }

  .cpu-word-history .info-toggle { top: 0; right: -26px; }
  .performance-meter .info-toggle { top: 0; left: -26px; }

  .game-header {
    justify-content: flex-end;
    min-height: var(--profile-height);
    padding-right: 42px;
  }

  h1 {
    font-size: clamp(30px, 8vw, 40px);
    letter-spacing: 0;
  }

  .game-header .profile-panel {
    left: 0;
    width: min(var(--profile-menu-width), 42vw);
  }

  .fullscreen-toggle {
    display: none;
  }

  .fullscreen-toggle svg { width: 18px; height: 18px; }

  .top {
    grid-template-columns: repeat(6, 1fr);
    gap: 8px;
    width: 70%;
  }

  .top > .score { grid-column: span 2; }
  .top > .level { grid-column: span 3; }

  .score,
  .level { font-size: 12px; }

  .score strong { font-size: 21px; }
  .level select { font-size: 12px; }

  .board { width: min(100%, calc(100vw - 16px)); }

  .rack-line {
    /* Les commandes restent à gauche et à droite, sans réduire le chevalet
       jusqu'à zéro dans la colonne centrale de la grille. */
    --rack-width: min(502px, calc(100% - 104px));
    --rack-tile-size: calc(var(--rack-width) / 8.33195);
    --rack-letter-size: min(34px, calc((100vw - 60px) / 7.8));
    --rack-value-size: min(10px, calc((100vw - 60px) / 28 - 1px));
    gap: 0;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  }

  .rack { gap: 0; }
  .tile { box-shadow: 1.3px 2.6px 2.6px rgba(0, 25, 15, .60); }
  .tile i { right: 3.5cqi; bottom: 0; font-size: 23cqi; }

  .rack-shuffle {
    width: 28px;
    height: 28px;
    margin-left: 8px;
  }

  .actions { gap: 4px; width: 100%; }
  .actions button { padding: 6px 7px; font-size: 9px; letter-spacing: .025em; }
  #status,
  #status .dictionary-info { font-size: 14px; }
  .move-score { min-width: 25px; min-height: 25px; padding: 4px 7px; font-size: 11px; }
}

/* Priorité finale aux éléments SVG, après les réglages historiques du jeu. */
.board {
  container-type: inline-size;
  width: min(100%, 728.16px);
  aspect-ratio: 1;
  padding: 2.975%;
  gap: .41cqw;
  border: 0;
  border-radius: 0;
  background: url("assets/ui/plateau.png") center / 100% 100% no-repeat;
  box-shadow: none;
}
.cell,
.cell.center,
.cell.tw,
.cell.dw,
.cell.tl,
.cell.dl { z-index: 1; background: transparent; box-shadow: none; }
.cell.center::after,
.cell small { display: none; }
.tile,
.board-tile,
.title-tile { border: 0; background: transparent; box-shadow: none; }
.tile::after,
.board-tile::after { display: none; }
.rack {
  /* Le bandeau vert reste fixe. Les seules tuiles présentes sont placées
     côte à côte, sans intervalle, puis centrées dans ce bandeau. */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: var(--rack-width);
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  aspect-ratio: 502 / 80.8477;
  background: url("assets/ui/chevalet.png") center / 100% 100% no-repeat;
}
.rack-slot { z-index: 1; flex: 0 0 var(--rack-tile-size); }
/* Les absences de lettres sont des données de jeu, pas des espaces visibles. */
.rack-slot.empty,
.rack-slot.preview-empty { display: none; }
/* Pendant la prise, le slot source reste dans le flux : son trou visible
   empêche le chevalet de se recentrer artificiellement dès la saisie. */
.rack-slot:has(.drag-source) { display: block; }
/* Même garantie sans dépendre de :has(), notamment pendant une capture de
   pointeur Safari : le script marque directement le slot de départ. */
.rack-slot.drag-hole {
  display: block !important;
  flex: 0 0 var(--rack-tile-size);
}
.tile-art { display: block; width: 100%; height: 100%; pointer-events: none; user-select: none; -webkit-user-drag: none; object-fit: contain; }

/* Relief discret des trois éléments illustrés par SVG. Les ombres sont ici
   séparées des dessins afin de pouvoir ajuster leurs couleurs sans modifier
   les fichiers fournis. */
.board {
  box-shadow: 0 9px 18px rgba(0, 28, 16, .34);
  /* La grille de 687 px est centrée dans le nouveau plateau de 728,16 px. */
  padding: var(--board-grid-offset, 20.58px);
  grid-template-columns: repeat(15, var(--board-tile-size, 43px));
  grid-template-rows: repeat(15, var(--board-tile-size, 43px));
  gap: var(--board-grid-gap, 3px);
  align-content: start;
  justify-content: start;
}

.rack {
  box-shadow: 0 5px 11px rgba(0, 28, 16, .30);
}

.tile .tile-art,
.board-tile .tile-art {
  filter: drop-shadow(1px 3px 3px rgba(0, 28, 16, .33));
}

/* Le nouveau chevalet prévoit directement une marge régulière autour des tuiles. */
.rack .tile-art { transform: none; }

/* -------------------------------------------------------------------------
   Interface v5 - éléments Illustrator
   -------------------------------------------------------------------------
   Les trois dessins ci-dessous sont les fonds de référence. Les boutons
   transparents du moteur restent placés au-dessus : ils gardent toute la
   logique de jeu, tout en laissant l'illustration décider du rendu.
   ------------------------------------------------------------------------- */
.board {
  width: min(100%, 1090px);
  aspect-ratio: 1152 / 1154;
  padding: var(--board-grid-offset-y, 41px) var(--board-grid-offset-x, 46px);
  gap: var(--board-grid-gap, 6px);
  background-image: url("assets/v5/svg/plateau.svg?v=20260905-sharp");
  background-size: 100% 100%;
}

.rack-line {
  /* Le chevalet redessiné fait 908 × 206 unités. Les tuiles restent de
     110 unités et les espaces entre elles de 3 unités dans Illustrator. */
  --rack-width: min(908px, 100%);
  --rack-tile-size: calc(var(--rack-width) * 110 / 908);
  --rack-gap: calc(var(--rack-width) * 3 / 908);
}

.rack {
  width: var(--rack-width);
  aspect-ratio: 908 / 206;
  gap: var(--rack-gap);
  background-image: url("assets/v5/svg/chevalet.svg?v=20260905-sharp");
  background-size: 100% 100%;
}

/* Dans le dessin Illustrator, la base des tuiles est à 24 unités de la base
   du chevalet. Les éléments flex étant centrés, on les descend de 24 unités
   pour atteindre précisément cette ligne de référence. */
.rack-slot {
  /* La base visible du chevalet est à y = 160 dans le SVG ; une tuile dont
     la base est à 24 unités au-dessus arrive donc à y = 136. */
  transform: translateY(calc(var(--rack-width) * -22 / 908));
}

/* Une tuile SVG est déjà complète : pas de second texte HTML ou d'ombre
   héritée qui risquerait de modifier son dessin. */
.tile .tile-art,
.board-tile .tile-art {
  width: 100%;
  height: 100%;
  /* Pas de filtre sur l'image SVG : Safari la conservera vectorielle. */
  filter: none;
}

/* Les SVG v5 contiennent déjà leurs propres contours. Les anciennes ombres
   rectangulaires donnaient l'impression d'un second cadre autour du plateau
   et du chevalet : on les retire. */
.board,
.rack {
  box-shadow: none;
}

/* Une tuile de chevalet est dessinée en 110 × 110 unités dans Illustrator.
   Sans bordure CSS ajoutée, son SVG occupe exactement cette surface. */
.rack .tile {
  border: 0;
  border-radius: 0;
}

/* Une tuile de plateau occupe exactement la cellule qui la contient. Le SVG
   et la grille utilisent la même origine Illustrator : aucun décalage manuel
   n'est nécessaire. */
.board-tile {
  transform: rotate(var(--placed-tile-angle, 0deg));
}

.tile,
.board-tile { box-shadow: 1px 2px 2px rgba(0, 28, 16, .24); }

.tile .tile-letter,
.tile > i,
.board-tile .tile-letter,
.board-tile > i { display: none; }

/* Les deux petites roues dentées restent dans le coin du panneau concerné :
   elles n'ajoutent pas de nouvelle rangée à l'interface. */
.score { position: relative; }
.panel-settings {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: grid;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
}
.panel-settings svg { width: 100%; height: 100%; fill: currentColor; }
.panel-settings:hover { transform: rotate(22deg); }

.dialog-level {
  display: grid;
  gap: 6px;
  width: calc(100% - 56px);
  margin: 0 auto 18px;
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  text-align: left;
}
.dialog-level select {
  width: 100%;
  height: 44px;
  padding: 0 10px;
  border: 1px solid var(--dialog-input-line);
  border-radius: 6px;
  background: var(--dialog-surface);
  color: var(--ink);
}

/* Le plan fourni est conçu pour 390 × 844 points CSS (iPhone 13 portrait).
   Les positions emploient vw : elles gardent donc exactement les mêmes
   proportions sur les autres largeurs de téléphone. */
@media (max-width: 1024px) and (orientation: portrait) {
  body {
    display: block;
    min-height: 100dvh;
    background-color: var(--felt-dark);
    /* La maquette garde sa largeur exacte ; le second calque prolonge sans
       rupture le dégradé jusqu'aux bords de la page et de la fenêtre. */
    background-image:
      url("assets/v5/svg/fond-iphone.svg?v=20260905-sharp"),
      linear-gradient(180deg, var(--felt-light) 0%, var(--felt-dark) 66%, var(--felt-deep) 100%);
    background-repeat: no-repeat, no-repeat;
    background-position: center top, center;
    background-size: 100% auto, 100% 100%;
    background-attachment: scroll;
  }

  main {
    position: relative;
    width: 100%;
    min-height: 100dvh;
    margin: 0;
    padding: 0;
  }

  /* Les intitulés et panneaux sont dessinés dans Fond.svg ; le logo reste un
     SVG indépendant, ancré en bas à droite pour pouvoir être remplacé seul. */
  .game-header {
    position: absolute;
    z-index: 4;
    right: 4.2vw;
    bottom: 3.4vw;
    display: block;
    width: 43vw;
    min-height: 0;
    margin: 0;
    padding: 0;
  }
  .game-header h1 { display: block; width: 100%; margin: 0; transform: none; }
  .game-header .title-roro {
    position: static;
    display: block;
    width: 100%;
    height: auto;
    padding: 0;
    transform: none;
  }
  .game-header .title-tiles,
  .game-header .profile-panel,
  .game-header .fullscreen-toggle { display: none; }

  .top {
    position: absolute;
    z-index: 3;
    top: 10.256vw;
    left: 4.786vw;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.709vw;
    width: 90.427vw;
    height: 20.513vw;
    margin: 0;
  }

  .score {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: 100%;
    padding: 3.2vw 0 2.2vw;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: #fff8d6;
    font-size: 0;
  }

  .score-label { display: none; }

  .score strong {
    width: 100%;
    margin: 0;
    color: #fff8d6;
    font-size: clamp(26px, 10.5vw, 42px);
    font-weight: 800;
    line-height: 1;
  }

  .panel-settings {
    right: 2.05vw;
    bottom: 2.05vw;
    width: 6.15vw;
    height: 6.15vw;
    color: #fff8d6;
  }

  /* Les niveaux restent mémorisés par le moteur. Leur affichage est inclus
     dans le fond fourni ; leur réglage sera rattaché au futur dialogue dédié. */
  .top .level { display: none; }

  .game {
    position: absolute;
    top: 47.2vw;
    left: 0;
    width: 100%;
  }

  .board {
    width: calc(100vw - 10.77vw);
    max-width: none;
    margin: 0 auto;
  }

  .rack-wrap {
    margin: 4.6vw auto 5.1vw;
  }

  .rack-line {
    --rack-width: 80.855vw;
    --rack-tile-size: calc(var(--rack-width) * 110 / 908);
    --rack-gap: calc(var(--rack-width) * 3 / 908);
    display: flex;
    justify-content: center;
    width: 100%;
  }

  .rack { flex: 0 0 var(--rack-width); }

  .actions {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    gap: 1.026vw;
    width: 100%;
    margin: 0;
  }

  .actions button {
    flex: 0 0 16.068vw;
    width: 16.068vw;
    height: 16.068vw;
    min-width: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100% 100%;
    color: transparent;
    font-size: 0;
  }

  .actions #pass { order: 1; background-image: url("assets/v5/svg/btn-passer.svg?v=20260905-sharp"); }
  .actions #exchange { order: 2; background-image: url("assets/v5/svg/btn-echanger.svg?v=20260905-sharp"); }
  .actions #validate {
    order: 3;
    flex-basis: 23.248vw;
    width: 23.248vw;
    height: 23.248vw;
    background-image: url("assets/v5/svg/btn-jouer.svg?v=20260905-sharp");
  }
  .actions #cancel { order: 4; background-image: url("assets/v5/svg/btn-melanger.svg?v=20260905-sharp"); }
  .actions #new { order: 5; background-image: url("assets/v5/svg/btn-nouvelle-partie.svg?v=20260905-sharp"); }

  .play-feedback { margin-top: 3.1vw; padding: 0 8vw; }
  #status,
  #status .dictionary-info {
    color: #fff8d6;
    font-size: clamp(11px, 3.6vw, 15px);
    font-style: italic;
    font-weight: 700;
    line-height: 1.18;
  }

  .performance-meter {
    right: 3.4vw;
    bottom: 2.8vw;
    width: auto;
    min-width: 0;
    padding: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: #fff8d6;
    font-size: 8px;
  }

  .last-definition { display: none; }
}

/* Les cinq actions ne font pas partie de Fond.svg. Chacune reste un bouton
   HTML actif, auquel le dessin SVG correspondant est simplement appliqué. */
.actions button {
  background-color: transparent;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  color: transparent;
  font-size: 0;
}
.actions #pass { background-image: url("assets/v5/svg/btn-passer.svg?v=20260905-sharp"); }
.actions #exchange { background-image: url("assets/v5/svg/btn-echanger.svg?v=20260905-sharp"); }
.actions #validate { background-image: url("assets/v5/svg/btn-jouer.svg?v=20260905-sharp"); }
.actions #cancel { background-image: url("assets/v5/svg/btn-melanger.svg?v=20260905-sharp"); }
.actions #new { background-image: url("assets/v5/svg/btn-nouvelle-partie.svg?v=20260905-sharp"); }

/* -------------------------------------------------------------------------
   Scène unique - référence iPhone 13 (390 × 844 points CSS)
   -------------------------------------------------------------------------
   Toute l'interface est dessinée dans ce repère fixe. JavaScript ne modifie
   qu'une variable --stage-scale : la réduction conserve donc strictement les
   distances, les rayons et les tailles de toutes les illustrations.
   ------------------------------------------------------------------------- */
body {
  display: block;
  overflow: hidden;
  /* Le même fond carré recouvre la fenêtre, quelle que soit son orientation. */
  background: var(--felt-dark) url("assets/v5/svg/fond-fenetre.svg?v=20260905-sharp") center / cover no-repeat fixed;
}

main {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 390px;
  min-width: 390px;
  height: 844px;
  min-height: 844px;
  margin: 0;
  padding: 0;
  overflow: visible;
  /* Solution de secours pour les anciens navigateurs. Voir @supports plus
     bas : Safari récent effectue un zoom de mise en page, plus net pour SVG. */
  transform: translate(-50%, -50%) scale(var(--stage-scale, 1));
  transform-origin: center;
  /* La scène reste transparente : fond-fenetre.svg appartient à la fenêtre
     entière et ne subit donc aucune déformation avec le jeu. */
  background: transparent;
}

/* Une transformation CSS peut faire pixelliser les fonds SVG dans Safari.
   zoom les redimensionne pendant la mise en page et conserve leurs contours
   vectoriels, particulièrement visibles sur un écran Retina. */
@supports (zoom: 1) {
  main {
    zoom: var(--stage-scale, 1);
    transform: translate(-50%, -50%);
  }
}

/* Les trois panneaux, le plateau et les contrôles ont des coordonnées fixes
   dans la scène. Le facteur d'échelle les adaptera ensuite à l'écran réel. */
.game-header {
  position: absolute;
  z-index: 4;
  /* Le logo fourni est maintenant centré sur l'axe de la scène. */
  left: 50%;
  right: auto;
  /* 2 264 unités Illustrator / 3 : sommet du logo dans la scène. */
  top: 754.67px;
  bottom: auto;
  display: block;
  width: 168px;
  min-height: 0;
  margin: 0;
  padding: 0;
}
.game-header h1 { display: block; width: 100%; margin: 0; transform: translateX(-50%); }
.game-header .title-roro {
  position: static;
  display: block;
  width: 100%;
  height: auto;
  padding: 0;
  transform: none;
}
.game-header .title-tiles,
.game-header .profile-panel,
.game-header .fullscreen-toggle { display: none; }

.top {
  position: absolute;
  z-index: 3;
  /* Coordonnées Illustrator, divisées par trois pour la scène 390 × 844.
     Les ombres des trois panneaux se recouvrent très légèrement. */
  /* Marge haute de 40 unités dans le document Illustrator (échelle 3×). */
  top: 13.33px;
  left: 13.67px;
  display: flex;
  width: 362.66px;
  height: 98.67px;
  margin: 0;
}
.top .level { display: none; }
.score {
  position: relative;
  /* La hauteur inclut les retraits : le panneau SVG ne peut plus s'étirer
     sous le compteur et les coordonnées Illustrator restent exactes. */
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex: 0 0 123.33px;
  height: 98.67px;
  padding: 25px 0 10px;
  border: 0;
  border-radius: 0;
  background-color: transparent;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  color: #fff8d6;
  font-size: 0;
}
.score + .score { margin-left: -3.67px; }
.score-you { background-image: url("assets/v5/svg/panneau-joueur.svg?v=20260905-sharp"); }
.score-bag { background-image: url("assets/v5/svg/panneau-pioche.svg?v=20260905-sharp"); }
.score-cpu { background-image: url("assets/v5/svg/panneau-ordinateur.svg?v=20260905-sharp"); }
.score-label { display: none; }
.score strong {
  /* 85 unités Illustrator / 3 = 28,33 px dans la scène. La petite correction
     de .20em compense la descente typographique située sous la ligne de base. */
  position: absolute;
  right: 0;
  bottom: calc(28.33px - .20em);
  left: 0;
  width: 100%;
  margin: 0;
  color: #fff8d6;
  font-family: "Avenir Next", Avenir, "Nunito Sans", "Segoe UI", sans-serif;
  font-size: 36.67px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
}
.panel-settings {
  right: 8px;
  bottom: 8px;
  width: 24px;
  height: 24px;
  color: #fff8d6;
}

/* Le triangle blanc est placé juste sous le panneau actif. Sa largeur et sa
   hauteur proviennent directement de son SVG de 43,8786 × 38 unités. */
.turn-marker {
  position: absolute;
  z-index: 4;
  top: 112px;
  width: 14.63px;
  height: 12.67px;
  pointer-events: none;
  transition: left .28s ease-in-out, opacity .18s ease-out;
}
.turn-marker[data-turn="you"] { left: 68.02px; }
.turn-marker[data-turn="cpu"] { left: 307.35px; }
.turn-marker[hidden] { display: none; }

.game {
  position: absolute;
  /* 400 unités Illustrator / 3 : haut du plateau. */
  top: 133.33px;
  left: 0;
  width: 390px;
}
.board {
  /* 1 152 unités Illustrator / 3 : le plateau et le chevalet utilisent
     ainsi le même facteur d'échelle, indispensable pour leurs tuiles. */
  width: 384px;
  max-width: none;
  margin: 0 auto;
}
/* 1 580 unités Illustrator / 3 : haut du chevalet. */
.rack-wrap {
  position: absolute;
  top: 393.34px;
  left: 0;
  width: 390px;
  margin: 0;
}
.rack-line {
  /* 908 unités Illustrator correspondent à 302,67 px dans la scène 3×. */
  --rack-width: 302.67px;
  --rack-tile-size: calc(var(--rack-width) * 110 / 908);
  --rack-gap: calc(var(--rack-width) * 3 / 908);
  display: flex;
  justify-content: center;
  width: 100%;
}
.rack { flex: 0 0 var(--rack-width); }

.actions {
  /* Groupe : x 49, y 1 758, L 1 072, H 272 dans Illustrator (échelle 3×). */
  position: absolute;
  top: 452.67px;
  left: 16.33px;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  /* Marge de 12 unités Illustrator, soit 4 px dans la scène. */
  gap: 4px;
  width: 357.33px;
  height: 90.67px;
  margin: 0;
}
.actions button {
  /* 188 unités Illustrator / 3 = 62,67 px. */
  flex: 0 0 62.67px;
  width: 62.67px;
  height: 62.67px;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
}
.actions #pass { order: 1; }
.actions #exchange { order: 2; }
.actions #validate { order: 3; flex-basis: 90.67px; width: 90.67px; height: 90.67px; }
.actions #cancel { order: 4; }
.actions #new { order: 5; }
/* Le message suit le groupe de boutons sans occuper le flux du plateau. */
.play-feedback { position: absolute; top: 555.34px; left: 0; width: 390px; margin: 0; padding: 0 31px; }
#status,
#status .dictionary-info {
  color: #fff8d6;
  font-size: 14px;
  font-style: italic;
  font-weight: 700;
  line-height: 1.18;
}

/* Ces deux informations appartiennent à la fenêtre, pas à la scène : elles
   restent lisibles dans son vrai coin inférieur droit. */
.performance-meter {
  right: 14px;
  bottom: 12px;
  width: auto;
  min-width: 0;
  padding: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: #fff8d6;
  font-size: 8px;
}
.last-definition { display: none; }

/* -------------------------------------------------------------------------
   Mise à l'échelle vectorielle native
   -------------------------------------------------------------------------
   JavaScript donne à main une taille de police égale au facteur d'échelle
   (par exemple .82 px). Toutes les mesures de cette scène sont donc écrites
   en em : 390 em correspond toujours à la largeur du dessin de référence.
   Contrairement à transform: scale(), Safari peint alors chaque SVG à sa
   taille réelle et ne le transforme jamais en bitmap intermédiaire.
   ------------------------------------------------------------------------- */
main {
  width: 390em;
  min-width: 390em;
  height: 844em;
  min-height: 844em;
  transform: translate(-50%, -50%);
  zoom: 1;
  /* Les éléments de jeu restent au-dessus du décor fixe de la fenêtre. */
  z-index: 30;
}

.game-header { top: 754.67em; width: 168em; }
.top {
  top: 13.33em;
  /* Le tracé de chaque panneau possède 21 unités de marge à gauche et 27 à
     droite. Les boîtes se recouvrent donc de 11 unités source : le vide
     réellement visible entre les panneaux est exactement 12 unités. */
  left: 13.67em;
  display: flex;
  justify-content: flex-start;
  gap: 0;
  width: 362.66em;
  height: 98.67em;
}
.score {
  flex-basis: 123.33em;
  height: 98.67em;
  padding: 25em 0 10em;
  font-size: 1em;
}
.score + .score { margin-left: -3.67em; }
.score strong {
  /* Le rectangle visible du panneau s'arrête à 262 unités dans un SVG de
     296 unités. La base du compteur est 85 unités au-dessus de ce bas :
     la position du haut de sa ligne est donc 32,4 % de la hauteur totale. */
  position: absolute;
  top: 32.4%;
  right: 0;
  left: 0;
  width: 100%;
  font-size: 36.67em;
  line-height: 1;
}
.panel-settings { right: 8em; bottom: 8em; width: 24em; height: 24em; }

.turn-marker { top: 100.66em; width: 14.63em; height: 12.67em; }
.turn-marker[data-turn="you"] { left: 68.02em; }
.turn-marker[data-turn="cpu"] { left: 306.35em; }

.game { top: 133.33em; width: 390em; }
/* 390 - 384 = 6 : le plateau garde exactement 3 unités de scène de chaque côté. */
.board { width: 384em; margin-left: 3em; margin-right: 3em; }
.rack-wrap { top: 393.34em; width: 390em; }
.rack-line {
  /* Valeurs directes : 908, 110, 3 et 22 unités Illustrator ÷ 3.
     Éviter les multiplications et divisions dans calc() garde la même
     géométrie dans Safari, Chrome et les navigateurs mobiles. */
  --rack-width: 302.6667em;
  --rack-tile-size: 36.6667em;
  --rack-gap: 1em;
  /* Le chevalet et sa rangée de tuiles partagent le même axe horizontal. */
  justify-content: center;
}
.rack-slot {
  /* Référence Illustrator : la ligne basse du chevalet est à y=160,
     la base des tuiles se pose sur la limite basse de la zone crème claire.
     Elle reste donc à y=136, soit 24 unités au-dessus du rail. */
  transform: translateY(var(--rack-lift, -7.3333em));
}

.actions {
  top: 452.67em;
  left: 16.33em;
  /* Ces boutons doivent passer devant le décor inférieur de la fenêtre. */
  z-index: 30;
  gap: 4em;
  width: 357.33em;
  height: 90.67em;
}
.actions button {
  /* Une ancienne règle mettait la police à 0 pour masquer les libellés.
     Or les dimensions en em du bouton se réfèrent à sa propre police : elle
     devenait donc nulle et les cinq SVG ne pouvaient pas être peints. */
  flex-basis: 62.67em;
  width: 62.67em;
  height: 62.67em;
  font-size: 1em;
}
/* Les boutons restent lisibles même si un ancien script a remplacé le contenu
   interne du bouton d'échange. Les fichiers SVG sont donc aussi appliqués
   directement comme fond de secours. */
.actions #pass { background-image: url("assets/v5/svg/btn-passer.svg?v=20260906") !important; }
.actions #exchange { background-image: url("assets/v5/svg/btn-echanger.svg?v=20260906") !important; }
.actions #validate { background-image: url("assets/v5/svg/btn-jouer.svg?v=20260906") !important; }
.actions #cancel { background-image: url("assets/v5/svg/btn-melanger.svg?v=20260906") !important; }
.actions #new { background-image: url("assets/v5/svg/btn-nouvelle-partie.svg?v=20260906") !important; }
/* Le fond porte désormais l'illustration ; ne pas la dessiner deux fois. */
.actions .action-art { display: none; }
.actions #validate { flex-basis: 90.67em; width: 90.67em; height: 90.67em; }
.play-feedback {
  top: 555.34em;
  width: 390em;
  padding: 0 31em;
  display: block;
  text-align: center;
  z-index: 30;
}
#status, #status .dictionary-info {
  width: 100%;
  margin: 0;
  font-size: 14em;
  text-align: center;
}

/* Les dialogues sont ouverts dans la couche supérieure du navigateur : ils
   conservent une taille de lecture normale, indépendante du dessin du jeu. */
dialog { font-size: 16px; }
