/* Sokoban — la feuille de style.
   Copyright (c) 2026 Tristan Cazenave. Tous droits réservés — voir LICENSE.

   LE PLATEAU EST UNE GRILLE, ET RIEN D'AUTRE. Pas d'images : un mur est un
   carré, une caisse un carré posé dessus, un but un creux. Tout tient dans
   `--c`, la taille d'une case ; le reste en découle, et le plateau se met à
   l'échelle en changeant un seul nombre. */

:root {
  color-scheme: light dark;
  --fond: #f4f1ea; --surface: #fffdf8; --surface-2: #efeadf;
  --ink: #1d2126; --ink-2: #4a525c; --ink-3: #7b838d; --line: #d9d2c5;
  --accent: #2c4e7e; --accent-2: #24406a; --bad: #a6402f; --bon: #2f6b45;
  --sol: #e6dfd0; --mur: #6b6152; --mur-haut: #7d7362;
  --but: #c9a227; --caisse: #b07c3f; --caisse-haut: #c8944f;
  --posee: #4f8c5e; --posee-haut: #63a473; --homme: #2c4e7e;
}
@media (prefers-color-scheme: dark) {
  :root {
    --fond: #14171b; --surface: #1c2026; --surface-2: #242932;
    --ink: #e8eaed; --ink-2: #b3bac3; --ink-3: #868e98; --line: #333a44;
    --accent: #7aa2d6; --accent-2: #9dbce5; --bad: #e08b7c; --bon: #7fc79c;
    --sol: #2a2f36; --mur: #454d58; --mur-haut: #525b68;
    --but: #b9932a; --caisse: #8d6333; --caisse-haut: #a2753f;
    --posee: #3f7050; --posee-haut: #4f875f; --homme: #7aa2d6;
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--fond); color: var(--ink);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
  padding-bottom: env(safe-area-inset-bottom);
}
.page { max-width: 1060px; margin: 0 auto; padding: 10px 12px 28px; }

header.tete { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.pousse { margin-left: auto; }

.langues {
  display: inline-flex; border: 1px solid var(--line); border-radius: 999px;
  overflow: hidden; background: var(--surface);
}
.langues button {
  font: inherit; font-size: 13px; line-height: 1; padding: 6px 10px;
  border: 0; background: none; color: var(--ink-2); cursor: pointer;
}
.langues button + button { border-left: 1px solid var(--line); }
.langues button[aria-pressed="true"] { background: var(--accent); color: #fff; }

.btn {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 9px; padding: 7px 12px; cursor: pointer;
}
.btn:hover:not(:disabled) { background: var(--surface-2); border-color: var(--accent); }
.btn:disabled { opacity: .45; cursor: default; }
.btn.primaire { background: var(--accent); color: #fff; border-color: var(--accent); }

/* LES COMPTEURS PEUVENT REVENIR À LA LIGNE. Ils portent maintenant le nom du
   problème du jour — « Problème du jour — 2026-09-22 · Poussées 0 · Pas 0 »,
   soit près de quatre cents pixels : un `nowrap` les faisait déborder de tout
   téléphone. Chaque morceau reste insécable, la ligne non. */
.compteurs { font-size: 13.5px; color: var(--ink-3); min-width: 0; }
.compteurs > span { white-space: nowrap; }
.compteurs > span + span::before { content: ' · '; color: var(--ink-3); }
.compteurs b { color: var(--ink-2); font-weight: 600; }

/* ------------------------------------------------------------------ le jeu */

.jeu { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }

.plateau {
  /* LE PLATEAU PREND LA PLACE QU'IL Y A. Une taille de case figee laissait un
     niveau de sept cases perdu au milieu d'un grand ecran vide, et debordait
     sur un petit. La case est donc la plus petite de trois contraintes : la
     largeur qui reste a cote du tableau d'honneur, la hauteur qui reste sous
     l'en-tete, et un plafond — sans lui, deux caisses dans un grand ecran
     deviendraient des paves. */
  --large: calc((100vw - 400px) / var(--l));
  --haut: calc((100vh - 250px) / var(--h));
  /* Le plafond était à 64 quand un niveau pouvait faire dix cases de large.
     Les grilles du 5×5 en font sept, mur compris : sur un grand écran, 64
     laissait un plateau de 462 pixels perdu au milieu du vide. */
  --c: clamp(22px, min(var(--large), var(--haut)), 82px);
  display: grid;
  grid-template-columns: repeat(var(--l), var(--c));
  grid-template-rows: repeat(var(--h), var(--c));
  gap: 0; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 10px; padding: 6px;
  touch-action: manipulation;
}
.case { width: var(--c); height: var(--c); position: relative; }
.case.sol { background: var(--sol); cursor: pointer; }
.case.mur {
  background: linear-gradient(160deg, var(--mur-haut), var(--mur));
  border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
}

/* Un but est un creux dans le sol : on doit le voir sous une caisse posée
   dessus, sinon on ne sait plus ce qui est fait et ce qui reste. */
.case.but::before {
  content: ''; position: absolute; inset: 34%;
  border-radius: 50%; background: var(--but); opacity: .75;
}

.case.caisse::after {
  content: ''; position: absolute; inset: 9%;
  border-radius: 5px;
  background: linear-gradient(160deg, var(--caisse-haut), var(--caisse));
  box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .18), 0 1px 2px rgba(0, 0, 0, .25);
}
.case.caisse.posee::after {
  background: linear-gradient(160deg, var(--posee-haut), var(--posee));
}
/* Une caisse coincée se signale : rouge, et sans qu'on ait rien demandé. */
.case.coincee::after { box-shadow: inset 0 0 0 2px var(--bad), 0 1px 2px rgba(0,0,0,.25); }


.case.homme::after {
  content: ''; position: absolute; inset: 18%;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, #fff6, transparent 60%), var(--homme);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
}

/* ------------------------------------------------- le tableau d'honneur */

.cote { flex: 1 1 280px; min-width: 260px; }
.cote h2 { font-size: 16px; margin: 0 0 6px; }
.note { font-size: 13.5px; color: var(--ink-3); margin: 6px 0; }

.tableau { list-style: none; counter-reset: rang; margin: 0; padding: 0; }
.tableau li {
  counter-increment: rang;
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 8px; border-bottom: 1px solid var(--line); font-size: 14px;
}
.tableau li::before {
  content: counter(rang) '.'; color: var(--ink-3); min-width: 1.6em;
  font-variant-numeric: tabular-nums;
}
.tableau li.moi { background: var(--surface-2); border-radius: 6px; }
.tableau .nom { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tableau .score { margin-left: auto; white-space: nowrap; }
.tableau .pas { color: var(--ink-3); white-space: nowrap; font-size: 13px; }

/* L'invitation à se connecter : visible, mais pas criarde — elle accompagne
   des commandes éteintes, elle ne les remplace pas. */
.invite {
  margin: 0 0 10px; padding: 9px 12px; font-size: 13.5px; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line); border-radius: 9px;
}
.invite[hidden] { display: none; }
.invite a { color: var(--accent); font-weight: 600; }
.invite .ligne { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 8px; }
.invite input {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--fond);
  border: 1px solid var(--line); border-radius: 9px; padding: 7px 10px;
  min-width: 10ch; flex: 0 1 18ch;
}
.invite .apres { font-size: 13px; color: var(--ink-3); }
.invite .mauvais { color: var(--bad); }
.invite .bon { color: var(--bon); font-weight: 600; }

/* ── LE RITUEL ─────────────────────────────────────────────────────────────
   La serie se lit avant de jouer, le panneau de fin apres avoir gagne. Le
   ruban est en chasse fixe : les carres doivent s'aligner en colonnes, sinon
   il ne ressemble a rien une fois colle ailleurs. */
.rituel {
  margin: 0 2px 10px; font-size: 13.5px; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.rituel[hidden] { display: none; }

.attente {
  margin: 10px 0 0; padding: 10px 14px; font-size: 14px;
  background: var(--surface); border: 1px solid var(--accent); border-radius: 12px;
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap; max-width: 460px;
}
.attente[hidden] { display: none; }

.fin {
  margin: 14px 0 0; padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  max-width: 460px;
}
.fin[hidden] { display: none; }
.fin h2 { margin: 0 0 2px; font-size: 19px; color: var(--bon); }
.fin .fin-numero { margin: 0; font-size: 13.5px; color: var(--ink-3); }
.fin .fin-score { margin: 8px 0 0; font-size: 15px; color: var(--ink); }
.fin .fin-serie { margin: 10px 0 0; font-size: 14.5px; color: var(--ink-2); font-weight: 600; }
.fin p { margin: 6px 0 0; }
.fin .apres { font-size: 13px; color: var(--ink-3); margin-left: 8px; }
.fin .apres.bon { color: var(--bon); }
.fin .apres.mauvais { color: var(--bad); }
.ruban {
  margin: 10px 0 0; font: 17px/1.25 ui-monospace, "Segoe UI Emoji", monospace;
  letter-spacing: 1px; white-space: pre-wrap; word-break: break-all;
}
.ruban:empty { display: none; }
/* Un lien éteint : il se voit, il ne se suit pas. */
.btn.eteint { opacity: .45; cursor: default; }

.dit { min-height: 1.5em; margin: 10px 2px 0; font-size: 14.5px; color: var(--ink-2); }
.dit.bon { color: var(--bon); font-weight: 600; }
.dit.mauvais { color: var(--bad); }

.barre-haut { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 0 0 10px; }
.barre-bas { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 12px; }

.modale {
  position: fixed; inset: 0; background: rgba(10, 12, 16, .55);
  display: flex; align-items: center; justify-content: center; padding: 16px; z-index: 100;
}
.modale[hidden] { display: none; }
.boite {
  background: var(--surface); color: var(--ink); border: 1px solid var(--line);
  border-radius: 14px; padding: 20px; max-width: 560px; width: 100%;
  max-height: 84vh; overflow: auto;
}
.boite h2 { margin: 0 0 4px; font-size: 19px; }
.boite h3 { margin: 16px 0 4px; font-size: 15px; color: var(--ink-2); }
.boite p { margin: 4px 0; font-size: 14.5px; color: var(--ink-2); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.cache { display: none !important; }

/* Le plateau suit l'écran : un niveau de neuf cases de large doit tenir sur un
   téléphone sans qu'on ait à le faire glisser. */
@media (max-width: 760px) {
  /* Le tableau d'honneur est passe dessous : toute la largeur revient au
     plateau, et il n'y a plus de colonne a lui reserver. */
  .plateau { --large: calc((100vw - 34px) / var(--l)); }
  .jeu { gap: 14px; }
}
@media (max-width: 520px) { .page { padding: 6px; } }

/* ------------------------------------------------------- le choix d'une série
   Cent quarante-cinq classes, cinquante-trois millions de positions : deux
   listes déroulantes et un compte. Le reste de la page est le même plateau que
   partout ailleurs — une position du 5×5 se joue comme un niveau. */

.choix { max-width: 760px; }
.choix h1 { font-size: 21px; margin: 6px 0 10px; }
.reglages {
  display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap;
  margin: 16px 0 12px;
}
.reglage { display: flex; flex-direction: column; gap: 4px; font-size: 13px;
           color: var(--ink-3); }
.reglage select {
  font: inherit; font-size: 15px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 9px; padding: 7px 10px; min-width: 88px;
}
.combien { font-size: 14px; color: var(--ink-2); padding-bottom: 8px; }
.choix .note { font-size: 14px; color: var(--ink-3); line-height: 1.5;
               max-width: 68ch; }
.barre-bas[hidden], .compteurs[hidden], .jeu[hidden] { display: none; }

/* RIEN NE ZOOME TOUT SEUL SUR UN ÉCRAN TACTILE. Deux appuis rapprochés sur un
   bouton sont lus par Safari, sur iPhone et iPad, comme un double appui — c'est
   à dire comme un ordre de grossir : le navigateur met alors le BOUTON à la
   largeur de l'écran, et le jeu sort du cadre. Le geste se fait sans y penser
   quand on presse deux fois « Annuler » pour revenir de deux coups en arrière.

   `manipulation` dit au navigateur qu'il n'y a pas de double appui à attendre
   ici : l'appui simple part aussitôt, le défilement reste, et le pincement — le
   zoom VOULU — reste lui aussi. On ne retire que le zoom accidentel, et jamais
   par `user-scalable=no`, qui fermerait les deux.

   Le plateau était protégé depuis longtemps ; les commandes ne l'étaient pas.
   La propriété ne s'hérite pas — le navigateur croise celle de la cible avec
   celle de ses ancêtres, de sorte que la première ligne suffirait —, mais on la
   redit sur les commandes : c'est là que le geste a lieu, et la protection ne
   doit pas dépendre d'un ancêtre qui changerait d'avis. */
html, body { touch-action: manipulation; }
button, select, input, textarea, a, label, summary { touch-action: manipulation; }

/* ET RIEN NE ZOOME NON PLUS EN ENTRANT DANS UN CHAMP : sous seize pixels,
   Safari grossit la page dès qu'un `input`, un `select` ou un `textarea` prend
   le focus — il rend le texte lisible — et il ne la redescend pas en sortant.
   Même symptôme que le double appui, autre cause. Seize pixels sur les écrans
   tactiles SEULEMENT : à la souris, les commandes restent discrètes.

   Mesuré avant d'être posé, sur l'écran d'un téléphone : aucun champ du site ne
   dépassait seize pixels — la règle ne rétrécit donc rien —, et aucune page ne
   déborde. */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
  /* Ces champs-là tiennent leur taille d'une règle plus précise — une classe,
     un identifiant : la ligne du dessus perdrait la cascade sans les nommer.
     C'est la mesure qui les a trouvés, un par un. */
  .invite input { font-size: 16px; }
}
