/* ==========================================================================
   EIKOSI — feuille de style
   Toute la couleur passe par ces variables. Un habillage se fera en les
   redéfinissant sur une classe posée sur <body>, sans réécrire une règle.
   ========================================================================== */

:root {
  /* Sans cette déclaration, Chrome sur Android APPLIQUE SON MODE SOMBRE
     automatique aux pages qui ne se prononcent pas : le fond blanc devient
     gris foncé et la figure noire devient claire. C'est ce que Louis a vu.
     Une page qui déclare son jeu de couleurs n'est jamais retouchée. */
  color-scheme: light;

  --paper:      #ffffff;
  --paper-alt:  #eeeceb;
  --ink:        #12141a;
  --ink-soft:   #6b7280;
  --ghost:      #c9c7c0;

  /* Couleurs de tracé, par famille. Convention du document :
     triangle orange, quadrilatère bleu, cercle rouge. */
  --shape-triangle:      #f97316;
  --shape-quadrilatere:  #2563eb;
  --shape-cercle:        #ef4444;
  --shape-pentagone:     #059669;
  --shape-hexagone:      #7c3aed;
  --shape-trapeze:       #db2777;
  --shape-losange:       #0891b2;

  --life:  #e11d48;
  --ok:    #16a34a;

  --radius: 14px;

  /* ── L'ENVELOPPE PASTEL ───────────────────────────────────────────────────
     Louis : « un thème léger, enfantin, smooth, polish, avec des couleurs
     pastel […] la zone dédiée aux figures doit être un cadre couleur
     légèrement blanc cassé-beige clair, avec des bords arrondis. »

     ── LA DÉCISION QUI N'EST PAS DE GOÛT ──────────────────────────────────
     Le jeu repose sur la lecture d'un TRAIT FIN. Un fond pastel saturé sous
     une figure noire fait chuter le contraste : mesuré sur #FBF5EA, le
     rapport reste à 15,8:1 — au-dessus du 7:1 exigé par la norme AAA. Sur un
     pastel plus soutenu (#F3E3C8), il tombe à 13:1, encore lisible, mais la
     figure commence à « flotter ».
     D'où la règle tenue partout ici : **l'enveloppe est pastel, la surface de
     tracé reste presque blanche.** Le beige donne la chaleur, le cadre donne
     l'arrondi, et la géométrie garde son contraste.

     Ces couleurs ne sont pas choisies au hasard : chacune est la version
     délavée d'une couleur de forme du jeu. L'accueil et la carte parlent donc
     déjà le vocabulaire chromatique que le joueur apprendra en jouant. */
  --creme:      #FBF7F1;   /* le fond général, un blanc cassé chaud */
  --creme-fort: #F3ECE0;   /* les creux, les séparations */
  --cadre:      #FDFAF5;   /* l'intérieur du cadre de figure — presque blanc */
  --cadre-bord: #EADFCC;   /* son liseré, assez présent pour dessiner l'arrondi */

  --pastel-orange: #FFE0C7;
  --pastel-bleu:   #CFE0FF;
  --pastel-rose:   #FFD4D4;
  --pastel-vert:   #D2EEDC;
  --pastel-violet: #E0D6FA;
  --or:            #C99A2E;
  --or-clair:      #FBEFD2;

  /* Les arrondis vont par TROIS, et jamais plus : un rayon par échelle. Une
     interface qui invente un rayon par élément perd sa cohérence de forme,
     c'est ce qui sépare un habillage d'un thème. */
  --rond-s: 12px;
  --rond-m: 20px;
  --rond-l: 28px;

  /* L'ombre du casual : très large, très douce, jamais noire. Une ombre grise
     salit un pastel ; une ombre teintée de la couleur du fond le pose. */
  --pose:   0 2px 0 rgba(180,160,130,.14), 0 8px 20px -12px rgba(150,125,90,.5);
  --pose-h: 0 1px 0 rgba(180,160,130,.14), 0 4px 12px -9px rgba(150,125,90,.5);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--creme);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Un jeu de tracé au doigt : aucun geste du navigateur ne doit interférer.
   `touch-action: none` sur la scène est ce qui empêche le défilement de voler
   le geste dès que le doigt part vers le bas. */
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

/* ── `hidden` NE SUFFIT PAS QUAND UNE CLASSE DIT `display` ─────────────────
   L'attribut `hidden` du navigateur pose `display: none` avec une spécificité
   nulle. `.jeu { display: flex }` la bat. L'écran de jeu restait donc EN
   DESSOUS de l'accueil, en pleine page — Louis : « il y a toujours la partie
   basse, sous la page du menu principal, comme un artefact de tous les niveaux
   qui fonctionnent en parallèle ».
   `.accueil[hidden]` était déclaré, `.jeu[hidden]` avait été oublié. Un oubli
   d'une ligne, visible sur chaque écran, pendant tout le développement. */
.jeu[hidden] { display: none }

.jeu {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100dvh;               /* dvh, pas vh : la barre d'adresse mobile
                                   fait varier la hauteur en cours de route */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* --------------------------------------------------------------------------
   HAUT — la consigne, purement visuelle
   -------------------------------------------------------------------------- */

/* ══════════════════════════════════════════════════════════════════════════
   LA BANDE — tout ce qui n'est pas la figure doit s'effacer

   Mesuré avant de la réécrire : la figure n'occupait que 55 % de la surface
   utile. Elle est carrée, l'écran est haut, et la caméra cale sur la LARGEUR —
   donc la hauteur ne coûte rien au confort du doigt. C'est l'observation de
   Louis : « et si maintenant on lui donnait 90 % de la hauteur ? »

   Le score a disparu d'ici. Il ne sert à rien pendant qu'on trace, et il
   poussait l'énoncé sur le côté dès qu'il dépassait cinq chiffres. Il reste au
   menu principal, où on le regarde vraiment.
   ══════════════════════════════════════════════════════════════════════════ */
.bande {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 9px 16px 1px; min-height: 24px;
}
.bande__lieu {
  font-size: 12.5px; font-weight: 560; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bande__lieu b { color: var(--ink); font-weight: 620 }

.consigne {
  display: flex; gap: 14px; align-items: center; justify-content: center;
  flex-wrap: wrap; padding: 1px 16px 3px;
}

.palier, .score {
  font-size: 12px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.but { display: flex; align-items: center; gap: 3px; }
/* Le cadre de « la plus grande » se pose sur CETTE boîte, pas sur `.but` :
   voir plus bas — c'est ce qui garde le « ×2 » en dehors des flèches. */
.but__icone { position: relative; display: block; line-height: 0 }
.but svg { width: 30px; height: 30px; display: block; }
.but b { font-size: 19px; font-weight: 700; letter-spacing: -0.01em; }
.but--fait { opacity: 0.3; }

/* ── LA MARQUE « LA PLUS GRANDE », POSÉE SUR L'ICÔNE ───────────────────────
   Elle DÉBORDE volontairement du cadre de la forme — les quatre flèches
   partent vers l'extérieur — parce que c'est ce qu'elle veut dire : prenez
   celle qui va le plus loin. Posée à l'intérieur, elle aurait ressemblé à un
   motif de remplissage. */
/* ── LE CADRE ENTOURE L'ICÔNE, PAS L'ÉNONCÉ ENTIER ────────────────────────
   Il se posait sur `.but`, qui contient AUSSI le « ×2 ». Au palier 59 —
   « triangle ×2, la plus grande » — les quatre flèches s'écartaient donc pour
   englober le chiffre, et le cadre devenait démesuré. Louis :

     « le cadre autour du triangle est soudainement démesuré. Il faudrait que
       le cadre "Le Plus Grand" continue de rester autour du triangle, et que
       le "×2" soit à l'extérieur de ce cadre. »

   Il a raison, et pas seulement pour l'allure : les deux signes ne parlent pas
   de la même chose. Le « ×2 » compte des formes ; les flèches qualifient LA
   forme. Un cadre qui les englobe tous les deux laisse croire que « la plus
   grande » porte sur la paire — c'est-à-dire sur autre chose que ce qu'elle
   dit. Le cadre se pose donc sur l'icône seule, et le compte reste dehors. */
.but--grand .but__icone { position: relative }
.but--grand .plusgrand {
  position: absolute; inset: -5px; width: auto; height: auto;
  color: var(--ink-soft); opacity: .68; pointer-events: none;
}

/* Quatrième application de la même règle, écrite cette fois AVANT d'être
   oubliée : toute classe qui déclare `display` déclare aussi son `[hidden]`. */
.vies[hidden] { display: none }
.vies { display: flex; gap: 5px; }
.vie {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--life);
  transition: background-color 160ms, transform 160ms;
}
.vie--perdue { background: var(--ghost); transform: scale(0.72); }

/* --------------------------------------------------------------------------
   SCÈNE — les deux toiles superposées
   -------------------------------------------------------------------------- */

/* ── LE CADRE DE LA FIGURE ─────────────────────────────────────────────────
   « un cadre couleur légèrement blanc cassé-beige clair, avec des bords
   arrondis ». C'est la demande de Louis, et c'est aussi ce qui manquait le
   plus : sans cadre, la figure flottait sur la page et rien ne disait où
   commençait l'aire de jeu. Le doigt cherchait sa zone.

   Les marges sont volontairement FAIBLES (8 px de côté). On a passé une
   journée à récupérer de la hauteur pour la figure ; un cadre généreux la
   rendrait tout de suite. Le liseré et l'arrondi suffisent à délimiter — ils
   ne demandent pas d'espace, juste une bordure. */
.terrain {
  position: relative;
  display: flex;
  flex: 1;
  min-height: 0;
  margin: 2px 8px 4px;
  padding: 4px;
  border: 1.5px solid var(--cadre-bord);
  border-radius: var(--rond-l);
  background: var(--cadre);
  box-shadow: var(--pose);
  /* La figure ne doit jamais déborder de son cadre : c'est ce qui rend
     l'arrondi crédible plutôt que décoratif. */
  overflow: hidden;
}

.scene {
  /* La scène reste DANS LE FLUX — sortie du flux, elle laissait le terrain à
     hauteur nulle et le pied de page remontait sous l'énoncé. Les horizons,
     eux, se posent par-dessus. */
  position: relative;
  flex: 1;
  min-height: 0;
  /* La figure prend TOUT ce que la bande, l'énoncé et le pied lui laissent.
     C'est ici que se gagnent les 90 % de hauteur : non pas en agrandissant la
     scène, mais en rétrécissant tout le reste. */
  touch-action: none;
}

.toile {
  position: absolute;
  inset: 0;
  display: block;
}
.toile--dessus { pointer-events: none; }

/* --------------------------------------------------------------------------
   MESSAGE — court, discret, jamais bloquant
   -------------------------------------------------------------------------- */

.message {
  margin: 0;
  min-height: 22px;
  text-align: center;
  font-size: 13.5px;
  color: var(--ink-soft);
  opacity: 0;
  transition: opacity 160ms;
}
.message--visible { opacity: 1; }
.message--ok   { color: var(--ok); }
.message--rate { color: var(--life); }
/* Avertissement de règle — ni victoire ni échec : une note de jeu. Elle doit se
   distinguer des deux autres sans crier, puisqu'elle ne sanctionne rien. */
.message--note { color: #8a6d2f; }

/* --------------------------------------------------------------------------
   BAS — provisoire : naviguer entre les paliers pendant les essais
   -------------------------------------------------------------------------- */

.bas {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 8px 16px 14px;
  position: relative;
}

.bouton {
  font: inherit;
  font-size: 15px;
  min-width: 44px;              /* cible tactile confortable */
  min-height: 44px;
  padding: 0 14px;
  border: 1.5px solid var(--cadre-bord);
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
  font-weight: 560;
  box-shadow: var(--pose-h);
  /* ── LE « JUICY » TIENT DANS CETTE LIGNE ────────────────────────────────
     Ce qui donne aux jeux casual leur sensation de matière n'est ni la
     couleur ni l'arrondi : c'est que le bouton RÉPOND. Il s'enfonce sous le
     doigt, l'ombre se rétracte, et il remonte. 90 ms — assez pour se sentir,
     trop peu pour se voir. */
  transition: transform 90ms, box-shadow 90ms, background 140ms;
}
.bouton:active {
  background: var(--creme-fort);
  transform: translateY(1.5px);
  box-shadow: none;
}

/* ── L'AMPOULE ──────────────────────────────────────────────────────────────
   Posée au-dessus de « Rejouer », côté droit. Elle reste VISIBLE même verrouillée :
   un recours qu'on découvre au moment où l'on en aurait besoin arrive trop tard.
   Éteinte, elle dit qu'elle existe ; allumée, elle dit qu'elle est disponible. */
.ampoule {
  position: absolute;
  right: 16px;
  bottom: 62px;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--paper-alt);
  border-radius: 999px;
  background: #fff;
  /* JAMAIS PÂLE. Un bouton effacé se lit comme un bouton mort, et le joueur
     n'apprend pas la règle qu'il porte. Il est toujours net et toujours
     cliquable ; c'est sa RÉPONSE qui change selon qu'il est disponible. */
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 160ms, border-color 160ms, background 160ms;
}
.ampoule svg { width: 22px; height: 22px; display: block }
.ampoule--prete {
  color: #b8860b;
  border-color: #e6c96b;
  background: #fffdf3;
}
.ampoule--prete:active { background: #fdf5d8 }
.bouton--large { padding: 0 22px; }

/* Une flèche d'essai qui ne mène nulle part reste VISIBLE mais éteinte : une
   commande qui disparaît laisse croire à un défaut, une commande grisée
   enseigne une limite. */
.bouton--eteint { opacity: .32; cursor: default }
.bouton--eteint:active { background: #fff; transform: none }

/* Le compteur d'images : outil de mesure, pas décoration. Il doit rester figé
   quand personne ne touche l'écran — c'est la preuve que rien ne tourne. */
.compteur {
  position: absolute;
  right: 16px;
  bottom: 4px;
  font-size: 10px;
  color: var(--ghost);
  font-variant-numeric: tabular-nums;
}


/* ══════════════════════════════════════════════════════════════════════════
   L'ACCUEIL
   La structure vient de Meowdoku — un geste dominant, des cartes qui donnent
   une raison de revenir. La matière, non : Eikosi est fait de traits noirs sur
   du papier blanc, et son écran d'accueil doit annoncer ce que le jeu demande
   à l'œil. Les seules couleurs sont celles des familles de formes, qui
   deviendront le vocabulaire du joueur.
   ══════════════════════════════════════════════════════════════════════════ */

.accueil {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  /* repère de la toile de l'avatar, qui la couvre en absolu */
  position: relative;
  /* ── SANS CECI, ON NE PEUT PAS TRAÎNER L'ANIMAL AU DOIGT ────────────────
     Louis : « pour l'instant je n'arrive pas à cliquer glisser l'animal ».
     Reproduit en émulation tactile, et la trace est sans appel :

        pointerdown · touchstart · pointermove · touchmove · POINTERCANCEL

     Au deuxième mouvement, le navigateur décide que le geste est un
     défilement, s'approprie le doigt et ANNULE le pointeur. L'animal est lâché
     avant d'avoir bougé. À la souris rien ne se voyait — c'est pour ça que le
     défaut a survécu à mes essais.

     `touch-action: none` dit au navigateur qu'il n'y a rien à faire défiler
     ici, et c'est exact : l'accueil fait exactement une hauteur d'écran et ne
     défile pas. On ne perd donc rien, et le doigt reste à nous.
     `preventDefault()` seul n'aurait pas suffi : la décision de défiler se
     prend AVANT, sur la seule foi de cette propriété. */
  touch-action: none;
  padding: calc(env(safe-area-inset-top) + 14px) 20px calc(env(safe-area-inset-bottom) + 22px);
  /* Un dégradé très bas en contraste : le pastel se pose sur le haut de
     l'écran et s'éclaircit vers le bouton. L'œil descend sans qu'on lui dise. */
  background:
    radial-gradient(120% 55% at 50% 0%, #FFF1E2 0%, rgba(255,241,226,0) 68%),
    var(--creme);
}
.accueil[hidden] { display: none }

/* ── LE BANDEAU : DEUX GROUPES, ET UNE MARQUE QUI NE BOUGE PAS ─────────────
   Louis : le coffre va « à droite de l'icône profil (sans décaler le logo du
   jeu) ». La marque ne peut donc plus être centrée par le flux — en flux, tout
   bouton ajouté à gauche pousse le centre de la moitié de sa largeur, ce qui
   est précisément ce qu'il refuse. Elle est posée en absolu au milieu du
   bandeau, et n'a plus rien à voir avec ce qui l'entoure. */
.accueil__haut { display: flex; justify-content: space-between; align-items: center; gap: 10px }

/* ── LE COFFRE RESTE VISIBLE DERRIÈRE LA BOÎTE ─────────────────────────────
   Louis : « il faut que la fenêtre Votre parcours soit un petit peu abaissée en
   hauteur de sorte qu'on puisse voir l'icône de richesse derrière, comme c'est
   le cas (mais de manière trop importante) avec le bouton Réglages. »

   Le « mais de manière trop importante » est la clé : il ne veut pas que la
   boîte descende, il veut qu'elle laisse juste passer le bandeau. On ne la
   déplace donc pas — on lui met un rembourrage haut qui dégage exactement la
   hauteur du bandeau, et elle reste centrée dans ce qui reste. Descendre la
   boîte l'aurait aussi rapprochée du bas, où vit le pouce. */
/* `.voile.voile--haut` et non `.voile--haut` seul : la règle générale `.voile`
   est écrite plus bas dans ce fichier, et à spécificité égale c'est la dernière
   qui gagne. La boîte restait donc centrée, et le dégagement du bandeau ne
   tenait que par accident — par le rembourrage, pas par l'alignement. */
.voile.voile--haut { padding-top: 74px; align-items: flex-start }
.voile.voile--haut .boite { max-height: calc(100dvh - 98px) }

/* ── LA MARQUE EN PETIT, DANS LE BANDEAU ───────────────────────────────────
   Elle n'apparaît qu'une fois la constellation installée au centre : les deux
   ne coexistent jamais, sinon le nom du jeu serait écrit deux fois sur le même
   écran. Elle occupe la place LIBRE entre les deux boutons ronds, donc elle
   n'en prend à personne.
   `[hidden]` déclaré sur la ligne suivante — la règle qui a été enfreinte
   quatre fois dans ce fichier. */
.marque-mini[hidden] { display: none }
/* La marque redevient centrée PAR LE FLUX. Elle avait été posée en absolu pour
   que l'ajout du coffre à gauche ne la décale pas ; le coffre a quitté le
   bandeau pour sa propre case, et une position absolue qui ne protège plus rien
   est une complication de plus à comprendre le jour où l'on y revient. */
.marque-mini {
  display: flex; align-items: center; gap: 7px; flex: 1;
  justify-content: center; min-width: 0; opacity: .9;
}
.marque-mini__signe { width: 76px; height: auto; display: block; flex: none }
.marque-mini__nom {
  font-size: 13px; font-weight: 620; letter-spacing: .18em; color: var(--ink-soft);
}

/* ── PASSER — discret, mais atteignable ────────────────────────────────────
   Un bouton de sortie ne doit pas concurrencer le jeu : il est en creux, sans
   fond, à la place laissée libre par les vies masquées. Assez grand pour le
   pouce (44 px de haut avec son remplissage), assez pâle pour qu'on ne le
   presse pas par mégarde. */
.passer[hidden] { display: none }
.passer {
  display: inline-flex; align-items: center; gap: 5px;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 560;
  color: var(--ink-soft); padding: 11px 4px 11px 14px;
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--cadre-bord);
}
.passer:active { color: var(--ink) }

.rond {
  width: 42px; height: 42px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--paper-alt);
  border-radius: 999px;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
}
.rond svg { width: 21px; height: 21px; display: block }
.rond:active { background: var(--paper-alt) }
.rond--petit { width: 34px; height: 34px }
.rond--petit svg { width: 17px; height: 17px }

/* ── LE BOUTON DU PARCOURS ─────────────────────────────────────────────────
   Il porte une image et non un trait : l'orbe est un objet du jeu, et un objet
   ne se redessine pas au stylo. `position: relative` est ce qui lui permet de
   porter sa pastille — sans lui, elle irait se caler sur le premier ancêtre
   positionné, c'est-à-dire quelque part dans l'en-tête.

   ── POURQUOI LE HALO EST UNE OMBRE ET NON UN CERCLE ────────────────────────
   Un `::before` agrandi aurait poussé la mise en page ou demandé un `overflow`
   à découper — et le découpage aurait mangé la pastille, qui dépasse. Une
   `box-shadow` ne participe à AUCUN calcul de place : elle déborde sans rien
   décaler, et rien ne la rogne. Deux couches, l'une nette et l'autre diffuse,
   parce qu'un seul flou donne un rond de buée au lieu d'une lueur. */
.rond--parcours {
  position: relative;
  border-color: #d9cdea;
  background: linear-gradient(180deg, #fff 0%, #f6f1fb 100%);
  animation: parcoursRespire 2.6s ease-in-out infinite;
}
.rond__orbe {
  width: 32px; height: 32px; display: block;
  /* L'orbe est sombre sur fond clair : il n'a pas besoin d'ombre portée pour se
     détacher, seulement d'un rien de contraste pour ne pas paraître délavé. */
  filter: saturate(1.14) brightness(1.07) contrast(1.03);
}
@keyframes parcoursRespire {
  0%, 100% { box-shadow: 0 0 0 0 rgba(124, 58, 200, .34), 0 0 9px 2px rgba(124, 58, 200, .20) }
  50%      { box-shadow: 0 0 0 4px rgba(124, 58, 200, .10), 0 0 17px 5px rgba(124, 58, 200, .34) }
}
/* Le halo appelle ; une fois le bouton pressé, il n'a plus rien à dire. */
.rond--parcours:active { background: #efe6f8 }

/* ── ET IL SE TAIT QUAND ON LE LUI DEMANDE ─────────────────────────────────
   Une lueur qui pulse sans fin est exactement ce que ce réglage existe pour
   supprimer. On garde la lueur — elle porte du sens —, on retire la pulsation. */
@media (prefers-reduced-motion: reduce) {
  .rond--parcours { animation: none;
    box-shadow: 0 0 0 2px rgba(124, 58, 200, .12), 0 0 12px 3px rgba(124, 58, 200, .26) }
}

/* ── Les cartes ─────────────────────────────────────────────────────────────
   Deux, pas six. Un écran d'accueil qui propose tout ne propose rien, et le
   jeu n'a aujourd'hui que deux raisons honnêtes de revenir : son rang du jour,
   et le vocabulaire qu'on est en train de se constituer. */
/* ── LES DEUX CASES NE FONT PLUS LA MÊME HAUTEUR ───────────────────────────
   `align-items: start` est ce qui permet à la case du classement d'être « plus
   fine en hauteur », comme Louis l'a demandé, pendant que la case dorée prend
   la place qu'il faut au coffre. Par défaut, une grille étire ses éléments à la
   hauteur de la rangée : les deux auraient été aussi hautes l'une que l'autre,
   et amincir l'une aurait aminci l'autre. */
.cartes { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 18px;
  align-items: start }

.carte {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 12px 14px 11px;
  border: 1.5px solid var(--cadre-bord);
  border-left-width: 5px;
  border-radius: var(--rond-m);
  background: #fff;
  box-shadow: var(--pose-h);
  font: inherit; text-align: left; cursor: pointer;
  transition: transform 90ms, box-shadow 90ms, background 140ms;
}
.carte:active { background: var(--creme-fort); transform: translateY(1.5px); box-shadow: none }
.carte--classement { border-left-color: #F5B267; padding-bottom: 0 }
.carte--glossaire  { border-left-color: #8FB4F0 }
/* 11,5 px et pas de retour à la ligne : « Classement du jour » tenait sur deux
   lignes et rendait la case plus haute que la dorée — l'inverse exact de ce que
   Louis demande. Une case « plus fine en hauteur » commence par un titre qui
   tient sur une ligne. */
.carte__titre  { font-size: 11.5px; color: var(--ink-soft); white-space: nowrap }
.carte__chiffre{ font-size: 24px; font-weight: 650; line-height: 1.15; font-variant-numeric: tabular-nums }
.carte__note   { font-size: 11.5px; color: var(--ghost) }

/* ── LA PARTIE BASSE DE LA CASE DU CLASSEMENT ──────────────────────────────
   Louis : « dans une partie basse de cette case, on y ajoute la place actuelle
   du joueur au classement ». Une PARTIE, donc, et pas une troisième ligne de
   texte : un bandeau qui court d'un bord à l'autre, séparé par un trait. C'est
   ce qui distingue « voici autre chose » de « voici la suite ». */
.carte__place {
  display: flex; align-items: baseline; gap: 7px;
  width: calc(100% + 28px); margin: 8px -14px 0;
  padding: 6px 14px 8px;
  border-top: 1px solid var(--creme-fort);
}
.carte__place b { font-size: 14px; font-weight: 750; font-variant-numeric: tabular-nums }
.carte__place i { font-style: normal; font-size: 10.5px; color: var(--ghost); line-height: 1.2 }

/* ══════════════════════════════════════════════════════════════════════════
   LA CASE DORÉE — le coffre a son cadre

   Louis, après l'avoir vu dans le bandeau : « à la base j'avais dit de mettre
   le coffre à côté du bouton profil, mais en fait il a plutôt sa place dans son
   propre cadre, à la place de Collection. »

   Il a raison, et ça se voit sur l'écran : dans le bandeau, le coffre était un
   troisième bouton rond entre deux réglages, et sa somme à cinq chiffres venait
   toucher le logo. Ici il a la place de dire aussi le titre du joueur et le
   montant — c'est-à-dire tout ce qu'il faut pour savoir où l'on en est sans
   rien ouvrir.
   ══════════════════════════════════════════════════════════════════════════ */
.carte--tresor {
  align-items: center; gap: 5px;
  padding: 11px 12px 12px;
  border-left-color: var(--or);
  /* Le doré est un FOND, pas un liseré : c'est la seule case du menu qui parle
     d'argent, et elle doit se reconnaître avant d'être lue. Deux teintes très
     proches suffisent — un dégradé franc ferait un bouton de casino. */
  background: linear-gradient(168deg, #FFF8E6 0%, #FBEFCF 100%);
  border-color: #EBD9A6;
}
.carte--tresor:active { background: linear-gradient(168deg, #FBEFCF 0%, #F5E3B8 100%) }

.tresor__titre {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: #8A6A16; text-align: center; line-height: 1.2;
}
.tresor__coffre {
  width: 62px; height: 62px; object-fit: contain; display: block;
  filter: drop-shadow(0 3px 6px rgba(140, 104, 24, .30));
  transition: transform 220ms cubic-bezier(.2,.8,.3,1.4);
}
.tresor__somme { display: flex; align-items: center; gap: 5px }
.tresor__somme b {
  font-size: 19px; font-weight: 780; color: #6E5310;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.tresor__jeton { width: 19px; height: 19px; object-fit: contain; display: block }

/* Le gonflement bref quand les pièces tombent dedans. Louis : « ce qui fait
   gonfler brièvement l'icône du coffre quand il reçoit les pièces ». */
.carte--tresor.coffre--gonfle .tresor__coffre { transform: scale(1.24) }

/* ── La marque ─────────────────────────────────────────────────────────────
   Le nom seul ne dit rien d'un jeu qu'on ne connaît pas. Les quatre figures
   au-dessus disent tout : voilà ce que vous allez chercher. */
/* ── ENCORE `hidden` CONTRE `display` ──────────────────────────────────────
   Troisième fois dans ce projet. `.marque { display: flex }` bat l'attribut
   `hidden` du navigateur, dont la spécificité est nulle : la constellation
   s'affichait AVEC le logo au lieu de le remplacer. C'est exactement le
   défaut de `.jeu[hidden]`, reproduit par le même geste — poser `display` sur
   une classe sans écrire la ligne qui l'annule.
   Règle, désormais : toute classe qui déclare `display` déclare aussi son
   `[hidden]`, dans la foulée, sur la ligne d'après. */
.marque[hidden] { display: none }
.marque { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px }
.marque__signe { width: min(72vw, 260px); height: auto; display: block }
.marque__nom {
  margin: 0; font-size: 15px; letter-spacing: .42em; text-indent: .42em;
  color: var(--ink-soft); font-weight: 500;
}

/* ── Le bouton unique ──────────────────────────────────────────────────────
   Un seul geste évident. Tout le reste de l'écran peut être ignoré sans
   conséquence — c'est la définition d'un bon écran d'accueil. */
.jouer {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  width: 100%; padding: 15px 0 17px;
  border: none; border-radius: var(--rond-l);
  background: var(--ink); color: #fff;
  font: inherit; cursor: pointer;
  /* Le seul élément de l'interface à porter une ombre PORTÉE franche : c'est
     ce qui le fait sortir de la page. Un écran d'accueil n'a qu'un geste
     évident, et il doit être le seul objet en relief. */
  box-shadow: 0 4px 0 #C9C3B6, 0 12px 22px -12px rgba(60,50,35,.55);
  transition: transform 90ms, box-shadow 90ms, background 140ms;
}
.jouer:active {
  background: #2a2d36;
  transform: translateY(4px);
  box-shadow: 0 0 0 #C9C3B6, 0 6px 14px -12px rgba(60,50,35,.55);
}
.jouer__sur    { font-size: 12px; opacity: .62; letter-spacing: .08em; text-transform: uppercase }
.jouer__palier { font-size: 23px; font-weight: 650 }

/* ══════════════════════════════════════════════════════════════════════════
   LA BOÎTE DE DIALOGUE — une seule, réemployée
   ══════════════════════════════════════════════════════════════════════════ */

.voile {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: max(24px, env(safe-area-inset-top)) 24px max(24px, env(safe-area-inset-bottom));
  background: rgba(18, 20, 26, .42);
}
.voile[hidden] { display: none }
/* Encore la même règle : une classe qui déclare `display` déclare son
   `[hidden]`. La croix de fermeture a le sien. */
.rond[hidden] { display: none }

.boite {
  width: 100%; max-width: 340px;
  /* ── LA BOÎTE S'ADAPTE À L'ÉCRAN ──────────────────────────────────────────
     Les notes de règle grandissent avec la partie : au palier 200, elles en
     contiennent cinq, chacune avec son exemple. Sur un téléphone, la boîte
     débordait — et le bouton « J'ai compris » sortait de l'écran, ce qui est le
     pire endroit où le perdre.
     Trois bandes fixes : le titre et sa croix en haut, le pied en bas, et
     ENTRE LES DEUX le seul élément qui défile. Le joueur a toujours une sortie
     sous les yeux, quelle que soit la longueur du contenu. */
  max-height: calc(100dvh - 48px);
  display: flex; flex-direction: column;
  border-radius: var(--rond-l); background: #fff;
  /* Une ombre TEINTÉE, jamais neutre : sur un fond crème, une ombre grise
     salit tout ce qu'elle touche. Celle-ci est brune, très diluée. */
  box-shadow: 0 18px 48px rgba(80, 62, 40, .26);
  overflow: hidden;
}
.boite__haut {
  flex: none;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 14px 14px 12px 18px;
  border-bottom: 1px solid var(--paper-alt);
}
.boite__titre { margin: 0; font-size: 16px; font-weight: 620; min-width: 0 }
.boite__corps {
  flex: 1 1 auto;
  min-height: 0;                /* sans quoi un enfant flex refuse de rétrécir */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain; /* le défilement ne fuit pas vers la page */
  padding: 18px; font-size: 14.5px; line-height: 1.55; color: var(--ink-soft);
}
.boite__pied  {
  flex: none;
  display: flex; gap: 10px; padding: 14px 18px 18px;
  border-top: 1px solid var(--paper-alt);
  background: #fff;
}
/* Sans contenu, le pied n'a pas à poser un filet en travers de la boîte. */
.boite__pied:empty { display: none }
.boite__pied .bouton { flex: 1; justify-content: center }
.bouton--sombre { background: var(--ink); color: #fff; border-color: var(--ink) }
.bouton--sombre:active { background: #2a2d36 }

.reglage { display: flex; align-items: center; justify-content: space-between; padding: 9px 0 }
.reglage + .reglage { border-top: 1px solid var(--paper-alt) }
.reglage__nom { color: var(--ink) }
.bascule {
  width: 46px; height: 27px; border-radius: 999px; border: 1px solid var(--paper-alt);
  background: var(--paper-alt); position: relative; cursor: pointer; flex: none;
}
.bascule::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 21px; height: 21px; border-radius: 999px; background: #fff;
  transition: transform 150ms;
  box-shadow: 0 1px 3px rgba(0,0,0,.18);
}
.bascule[aria-pressed="true"] { background: var(--ok); border-color: var(--ok) }
.bascule[aria-pressed="true"]::after { transform: translateX(19px) }

/* Les flèches de mise au point, cachées hors du mode #dev. */
.dev { display: inline-flex; gap: 10px }
.dev[hidden] { display: none }

/* L'outil de mise au point de l'accueil ne doit RESSEMBLER à rien : ni carte,
   ni bouton d'action. Un lien discret sous le bouton Jouer, qu'on trouve quand
   on le cherche et qu'on ne clique jamais par mégarde. */
.dev--accueil { justify-content: center; margin-top: 14px }

/* L'empreinte du code, au pied de l'accueil. Discrète — elle ne doit pas
   entrer en concurrence avec les deux liens — mais SÉLECTIONNABLE : elle n'a
   d'utilité que si l'on peut la comparer, donc la lire et la recopier.
   `[hidden]` déclaré sur la ligne suivante, comme toute classe qui pose un
   `display` : sans serveur de développement, la balise n'existe pas et le
   script cache l'élément. */
.version-pied[hidden] { display: none }
.version-pied {
  display: inline-block; font-size: 11px; color: var(--ink-soft); opacity: .75;
  font-variant-numeric: tabular-nums; letter-spacing: .04em;
  -webkit-user-select: text; user-select: text; margin-right: 2px;
}
.lien-nu {
  background: none; border: 0; padding: 6px 10px; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--ink-3, #8a8a8a);
  text-decoration: underline; text-underline-offset: 3px;
}
.lien-nu:active { color: var(--ink, #222) }

.rond--pied { position: absolute; left: 16px; bottom: 14px }


/* ══════════════════════════════════════════════════════════════════════════
   LES SIGNES DE L'ÉNONCÉ
   Trois signes se posent ENTRE les icônes, et ils se combinent :
     ∿     le geste continu — les figures s'enchaînent sans lever le doigt
     ├──┤  le pont          — elles sont séparées, une route va de l'une à l'autre
     │     la barre         — aucun côté commun entre les figures
   Ils ne doivent surtout pas se confondre. Louis avait signalé très tôt qu'un
   trait entre deux icônes pouvait se lire comme « un côté entre les 2 » — et
   c'est vrai : c'est exactement ce que dit le PONT. Le trait droit à deux
   appuis lui revient donc, et le geste continu prend une onde, qui ne parle
   pas de ce qu'il y a entre les figures mais de la main qui ne s'arrête pas.
   ══════════════════════════════════════════════════════════════════════════ */
.lien { display: inline-flex; align-items: center }
.lien .but { display: inline-flex; align-items: center; gap: 0 }

/* Le petit écart tient la composition « │ ∿ » lisible : sans lui, la barre et
   le départ de l'onde se touchent et forment un signe unique. */
.sep { display: inline-flex; align-items: center; gap: 4px; color: var(--ink-soft, #7b7770) }
.sep .sep__lien { display: block; width: 28px; height: 22px; opacity: .72; margin: 0 -1px }
/* La barre est plus haute que les autres signes et deux fois moins large :
   à la taille où le joueur la lit, c'est ce contraste qui la rend
   immédiatement distincte, et non sa couleur. */
.sep .sep__barre { display: block; width: 11px; height: 26px; opacity: .78 }
/* ── LE SIGNE DU PONT ──────────────────────────────────────────────────────
   IL MANQUAIT, ET C'EST TOUT LE CHAPITRE 9 QUI EST TOMBÉ AVEC LUI. Le SVG
   était bien émis par `consigne.js`, mais sans règle CSS un SVG s'affiche en
   taille zéro : l'énoncé du pont montrait donc deux formes sans rien entre
   elles, exactement comme le geste continu. Louis :

     « Palier 81 ça parle de "><" mais dans l'énoncé lui-même, les 2 rectangles
       ne sont pas écrit "rectangle >< rectangle" […] Dans ce tutoriel, je n'ai
       absolument pas vu le gameplay du "forme + segment + forme". »

   Il ne l'a pas vu parce qu'il n'y avait rien à voir. Une classe qui déclare un
   SVG déclare aussi sa taille — c'est la même règle que « toute classe qui
   déclare `display` déclare son `[hidden]` », et c'est la cinquième fois.

   Le signe a changé depuis — c'est désormais le trait droit à deux appuis,
   celui que Louis avait inventé pour le pont. Il reste le plus large des trois :
   c'est une route, elle a besoin de longueur pour se lire comme un trajet et
   non comme un tiret. */
.sep .sep__pont { display: block; width: 34px; height: 24px; opacity: .85 }


/* ══════════════════════════════════════════════════════════════════════════
   LA NOTE DE RÈGLE
   Elle bat tant qu'elle n'a pas été lue, puis s'apaise. Elle ne disparaît
   jamais : le joueur doit pouvoir y revenir sans chercher où.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── ELLE DOIT SE LIRE COMME UN BOUTON ────────────────────────────────────
   Fond blanc, bord discret, texte gris : elle ressemblait à une étiquette. Or
   c'est désormais la seule porte vers l'explication d'un niveau une fois le
   bouton « Tuto » disparu. Un liseré plus franc, un texte plus contrasté et un
   chevron à droite suffisent — il n'y a rien à expliquer, il faut seulement
   arrêter de la déguiser en texte. */
.note {
  display: flex; align-items: center; gap: 7px;
  margin: 0 auto; padding: 6px 8px 6px 11px;
  border: 1px solid #dcd6c9; border-radius: 999px;
  background: #fff; color: var(--ink);
  font: inherit; font-size: 12px; cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.note:active { background: var(--paper-alt); box-shadow: none }
.note__chevron {
  width: 15px; height: 15px; display: block; flex: none;
  color: var(--ghost); margin-left: -1px;
}
.note__point {
  width: 7px; height: 7px; border-radius: 999px; flex: none;
  background: var(--ghost);
}
.note--neuve { color: var(--ink); border-color: #e6c96b; background: #fffdf3 }
.note--neuve .note__chevron { color: #d99b0b }
.note--neuve .note__point { background: #d99b0b; animation: battement 1.5s ease-in-out infinite }
@keyframes battement {
  0%, 100% { transform: scale(1);   opacity: 1 }
  50%      { transform: scale(1.7); opacity: .45 }
}
/* Un joueur qui a demandé moins d'animations ne doit pas voir un point battre. */
@media (prefers-reduced-motion: reduce) { .note--neuve .note__point { animation: none } }

.exemple { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle }
.exemple svg { width: 26px; height: 26px; display: block }
.exemple b { font-size: 14px }
/* « puis » : deux exemples dans le même aperçu, quand un signe se COMBINE avec
   un autre et qu'un seul dessin ne suffirait pas à le montrer. */
.exemple__ou { font-size: 12px; color: var(--ink-soft); margin: 0 8px }

/* L'empreinte du code, sous les réglages. Discrète mais SÉLECTIONNABLE : elle
   n'a d'utilité que si l'on peut la comparer, donc la lire et la recopier. */
.version {
  margin: 16px 0 0; text-align: center; font-size: 11.5px; color: var(--ink-soft);
  -webkit-user-select: text; user-select: text;
}
.version b { font-variant-numeric: tabular-nums; letter-spacing: .04em; color: var(--ink) }
.regle { padding: 4px 0 14px; border-bottom: 1px solid var(--paper-alt); margin-bottom: 14px }
.regle:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0 }
/* ── LE TITRE AU-DESSUS, L'EXEMPLE EN DESSOUS ─────────────────────────────
   Les deux étaient sur une MÊME ligne, en `flex`. Ça tenait tant qu'un exemple
   valait une icône. La règle stricte en montre deux, séparés par « puis » —
   l'exemple mesure alors trois fois la largeur disponible, et le titre se
   retrouve poussé hors de la fenêtre, coupé en trois lignes contre le bord.
   C'est ce que Louis a photographié.

   Un en-tête ne doit pas dépendre de la longueur de ce qu'il accompagne. Le
   titre prend donc sa ligne, l'exemple la sienne, et l'exemple a le droit de
   revenir à la ligne s'il est long. */
.regle__haut {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 8px; margin-bottom: 9px;
}
.regle__titre { font-size: 14.5px; font-weight: 620; color: var(--ink) }
/* La boîte de l'aperçu : elle enveloppe un ou plusieurs exemples et le mot qui
   les relie. `wrap` autorisé — deux exemples côte à côte dépassent la largeur
   d'une fenêtre sur un téléphone étroit, et une ligne coupée vaut mieux qu'une
   ligne qui sort du cadre. */
.regle__exemple {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 2px 4px; max-width: 100%;
}




/* ══════════════════════════════════════════════════════════════════════════
   LA PAGE DIRECTIONNELLE — deux moitiés

   ── LE TOURNIQUET A VÉCU ──────────────────────────────────────────────────
   Quatre bandes pavaient le bord de l'écran, autour d'une ouverture centrale.
   Louis, après l'avoir jouée : « la page directionnelle est un peu archaïque.
   On va la remodeler assez simplement. »

   Ce qui l'a tuée n'est pas le goût, c'est la géométrie : une bande qui longe
   un bord est longue et mince, donc elle ne peut porter qu'une ligne de texte
   minuscule — pendant que le centre restait vide sur les deux tiers de sa
   hauteur. La forme décidait du contenu au lieu de le servir.

   ── CE QU'ON GARDE DU TOURNIQUET ──────────────────────────────────────────
   Une seule chose, et c'est la bonne : LA POSITION DU BOUTON DIT OÙ IL MÈNE.
   Le bouton du haut mène en haut. C'est ce qui fait que le joueur choisit une
   direction sans lire, et c'est pour ça que la croix bat un menu de quatre
   lignes ou une grille 2×2, quelle que soit la place gagnée.
   ══════════════════════════════════════════════════════════════════════════ */

.horizons {
  position: fixed; inset: 0; z-index: 20;
  display: grid;
  /* ── LA PRISE MESURE SON CONTENU, LE MIROIR PREND LE RESTE ─────────────
     C'était `1fr 1fr` : deux moitiés strictement égales. La moitié haute
     portait alors trois choses — le rappel de l'icône, la carte des alentours,
     et la pastille d'accueil. Cette dernière retirée, la moitié gardait sa
     hauteur et la rendait en vide : le miroir restait exactement où il était,
     avec un trou au-dessus de lui.

     Louis : « une fois ce changement fait, tu vas remonter les choix
     directionnels du cran qui a été libéré par l'accueil. Comme ça on aura une
     interface vraiment propre. »

     `auto` fait ça tout seul, et surtout le refera : la prise prend ce que son
     contenu demande, le miroir occupe tout ce qui reste. Une valeur en dur
     réglée à l'œil sur cet état-là redeviendrait fausse au prochain élément
     ajouté ou enlevé en haut. */
  grid-template-rows: auto 1fr;
  /* ── LE FOND EST CELUI DU NIVEAU QU'ON VIENT DE JOUER ──────────────────
     Louis : « le fond de l'écran doit garder le fond d'écran du niveau
     antérieur (et non la couleur de son texte). Donc ce n'est pas du rose qu'on
     veut voir comme couleur principale, mais bien le dégradé du fond du niveau
     réalisé. »

     Il a raison, et l'erreur était de nature : j'avais peint la moitié haute
     avec la couleur VIVE de la famille — celle des carreaux et des emblèmes,
     faite pour porter du signe, pas pour tenir un demi-écran. Le dégradé du
     niveau, lui, est déjà là, sous cette page : `#jeu` le porte depuis
     `peintLeFond()`. Il suffit de ne rien mettre par-dessus.

     Transparent, donc — et la scène reculée reste visible en fantôme derrière,
     ce qui dit d'où l'on vient. */
  background: transparent;
  opacity: 1;
  animation: horizonParait 320ms cubic-bezier(.2,.7,.3,1);
}
.horizons[hidden] { display: none }

/* NI `both` NI `forwards`, NI DÉLAI. Avec `both`, l'état de départ s'applique
   pendant le délai ; si l'animation ne joue pas (onglet en arrière-plan,
   préférence système, moteur qui la saute), l'écran reste invisible ET
   cliquable. Deux fois de suite ce projet a fait dépendre la visibilité d'une
   animation, et deux fois les zones ont disparu. Sans fill-mode, l'état hors
   animation est TOUJOURS l'état de repos, et l'état de repos est visible. */
@keyframes horizonParait {
  from { opacity: 0 }
  to   { opacity: 1 }
}

/* ══════════════════════════════════════════════════════════════════════════
   LA MOITIÉ HAUTE — LA PRISE

   « La partie haute est dans la couleur de l'icône que l'on vient de collecter.
     Et d'ailleurs, la partie haute rappelle l'icône collectée en plus petit et
     l'appartenance de sa collection. »

   La couleur est posée en ligne par `main.js`, depuis la palette des familles.
   `--prise` sert de repli tant qu'aucune icône n'a été prise — sur une case
   déjà explorée rouverte avant tout gain, par exemple.
   ══════════════════════════════════════════════════════════════════════════ */
.prise {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 12px;
  padding: 14px 16px calc(14px + env(safe-area-inset-top));
  /* Plus de fond : c'est le dégradé du niveau qui passe, et c'est tout l'objet
     de la correction de Louis. La couleur vive de la famille tenait un demi-
     écran alors qu'elle est faite pour porter un signe. */
  background: none;
  color: #fff;
  min-height: 0;   /* sans quoi le contenu déborde de sa rangée de grille */
}

.prise__tete {
  display: flex; align-items: center; gap: 11px;
  max-width: 100%;
}
/* L'icône « en plus petit », comme demandé : assez grande pour se reconnaître,
   assez petite pour ne pas rejouer l'animation de récompense qu'on vient de
   voir. Elle est posée sur un creux sombre parce qu'une icône claire sur son
   propre aplat de famille disparaîtrait à moitié. */
.prise__ic {
  width: 54px; height: 54px; object-fit: contain; flex: none;
  border-radius: 13px; padding: 5px;
  background: rgba(0,0,0,.22);
}
.prise__ic[hidden] { display: none }
.prise__dit { display: flex; flex-direction: column; gap: 2px; min-width: 0 }
.prise__nom {
  font-size: 15px; font-weight: 700; line-height: 1.2;
  text-shadow: 0 1px 2px rgba(0,0,0,.32);
}
.prise__col {
  font-style: normal; font-size: 11.5px; font-weight: 560;
  color: rgba(255,255,255,.80);
  text-shadow: 0 1px 2px rgba(0,0,0,.28);
}

.horizon__carte {
  width: 152px; height: 152px; display: block;
  border-radius: var(--rond-m);
  border: 1px solid rgba(255,255,255,.16);
  background: rgba(0,0,0,.20);
}
.horizon__carte[hidden] { display: none }

/* La pastille « Accueil · partie sauvegardée » vivait ici, en verre, sous la
   carte des alentours. Elle a été retirée : la maison du pied sert désormais
   aussi cette page — voir `.jeu--monde` plus bas. */

/* ══════════════════════════════════════════════════════════════════════════
   LA MOITIÉ BASSE — LE MIROIR DE LA CONSTELLATION

   Louis : « on va faire en sorte que visuellement, les 4 boutons directionnels
   correspondent aux carreaux de la constellation, c'est-à-dire 4 carrés
   similaires. […] Au centre l'icône qu'on vient de débloquer avec sa famille,
   et autour les 4 cases avec l'emblème de ce que c'est. »

   Une grille de trois sur trois, les quatre coins vides. La position dit la
   direction — le carreau du haut mène en haut — et c'est pour ça que les
   flèches ont disparu : elles répétaient une évidence, et Louis les trouvait
   « quasi invisibles », ce qui prouvait qu'elles ne servaient à personne.
   ══════════════════════════════════════════════════════════════════════════ */
.aller {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 6px 10px calc(6px + env(safe-area-inset-bottom));
  min-height: 0;
}
.mire {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  gap: 6px;
  /* Le miroir est CARRÉ, comme la carte dont il est le reflet, et borné par la
     hauteur disponible : une grille qui déborderait de sa moitié pousserait le
     bouton du bas hors de l'écran, ce qui est le pire endroit où le perdre. */
  width: min(100%, 62dvh, 330px);
  aspect-ratio: 1;
}
.mire__case--haut   { grid-area: 1 / 2 }
.mire__case--gauche { grid-area: 2 / 1 }
.mire__centre       { grid-area: 2 / 2 }
.mire__case--droite { grid-area: 2 / 3 }
.mire__case--bas    { grid-area: 3 / 2 }

.mire__case, .mire__centre {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; min-width: 0; min-height: 0;
  border: 0; background: none; padding: 0;
  font: inherit; color: rgba(255,255,255,.86);
  cursor: pointer;
}
.mire__centre { cursor: default }

/* ── LE CARREAU ────────────────────────────────────────────────────────────
   Mêmes proportions, même arrondi, même liseré que sur la constellation : le
   joueur ne doit pas avoir à comprendre qu'il regarde la même chose. Les
   couleurs sont posées en ligne par `main.js`, depuis `familles.js` — jamais
   recopiées ici. */
.mire__tuile {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  /* ── LA HAUTEUR D'ABORD, LA LARGEUR ENSUITE ───────────────────────────
     `width: 100%` avec un rapport de 1 donnait un carreau aussi haut que la
     case entière : le compte, posé dessous, débordait dans la rangée suivante
     et passait sous le carreau du centre. Mesuré : le « 1 / 12 » du haut
     disparaissait purement et simplement.
     On part donc de la hauteur DISPONIBLE — celle de la case moins la ligne du
     compte — et la largeur suit le rapport. Le carreau ne peut plus déborder de
     ce qui lui est alloué, quelle que soit la taille de l'écran. */
  height: calc(100% - 17px); width: auto; aspect-ratio: 1;
  max-width: 100%;
  border-radius: 22%;
  background: var(--tuile, #2a2e3c);
  box-shadow: 0 2px 6px rgba(0,0,0,.28);
}
/* La case où l'on est : la couleur vive et le cercle blanc d'une case franchie,
   exactement comme sur la carte. */
/* Le centre n'a pas de compte : il prend toute sa case. */
.mire__centre .mire__tuile { height: 100% }
.mire__tuile--ici {
  box-shadow: 0 0 0 2.5px rgba(255,255,255,.92), 0 4px 12px rgba(0,0,0,.36);
}
.mire__embleme { display: flex; align-items: center; justify-content: center; width: 54%; height: 54% }
.mire__embleme .embleme { width: 100%; height: 100%; color: var(--encre, #1a1d26) }
.mire__tuile--ici .mire__embleme { position: absolute; inset: 23%; width: auto; height: auto }
.mire__prise { width: 78%; height: 78%; object-fit: contain; display: block; position: relative }
.mire__prise[hidden] { display: none }

/* Le compte SOUS le carreau, sur le fond de la page. Il était dans le bouton et
   en débordait — Louis : « on a le nombre d'icônes débloquées qui sortent des
   zones de boutons ». Un chiffre qui déborde de sa boîte fait « pas fini ». */
.mire__compte {
  font-size: 11.5px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: rgba(255,255,255,.80);
  text-shadow: 0 1px 3px rgba(0,0,0,.5);
}
.mire__case:active .mire__tuile { transform: scale(.94) }
.mire__tuile { transition: transform 110ms }

/* Une case déjà jouée : éteinte, pas barrée. Elle reste cliquable — le joueur
   DOIT pouvoir y repasser, sans quoi il peut s'enfermer dans sa constellation. */
.horizon--revu .mire__tuile { filter: saturate(.14) brightness(.62) }
.horizon--revu .mire__compte { opacity: .6; font-style: italic }

/* Le bord qui change de groupe de classement : un liseré discret, pas un cadre. */
.horizon--dehors .mire__tuile { box-shadow: 0 0 0 1.5px rgba(255,255,255,.34), 0 2px 6px rgba(0,0,0,.28) }

/* ── LE TRÉSOR DE CETTE CASE, EN MÉMOIRE ───────────────────────────────────
   En bas à droite du miroir, dans le coin resté vide. Il ne se clique pas : le
   coffre a déjà eu sa révélation. Il dit seulement, à côté de l'objet trouvé,
   ce que ce niveau a rapporté d'autre. */
.mire__tresor {
  grid-area: 3 / 3;
  width: 76%; height: 76%; object-fit: contain;
  align-self: center; justify-self: center;
  filter: drop-shadow(0 3px 7px rgba(255, 190, 80, .45));
}
.mire__tresor[hidden] { display: none }

/* ── ON PART, MAIS ON N'EST PAS ENCORE PARTI ───────────────────────────────
   Entre le doigt posé sur une direction et l'apparition du contour suivant, il
   y a un fichier de contours à lire et une image à décoder. La page reste donc
   à l'écran — c'est tout l'objet du correctif — mais elle doit DIRE qu'elle a
   entendu, sinon le joueur appuie une deuxième fois.

   Elle s'éteint doucement, et cesse d'écouter : `pointer-events: none` empêche
   qu'un second appui, sur une autre direction, ne parte en même temps. Le garde
   `attente` le protégeait déjà côté code ; ici on l'empêche à la main aussi,
   parce qu'un bouton qui ne réagit plus alors qu'il a l'air actif est le genre
   de détail qui fait croire à une application lente. */
.horizons--part { pointer-events: none }
.horizons--part .horizon { opacity: .45; transition: opacity 260ms }
.horizons--part .prise { transition: opacity 260ms }

/* ── L'ÉCRAN DE JEU AVANT D'ÊTRE PRÊT ──────────────────────────────────────
   Il paraît vide plutôt qu'habillé du niveau précédent. Tout ce qui appartient
   au niveau est masqué, le fond reste : on voit un écran qui se prépare, pas
   un écran qui n'a pas fini de se ranger. */
.jeu--attente .terrain,
.jeu--attente .collecte,
.jeu--attente .bande,
.jeu--attente .bas { opacity: 0 }

/* ── LE RETRAIT ────────────────────────────────────────────────────────────
   La scène recule et pâlit pendant que la page paraît. Elle finit cachée
   derrière deux moitiés opaques — mais le mouvement se voit pendant la
   transition, et c'est lui qui fait comprendre, sans un mot, que le niveau est
   derrière et le monde devant. */
.scene { transition: transform 300ms, opacity 300ms, filter 300ms }

/* Pendant les horizons, tout ce qui appartient au NIVEAU s'efface : il est
   derrière, le monde est devant. Rien n'est masqué brutalement — l'opacité
   suffit, et les cibles tactiles disparaissent avec elle. */
/* ── LE FOND DU NIVEAU RESTE, SON HABILLAGE PART ───────────────────────────
   La page directionnelle est maintenant TRANSPARENTE : c'est le dégradé du
   niveau, porté par `#jeu`, qui passe au travers — Louis : « le fond de l'écran
   doit garder le fond d'écran du niveau antérieur ».

   Mais tout ce qui n'est pas le fond doit disparaître, et la liste était
   incomplète : le compteur de collection et le cadre du terrain restaient. Sur
   fond opaque personne ne les voyait ; par transparence, on lisait « 1 / 11 ·
   4,1 s » et un grand rectangle vide par-dessus le miroir. **Une liste
   d'éléments à masquer est une liste qui doit être relue chaque fois que le
   fond change.** */
.jeu--monde .bande,
.jeu--monde .consigne,
.jeu--monde .collecte,
.jeu--monde .terrain,
.jeu--monde .note,
.jeu--monde .bas > :not(#maison),
.jeu--monde .ampoule {
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms;
}

/* ── LA MAISON TRAVERSE, LE PIED S'EFFACE ─────────────────────────────────
   Louis : « on va laisser l'icône Accueil, juste l'icône, en bas à gauche,
   EXACTEMENT là où elle est pendant un niveau. Et sur la page directionnelle,
   elle va rester au même endroit. »

   « Exactement » est la contrainte, et elle exclut la solution facile — un
   second bouton posé en `fixed` quelque part sur la page directionnelle. Deux
   éléments distincts finissent toujours par diverger d'un pixel ou deux au
   premier changement de rembourrage, et sur un écran de 360 px un pixel se
   voit. Il n'y a donc qu'un seul bouton, et il ne bouge pas : c'est le PIED
   qu'on vide autour de lui. La maison ne se déplace pas d'un cheveu parce qu'à
   aucun moment on ne la déplace.

   Restait qu'elle passait dessous : `#horizons` occupe tout l'écran en
   `z-index: 20`. On la relève d'un cran — `.bas` n'a pas de contexte
   d'empilement propre (`position: relative` sans `z-index`), la comparaison se
   fait donc bien entre elle et la page directionnelle. */
.jeu--monde .bas .rond--pied { position: relative; z-index: 21 }

.scene--recule {
  transform: scale(0.8);
  opacity: 0.26;
  filter: saturate(0.35);
  transition: transform 440ms cubic-bezier(.2,.7,.3,1), opacity 440ms, filter 440ms;
}

@media (prefers-reduced-motion: reduce) {
  .horizon { animation: none; opacity: 1 }
  .scene, .scene--recule { transition: none }
}

/* ══════════════════════════════════════════════════════════════════════════
   LA CONSTELLATION SUR L'ACCUEIL

   Elle prend la place de la marque — mais SEULEMENT une fois le joueur entré
   dans le monde. Avant le palier 150, il n'y aurait qu'un point au milieu du
   vide : un objet qui ne raconte rien occuperait la moitié de l'écran
   d'accueil, et le logo, lui, dit au moins ce que le jeu demande à l'œil.
   ══════════════════════════════════════════════════════════════════════════ */

.ciel {
  flex: 1;
  min-height: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 14px 0 4px;
}
.ciel[hidden] { display: none }

/* Le cadre du ciel : même famille que le cadre de figure — même liseré, même
   arrondi. Deux cadres identiques disent au joueur que ce sont deux fenêtres
   sur le même monde. */
.ciel__toile {
  /* ── CARRÉE, ET PAS SEULEMENT PAR GOÛT ────────────────────────────────────
     Le monde est une grille carrée : la carte cale son échelle sur le plus
     petit côté du canvas. Un canvas en portrait laissait donc deux bandes
     mortes, en haut et en bas, sur près d'un tiers de sa hauteur — de la
     place prise à l'écran d'accueil pour n'y rien montrer. */
  /* ── NEUF SUR CINQ, PAS UN CARRE ──────────────────────────────────────
     La toile etait carree parce que la carte calait son echelle sur le plus
     petit cote : neuf colonnes donnaient donc neuf rangees, et l'accueil
     perdait la moitie de sa hauteur en carte.

     Louis veut « 9 carreaux de largeur et 5 carreaux de hauteur, avec 1
     demi-carreau a chaque extremite ». C'est un format de FENETRE, pas de
     plan : on ne regarde plus le monde d'en haut, on regarde autour de soi.
     Le carreau y gagne le double de surface, et les icones trouvees deviennent
     lisibles au lieu d'etre devinees. */
  aspect-ratio: 9 / 5;
  width: 100%;
  max-height: 100%;
  min-height: 0;
  display: block;
  border: 1.5px solid var(--cadre-bord);
  border-radius: var(--rond-l);
  box-shadow: var(--pose);
  cursor: pointer;
  touch-action: manipulation;
}

.ciel__legende {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 11.5px; color: var(--ink-soft); text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Une case désignée se dit par son EMBLÈME — le même que sur les bords, que le
   joueur a déjà appris en choisissant sa direction. Réemployer un signe qu'il
   connaît vaut mieux que lui expliquer un mot qu'il ne connaît pas. */
.ciel__case { display: flex; align-items: center; justify-content: center; gap: 9px }
.ciel__case .embleme { width: 26px; height: 26px; color: var(--ink); flex: none }
.ciel__mots { display: flex; flex-direction: column; align-items: flex-start;
  gap: 1px; min-width: 0; text-align: left }
.ciel__case b { font-size: 14px; font-weight: 640; color: var(--ink); line-height: 1.2 }
.ciel__case i { font-style: normal; color: var(--ghost); font-size: 12px; line-height: 1.25 }
.ciel__reste { color: var(--ghost) }

/* La carte se manipule au doigt : aucun geste du navigateur ne doit voler le
   défilement ni le pincement. */
.ciel__toile { touch-action: none }

/* ══════════════════════════════════════════════════════════════════════════
   LE PALMARÈS — le modal de montée de palier

   Louis : « un modal score qui montre au joueur qu'il vient de monter en
   palier ». C'est le moment le plus important du jeu et, jusqu'ici, le plus
   discret : une ligne de texte grise sous la figure, effacée trois secondes
   plus tard.

   ── CE QU'IL MONTRE, ET POURQUOI DANS CET ORDRE ──────────────────────────
   D'abord les points GAGNÉS, gros, seuls — c'est la récompense, elle ne se
   partage pas la ligne. Puis le détail (temps, vies restantes), petit, pour
   qui veut comprendre d'où vient le chiffre. Puis le total, discret : il ne
   récompense pas ce coup-ci.
   L'inverse — total en gros — est l'erreur classique : au palier 40, le total
   écrase tellement le gain que la récompense devient invisible.
   ══════════════════════════════════════════════════════════════════════════ */

.palmares { text-align: center; padding: 2px 0 4px }

.palmares__quoi {
  font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft);
}
.palmares__gain {
  margin: 2px 0 10px;
  font-size: 44px; font-weight: 700; line-height: 1.05;
  color: var(--or); font-variant-numeric: tabular-nums;
  /* Le chiffre ARRIVE. Sans ce ressort, le modal s'ouvre déjà fini et le gain
     se lit comme un état plutôt que comme un événement. */
  animation: gainArrive 460ms cubic-bezier(.2,1.5,.4,1);
}
@keyframes gainArrive {
  from { transform: scale(.55); opacity: 0 }
  to   { transform: none; opacity: 1 }
}

.palmares__detail {
  display: flex; justify-content: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.jeton {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 11px 6px;
  border-radius: 999px;
  background: var(--creme-fort);
  font-size: 12.5px; color: #6E6455; font-variant-numeric: tabular-nums;
}
.jeton b { color: var(--ink); font-weight: 620 }

.palmares__total {
  font-size: 12.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.palmares__total b { color: var(--ink); font-weight: 620 }

/* Le lieu atteint — dans le monde, c'est l'information qui compte le plus :
   un anneau franchi vaut mieux qu'un numéro. */
.palmares__lieu {
  display: inline-flex; align-items: center; gap: 7px;
  margin-bottom: 12px; padding: 6px 13px 7px;
  border-radius: 999px;
  background: var(--or-clair); border: 1.5px solid #E8CE93;
  font-size: 13px; font-weight: 600; color: #8A6A22;
}
.palmares__lieu .embleme { width: 20px; height: 20px }

/* ══════════════════════════════════════════════════════════════════════════
   LE RECORD — un liseré, pas une fanfare
   ══════════════════════════════════════════════════════════════════════════ */
.jeton--record { background: #E8F6EC; color: #1D6B36 }
.jeton--record b { color: #14522A }

@media (prefers-reduced-motion: reduce) {
  .palmares__gain { animation: none }
  .bouton, .carte, .jouer, .horizon { transition: none }
}

/* Le palier franchi pendant le tutoriel : pas de points, donc pas de gros
   chiffre doré. Le numéro suffit, et il est plus petit — on ne célèbre pas un
   gain qui n'existe pas. */
.palmares__gain--tuto { font-size: 30px; color: var(--ink) }

/* Le bouton « Suivant » attend le palmarès. `hidden` sur un élément à
   `display` déclaré ne suffit pas — troisième leçon de ce projet, on l'écrit
   maintenant en même temps que la règle. */
.bouton[hidden] { display: none }

/* ══════════════════════════════════════════════════════════════════════════
   LA VISITE GUIDÉE — des bulles qui DÉSIGNENT

   Une boîte de dialogue cache l'écran dont elle parle. Une bulle se pose à
   côté de son sujet et l'entoure : le joueur regarde la chose pendant qu'on
   la lui nomme. C'est la différence entre apprendre et être informé.

   Le voile est volontairement PÂLE (18 %). Un voile sombre ferait de la
   constellation un souvenir au lieu d'un objet qu'on regarde — or c'est elle
   le sujet de la première bulle.
   ══════════════════════════════════════════════════════════════════════════ */
.visite {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(60, 48, 32, .18);
}
.visite[hidden] { display: none }

/* Le halo : un liseré arrondi autour de la cible, jamais un masque découpé.
   Un trou dans un voile coûte quatre rectangles et se décale d'un pixel à
   chaque redimensionnement ; un liseré désigne aussi bien et ne ment jamais. */
.visite__halo {
  position: absolute;
  border-radius: var(--rond-l);
  border: 2.5px solid #fff;
  box-shadow: 0 0 0 3px rgba(60,48,32,.28), 0 10px 30px -14px rgba(40,32,20,.7);
  pointer-events: none;
  transition: top 260ms cubic-bezier(.2,.7,.3,1), left 260ms cubic-bezier(.2,.7,.3,1),
              width 260ms cubic-bezier(.2,.7,.3,1), height 260ms cubic-bezier(.2,.7,.3,1);
}
.visite__halo[hidden] { display: none }

.visite__bulle {
  position: absolute; left: 16px; right: 16px;
  padding: 16px 17px 14px;
  border-radius: var(--rond-l);
  background: #fff;
  box-shadow: 0 18px 44px rgba(80, 62, 40, .3);
  animation: bulleParait 260ms cubic-bezier(.2,.7,.3,1);
}
@keyframes bulleParait { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: none } }

.visite__titre { margin: 0 34px 6px 0; font-size: 16px; font-weight: 650 }
.visite__texte { margin: 0 0 14px; font-size: 14px; line-height: 1.5; color: var(--ink-soft) }
.visite__texte b { color: var(--ink); font-weight: 620 }

.visite__fermer {
  position: absolute; top: 10px; right: 10px;
  width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 999px; background: transparent; color: var(--ghost); cursor: pointer;
}
.visite__fermer svg { width: 16px; height: 16px; display: block }

.visite__pied { display: flex; align-items: center; justify-content: space-between; gap: 12px }
.visite__points { display: flex; gap: 6px }
.visite__point {
  width: 6px; height: 6px; border-radius: 999px; background: var(--cadre-bord);
  transition: background 200ms, transform 200ms;
}
.visite__point--ici { background: var(--ink); transform: scale(1.25) }
.visite__suite { min-height: 40px }

@media (prefers-reduced-motion: reduce) {
  .visite__bulle { animation: none }
  .visite__halo { transition: none }
}

/* ══════════════════════════════════════════════════════════════════════════
   LE BILAN — le profil, devenu un portrait

   Huit lignes dans l'ordre où on les a apprises, puis trois chiffres. L'ordre
   compte : un classement décroissant ferait de ce tableau un palmarès, alors
   qu'on veut y lire « je n'ai jamais touché aux cercles » — et cela ne se voit
   que si la ligne des cercles est toujours à la même place.
   ══════════════════════════════════════════════════════════════════════════ */
.bilan { display: flex; flex-direction: column; gap: 1px }

.bilan__ligne {
  display: flex; align-items: center; gap: 11px;
  padding: 7px 2px;
}
.bilan__ligne + .bilan__ligne { border-top: 1px solid var(--creme-fort) }

.bilan__signe { display: flex; align-items: center }
.bilan__signe .embleme { width: 22px; height: 22px }

.bilan__nom { flex: 1; font-size: 13.5px; color: var(--ink) }
.bilan__compte { font-size: 15px; font-weight: 660; font-variant-numeric: tabular-nums }

/* Une nature jamais jouée reste LISIBLE, seulement pâlie. La faire disparaître
   priverait le joueur de la seule information qui l'inviterait à y aller. */
.bilan__ligne--vide .bilan__nom { color: var(--ghost) }
.bilan__ligne--vide .bilan__compte { color: var(--ghost); font-weight: 500 }
.bilan__ligne--vide .bilan__signe { opacity: .38 }

.bilan__pied {
  display: flex; gap: 8px; margin-top: 14px;
}
.bilan__chiffre {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 11px 6px 10px;
  border-radius: var(--rond-m);
  background: var(--creme-fort);
  text-align: center;
}
.bilan__chiffre b { font-size: 21px; font-weight: 680; font-variant-numeric: tabular-nums }
.bilan__chiffre i { font-style: normal; font-size: 10.5px; color: var(--ink-soft); line-height: 1.25 }

/* ── LE COMPTE DES AMPOULES ─────────────────────────────────────────────────
   Depuis qu'il y en a deux et que chacune coûte une vie, l'ampoule est un outil
   qu'on planifie — donc un outil dont il faut connaître la réserve. Le chiffre
   se pose en pastille sur le bouton, assez petit pour ne pas concurrencer
   l'icône, assez contrasté pour se lire d'un coup d'œil.
   Vide pendant le tutoriel, où elle est illimitée : afficher un nombre y
   laisserait croire à une réserve qu'on ne consomme pas. */
/* PAS DE `position` ICI. L'ampoule est déjà `position: absolute` plus haut, ce
   qui suffit à ancrer sa pastille. En rajoutant `position: relative`, cette
   règle — écrite plus bas, donc gagnante — lui retirait son positionnement
   absolu : le bouton retombait dans le flux et partait se ranger en bas à
   GAUCHE de l'écran. Louis : « l'ampoule est envoyée à gauche de l'écran je
   sais pas pourquoi ».

   La leçon vaut au-delà du bug : **on ne redéclare pas une propriété de
   positionnement pour obtenir un effet de décoration.** `position: relative`
   avait l'air inoffensif — il n'ajoutait rien —, et il écrasait la seule ligne
   qui plaçait le bouton. */
/* ── LA PASTILLE A ÉTÉ RETIRÉE ────────────────────────────────────────────
   Elle annonçait « 1 » pendant que le bouton refusait de servir, faute d'assez
   de vies. Un bouton dit ce qu'il peut faire MAINTENANT — voir `montreAmpoule`.
   Restent deux états, et deux suffisent : allumé, éteint. */
.ampoule--eteinte {
  color: var(--ghost);
  border-color: var(--paper-alt);
  background: var(--paper);
  box-shadow: none;
}

/* ── LES ILLUSTRATIONS DE LA VISITE ─────────────────────────────────────────
   Deux images, construites depuis les vraies données du jeu — voir la note en
   tête de `visite.js`. Elles ont besoin d'un habillage, pas d'une mise en page :
   tout ce qui les positionne vit dans le SVG, qui sait le faire mieux que des
   marges.

   LE RANG DES DIX NATURES. Sur une ligne, comme Louis l'a demandé — mais
   `flex-wrap` autorisé : sur un écran étroit, dix carreaux de 30 px et leurs
   espaces dépassent la largeur d'une bulle, et une ligne coupée vaut mieux
   qu'une ligne qui déborde. */
.visite__rang {
  /* SUR UNE SEULE LIGNE — c'est la demande, et c'est ce qui fait l'image :
     dix carreaux alignés se lisent comme une palette, deux rangs de sept et
     trois se lisent comme une liste qui a débordé. Les carreaux se serrent
     donc (`min-width: 0` + `flex: 1 1 0`) au lieu de passer à la ligne. */
  display: flex; justify-content: center;
  gap: 4px; margin: 2px 0 12px;
}
.visite__pave {
  flex: 1 1 0; min-width: 0; max-width: 30px; aspect-ratio: 1;
  border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
}
.visite__embleme { width: 64%; height: 64%; display: block }

/* L'APERÇU DE LA CARTE. Il vient SOUS le texte de la quatrième bulle : la
   phrase pose la promesse, l'image la montre. L'inverse ferait regarder avant
   de savoir quoi chercher. */
.visite__plan {
  display: block; margin: 14px auto 2px;
  max-width: 230px;
}
/* LE SÉLECTEUR VISE L'ENFANT DIRECT, ET IL LE FAUT ABSOLUMENT.
   Écrit `.visite__plan svg`, il attrapait AUSSI les emblèmes imbriqués et leur
   imposait `width: 100%` — ce qui écrasait leurs attributs `width`/`x` et les
   faisait disparaître de l'aperçu. Les vingt-cinq carreaux s'affichaient nus.
   Une règle de mise en page qui descend dans un SVG y casse une géométrie
   qu'elle ne connaît pas. */
.visite__plan > svg { display: block; width: 100% }

/* ══════════════════════════════════════════════════════════════════════════
   LE BOUTON « TUTO » ET LA MAIN DE LA DÉMONSTRATION
   ══════════════════════════════════════════════════════════════════════════

   L'explication ne s'ouvre plus d'elle-même : elle attend d'être demandée.
   Le bouton BAT pour se faire remarquer sans rien interrompre — c'est toute la
   différence entre une aide offerte et une aide imposée. */
.tuto {
  position: absolute;
  right: 16px;
  bottom: 114px;            /* juste au-dessus de l'ampoule */
  padding: 0 14px;
  height: 34px;
  border: 1px solid #e6c96b;
  border-radius: 999px;
  background: #fffdf3;
  color: #8a6508;
  font: inherit;
  font-size: 13px;
  font-weight: 650;
  cursor: pointer;
  z-index: 3;
  animation: tuto-bat 1.9s ease-in-out infinite;
}
.tuto[hidden] { display: none }
.tuto:active { background: #fdf5d8; animation: none }
@keyframes tuto-bat {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217, 155, 11, .45) }
  55%      { box-shadow: 0 0 0 9px rgba(217, 155, 11, 0) }
}
/* Un joueur qui a demandé qu'on ne l'anime pas ne doit pas voir ça battre —
   et le bouton reste parfaitement visible sans son halo. */
@media (prefers-reduced-motion: reduce) { .tuto { animation: none } }

/* LA MAIN. Elle se déplace par `transform`, jamais par `left`/`top` : c'est la
   seule façon d'obtenir un mouvement fluide sur un téléphone, parce que le
   compositeur peut l'animer sans repasser par la mise en page. */
.main-tuto {
  position: absolute;
  left: 0; top: 0;
  width: 44px; height: 44px;
  pointer-events: none;
  color: var(--ink);
  z-index: 6;
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, .25));
}
.main-tuto[hidden] { display: none }
.main-tuto svg { width: 100%; height: 100%; display: block }
/* Le « clic » : la main s'enfonce et rebondit une fois. Court — c'est un geste
   qu'on reconnaît, pas une animation qu'on regarde. */
.main-tuto--tape svg { animation: main-tape .42s ease-in-out 2 }
@keyframes main-tape {
  0%, 100% { transform: scale(1) }
  45%      { transform: scale(.78) translate(2px, 2px) }
}
/* L'ampoule répond au doigt fantôme : sans ça, on voit une main appuyer sur un
   bouton qui ne bouge pas, et le geste ne se lit plus comme un appui. */
.ampoule--appuyee { transform: scale(.9); background: #fdf5d8 }

/* Le jeton des aides, au palmarès : il dit ce qui a été déduit et pourquoi.
   Ton d'avertissement, pas d'échec — le palier est franchi. */
.jeton--aide { background: #fff4e0; color: #8a5a08 }



/* ══════════════════════════════════════════════════════════════════════════
   LA DÉCOUVERTE D'UN TRÉSOR — ELLE N'EST PLUS ICI
   Le calque, ses styles et son animation vivent dans `app/animation/`, un
   dossier autonome que Louis emportera dans d'autres projets. La feuille
   `animation/revelation.css` est chargée par `index.html` juste après
   celle-ci ; elle ne dépend d'aucune variable déclarée dans ce fichier, et
   c'est ce qui la rend transportable.
   ══════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   LE TRACE DE CONTOUR — la mecanique qui remplace la geometrie
   ═══════════════════════════════════════════════════════════════════════════ */

/* La toile occupe tout le terrain. Les deux tailles — memoire et affichage —
   sont posees par `jeu/contour.js` lui-meme : un canvas est un element remplace,
   et sans largeur CSS explicite il s'affiche a sa taille intrinseque, densite
   comprise. Cette regle-ci n'est donc qu'une securite. */
.toile-contour { display: block; width: 100%; height: 100%; touch-action: none; }
.scene { position: relative; }

.bande__lieu { display: inline-flex; align-items: center; gap: 7px; }
.embleme--lieu { width: 17px; height: 17px; }

.collecte { padding: 0 18px 4px; }
.collecte__compte { font-size: 22px; font-variant-numeric: tabular-nums; letter-spacing: .01em; }
.collecte__etat { margin-left: 10px; font-size: 12px; opacity: .62; }
.collecte__jauge { height: 4px; margin-top: 7px; border-radius: 9px;
  background: rgba(255,255,255,.10); overflow: hidden; }
.collecte__jauge i { display: block; height: 100%; border-radius: 9px;
  transition: width .35s ease; }

/* Le fond du jeu prend la couleur de la famille — pose en ligne par `main.js`,
   depuis la palette du catalogue. On ne recopie pas une table de couleurs :
   ce projet a paye quatre fois pour l'apprendre. */
.jeu { transition: background .5s ease; }

/* ═══════════════════════════════════════════════════════════════════════════
   LES ONGLETS ET LA COLLECTION
   ═══════════════════════════════════════════════════════════════════════════ */

.onglets { display: flex; gap: 6px; margin: 0 0 14px; }
.onglet { flex: 1; padding: 8px 10px; border-radius: 9px; cursor: pointer;
  border: 1px solid rgba(0,0,0,.12); background: transparent; font: inherit;
  color: inherit; opacity: .62; }
.onglet--vif { opacity: 1; background: rgba(0,0,0,.06); border-color: rgba(0,0,0,.2); }

.coll__fam { margin: 0 0 20px; }
.coll__titre { display: flex; align-items: center; gap: 8px; margin: 0 0 8px;
  color: var(--teinte); }
.coll__titre .embleme { width: 18px; height: 18px; }
.coll__titre b { font-size: 14px; }
.coll__titre i { margin-left: auto; font-style: normal; font-size: 12px; opacity: .6; }
.coll__sous { display: flex; align-items: baseline; gap: 8px; margin: 12px 0 5px;
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase; opacity: .55; }
.coll__compte { margin-left: auto; letter-spacing: 0; text-transform: none; }
.coll__grille { display: grid; gap: 5px;
  grid-template-columns: repeat(auto-fill, minmax(30px, 1fr)); }

/* ── LES MANQUANTES SONT DES SILHOUETTES, PAS DES TROUS ────────────────────
   Une grille pleine de trous ne dit pas ce qu'il reste a faire ; une grille
   pleine d'ombres, si. C'est la difference entre un compteur et une envie. */
.coll__ic { width: 100%; aspect-ratio: 1; object-fit: contain; border-radius: 7px;
  background: rgba(0,0,0,.04); }
.coll__ic--vide { display: block; background: rgba(0,0,0,.07);
  border: 1px dashed rgba(0,0,0,.14); }


/* ── L'ATTRIBUT `hidden` DOIT GAGNER ───────────────────────────────────────
   Plusieurs elements de l'ancien moteur portent un `display:flex` dans leur
   propre regle. `hidden` pose `display:none`, mais avec la MEME specificite :
   la derniere regle l'emporte, et l'ampoule restait a l'ecran alors que le
   code venait de la cacher. Un attribut qui dit « cache » et un element qui
   reste visible, c'est le genre de contradiction qu'on cherche ailleurs
   pendant une heure. */
[hidden] { display: none !important; }

/* ── LE TERRAIN S'EFFACE DEVANT LA COULEUR DE LA FAMILLE ───────────────────
   Il etait une carte de papier creme, posee sur du blanc : c'etait juste quand
   le jeu etait fait de traits noirs sur du papier. Le contour se trace
   maintenant sur le fond teinte de la famille, et un rectangle creme au milieu
   le couperait en deux. Il ne disparait pas pour autant : un liserre discret
   garde le cadre, qui dit ou le doigt a le droit d'aller. */
.terrain {
  background: transparent;
  border-color: rgba(255, 255, 255, .09);
  box-shadow: none;
}

.jeu .bande__lieu { font-weight: 600; letter-spacing: .01em; }
.message { color: rgba(255, 255, 255, .82); }

/* La collection compte 758 vignettes : a 44 px elles remplissaient six colonnes
   et la fenetre defilait sans fin. A 30 px on en voit une dizaine par rangee,
   et une sous-famille tient d'un coup d'oeil — ce qui est tout l'interet
   d'avoir decoupe les familles en petits lots. */
.coll__grille { gap: 4px; }
.coll__ic--vide { border-radius: 6px; }

/* ═══════════════════════════════════════════════════════════════════════════
   LA BARRE DU BAS, POUR LE TRACE
   ═══════════════════════════════════════════════════════════════════════════

   Elle portait cinq elements : maison, ampoule, bouton d'action, deux fleches
   de mise au point, un compteur d'images. Cinq commandes pour un jeu ou l'on
   ne fait qu'une chose — suivre un contour du doigt. Il en reste deux, et la
   maison ne se dispute plus la place avec rien. */
.jeu .bas {
  /* Une GRILLE, pas une rangee souple. En flex, `margin: 0 auto` sur le bouton
     absorbe l'espace libre et mange le `gap` : le bouton venait coller a la
     maison, exactement ce que Louis a vu. Trois colonnes fixes, et la distance
     ne depend plus de la largeur du libelle. */
  display: grid;
  /* `auto`, pas `44px` : la maison porte un `min-width: 44px` PLUS son
     rembourrage, elle deborde donc d'une colonne fixe de 44 et vient toucher le
     bouton — le gap est bien la, il est simplement derriere. Une colonne qui
     mesure son contenu ne peut pas mentir sur sa largeur. */
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  min-height: 66px;
}
.jeu .bas .bouton--large {
  grid-column: 2;
  width: 100%;
  max-width: 280px;
  justify-self: center;
}
/* La maison garde sa colonne meme quand le bouton d'action est absent : un
   element qui se deplace selon l'etat oblige a le rechercher des yeux. */
/* ── ELLE ETAIT HORS DU FLUX, D'OU LE CHEVAUCHEMENT ───────────────────────
   `.rond--pied` porte `position: absolute; left: 16px`. Une grille ne place pas
   ce qui est sorti du flux : la maison se posait par-dessus la premiere
   colonne, et le bouton, lui, commencait au bord. Le `gap` etait bien la — il
   n'y avait simplement personne pour l'occuper. On la remet dans le flux, et
   les trois colonnes redeviennent vraies. */
.jeu .bas .rond--pied { position: static; grid-column: 1; }


/* ═══════════════════════════════════════════════════════════════════════════
   L'ENCRE DE L'ECRAN DE JEU

   Le jeu etait fait de traits noirs sur du papier blanc : toute la feuille de
   style ecrit donc en encre sombre. Le fond est maintenant celui de la famille,
   tres sombre — et le texte restait noir sur noir. Ce n'est pas un detail de
   gout : le compteur de passes et le chronometre etaient litteralement
   illisibles, alors que ce sont les deux seules choses qui font le score.
   ═══════════════════════════════════════════════════════════════════════════ */
.jeu { color: #e9ebf3; }
.jeu .collecte__etat { color: rgba(233, 235, 243, .60); }
.jeu .bande__lieu { color: inherit; }
.jeu .message { color: rgba(233, 235, 243, .86); }
.jeu .terrain { border-color: rgba(255, 255, 255, .07); }


/* ═══════════════════════════════════════════════════════════════════════════
   LA RICHESSE — pastilles, pluie de pièces, fenêtre dorée
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── LA COUCHE DE VOL ──────────────────────────────────────────────────────
   Les pièces partent d'un élément qui vit DANS une boîte à défilement et
   arrivent sur un bouton qui n'y est pas. Une pièce enfant de la boîte serait
   rognée par son `overflow: hidden` dès la première frontière. Elles vivent
   donc ici, au-dessus de tout, en coordonnées d'écran. */
.pluie { position: fixed; inset: 0; z-index: 90; pointer-events: none }
.piece {
  position: absolute; top: 0; left: 0;
  /* Au-dessus du chiffre du gain, explicitement. Voir `montreLeGain` : l'ordre
     du document le faisait déjà, mais par accident. */
  z-index: 1;
  width: 22px; height: 22px; display: block;
  filter: drop-shadow(0 2px 4px rgba(120, 90, 40, .45));
  will-change: transform, opacity;
}

/* ── LA PASTILLE DE NON-LU ─────────────────────────────────────────────────
   Rouge, ronde, en haut à droite de ce qu'elle signale. C'est la convention la
   plus universelle de toutes les interfaces de téléphone : elle n'a pas besoin
   d'être apprise, et c'est précisément pour ça qu'on l'emploie ici plutôt
   qu'une invention maison.

   Elle PULSE, doucement. Une pastille immobile se fond dans le décor au bout
   de trois écrans ; celle-ci dit qu'il y a quelque chose à ouvrir, et elle le
   dit tant qu'on ne l'a pas fait. */
.pastille {
  position: absolute; top: -5px; right: -5px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 99px;
  background: var(--life); color: #fff;
  font-size: 10px; font-weight: 800; line-height: 17px; text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px #fff, 0 1px 3px rgba(0,0,0,.28);
  pointer-events: none;
  animation: pastilleBat 2.4s ease-in-out infinite;
}
@keyframes pastilleBat {
  0%, 72%, 100% { transform: scale(1) }
  80% { transform: scale(1.16) }
  88% { transform: scale(1) }
}
/* Sur une vignette d'icône, la pastille est un simple point : le nombre n'a
   aucun sens quand il vaut toujours un. */
.pastille--point { min-width: 10px; height: 10px; padding: 0; top: -2px; right: -2px }

@media (prefers-reduced-motion: reduce) {
  .pastille { animation: none }
}

/* ── LA LIGNE DU PARCOURS, DEVENUE UN LIEN ─────────────────────────────────
   Elle était un `<div>` : inatteignable au clavier, muette pour un lecteur
   d'écran, et surtout sans le moindre signe qu'on pouvait la toucher. Le
   chevron est ce qui dit qu'elle mène quelque part — une affordance qui
   n'existe que dans le code n'existe pas. */
.bilan__ligne {
  position: relative;
  width: 100%; text-align: left;
  border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer;
  border-radius: var(--rond-s);
  transition: background 140ms;
}
.bilan__ligne:active { background: var(--creme-fort) }
.bilan__chevron { color: var(--ghost); font-size: 17px; margin-left: 6px; line-height: 1 }
/* Sur une ligne pleine largeur, la pastille se met DANS le flux, après le
   chevron. En absolu elle se posait sur le compteur « 13 / 153 » : une pastille
   qui cache le chiffre qu'elle commente ne sert plus à rien. */
.bilan__ligne .pastille { position: static; margin-left: 8px; flex: none }
.bilan__ligne--vide .bilan__chevron { opacity: .4 }

/* Le titre de richesse, au pied du parcours : le seul endroit où le joueur
   regarde QUI il est, par opposition à ce qu'il possède. */
.bilan__titre {
  display: flex; align-items: baseline; gap: 8px;
  margin: 14px 0 2px; padding: 9px 12px;
  border-radius: var(--rond-s);
  background: var(--or-clair);
  font-size: 13px; font-weight: 700; color: #7a5a12;
}
.bilan__titre i { margin-left: auto; font-style: normal; font-size: 11.5px; font-weight: 500; opacity: .8 }

/* ── LA VIGNETTE DE COLLECTION, DEVENUE UN BOUTON ──────────────────────────
   `position: relative` pour porter sa pastille, et rien d'autre : la vignette
   garde exactement l'apparence qu'elle avait. Un bouton qui change de tête en
   devenant un bouton ferait douter de ce qu'on regarde. */
.coll__case {
  position: relative; display: block; padding: 0; border: 0; background: none;
  cursor: pointer; border-radius: 7px; width: 100%;
}
.coll__case:active { transform: scale(.92) }
.coll__case .coll__ic { width: 100% }

/* ── LE HALO QUI DIT « C'EST CELLE-CI » ────────────────────────────────────
   Le raccourci d'une famille dépose le joueur sur l'icône qui attend son EIKO,
   au milieu d'une grille de vignettes qui se ressemblent toutes. Sans repère,
   il ne sait pas laquelle on lui montrait — et une désignation muette ne
   désigne rien.
   Deux battements puis plus rien : le halo est un GESTE, pas un état. S'il
   restait, il deviendrait une décoration de plus dans une grille qui en a déjà
   assez, et le joueur cesserait de le voir. */
.coll__case--vise { animation: vise 1.6s ease-out both }
@keyframes vise {
  0%   { box-shadow: 0 0 0 0 var(--teinte, #e0b23a); transform: scale(1) }
  14%  { box-shadow: 0 0 0 5px var(--teinte, #e0b23a); transform: scale(1.14) }
  34%  { box-shadow: 0 0 0 2px var(--teinte, #e0b23a); transform: scale(1) }
  54%  { box-shadow: 0 0 0 5px var(--teinte, #e0b23a); transform: scale(1.09) }
  100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); transform: scale(1) }
}
@media (prefers-reduced-motion: reduce) {
  .coll__case--vise { animation: none; box-shadow: 0 0 0 3px var(--teinte, #e0b23a) }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA FICHE D'UNE ICÔNE
   ═══════════════════════════════════════════════════════════════════════════ */

.boite--fiche { max-width: 380px }
.fiche__corps { text-align: center }

.fiche__famille {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin: 0 0 10px; font-size: 12px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
}
.fiche__famille .embleme { width: 17px; height: 17px }

.fiche__scene {
  display: flex; align-items: center; justify-content: space-between; gap: 4px;
  min-height: 180px;
  /* ── LE FOND DE LA SOUS-FAMILLE, POSÉ PAR LE CODE ──────────────────────
     J'avais mis ici un damier clair — la convention des logiciels d'image pour
     montrer la transparence. Louis : « merci de remettre le fond coloré
     d'origine dédié à la sous-famille de l'icône quand le joueur clique dessus
     (et non pas le damier). »

     Il a raison, et l'erreur était de nature : cette fiche ne montre pas un
     FICHIER, elle montre un OBJET TROUVÉ. Le damier dit « voici un PNG avec un
     canal alpha » ; le fond de la sous-famille dit « voici ce que tu as
     rapporté du lieu vert » — et c'est exactement le fond sur lequel il l'a
     tracé. Ce qui suit n'est qu'un repli, le temps que `main.js` pose le
     dégradé de la famille. */
  background: #1a1d26;
  border-radius: var(--rond-m);
  padding: 10px;
  overflow: hidden;
}
/* La taille native est posée en ligne par le code ; ces bornes l'empêchent de
   déborder d'un téléphone étroit sans la réduire autrement.

   ── POURQUOI `max-width: 100 %` NE SUFFISAIT PAS ─────────────────────────
   Louis, deux captures à l'appui : « il semble que certaines icônes sortent du
   cadre dédié dans Parcours ». Mesuré sur la semi-remorque, en 360 px de large :
   l'image débordait de **28 pixels à droite** du cadre arrondi.

   Deux causes, et il fallait les deux :

     `100 %` SE COMPTE SUR LA SCÈNE ENTIÈRE, pas sur la place qui reste. La
     scène fait 256 px de contenu ; l'image y avait donc droit en entier — mais
     les deux flèches en prennent 34 chacune, et personne ne le lui avait dit.
     Elle sortait par la droite d'exactement la largeur d'une flèche.

     UN ÉLÉMENT DE FLEX NE RÉTRÉCIT PAS SOUS SON CONTENU. `min-width` vaut
     `auto` par défaut dans une rangée flexible : la largeur posée en ligne par
     le code faisait plancher, et aucune borne en pourcentage ne pouvait passer
     dessous. `min-width: 0` lève ce plancher, et le partage se fait enfin.

   La leçon est générale : dans une rangée flexible, une borne en pourcentage ne
   protège de rien tant que `min-width: 0` n'est pas posé. Et `overflow: hidden`
   sur le cadre est la ceinture — s'il naît un jour une icône que ces règles ne
   couvrent pas, elle sera rognée par le coin arrondi au lieu d'en sortir. */
.fiche__image {
  min-width: 0; flex: 0 1 auto;
  max-width: 100%; max-height: 46dvh;
  object-fit: contain; display: block; margin: auto;
}

/* Les flèches passent en verre clair : elles vivent maintenant sur le fond
   sombre de la famille, où un bouton blanc ferait une tache. */
.fiche__fleche {
  flex: none; width: 34px; height: 44px;
  border: 0; background: rgba(255,255,255,.14); border-radius: var(--rond-s);
  color: rgba(255,255,255,.92); font-size: 22px; line-height: 1; cursor: pointer;
  box-shadow: none;
}
.fiche__fleche:active { background: rgba(255,255,255,.26); transform: scale(.94) }
.fiche__fleche[hidden] { display: none }

.fiche__ou { margin: 12px 0 2px; font-size: 12.5px; color: var(--ink-soft) }
.fiche__reste { margin: 0; font-size: 13px; font-weight: 700 }
.fiche__reste--pleine { color: var(--ok) }

.fiche__points { display: flex; align-items: center; justify-content: center; gap: 5px; margin-top: 12px }
.fiche__points i { width: 5px; height: 5px; border-radius: 99px; background: var(--ghost) }
.fiche__points i.ici { background: var(--ink); width: 14px }
.fiche__rang { font-size: 11.5px; color: var(--ghost); font-variant-numeric: tabular-nums }

/* ── L'ÉCLAT DES TITRES ────────────────────────────────────────────────────
   Louis : « à chaque titre débloqué par le joueur, l'interface utilisateur
   commencerait doucement à changer d'apparence. »

   « Doucement » est l'instruction, et c'est la plus dure à tenir : la tentation
   est de faire quelque chose de VISIBLE, donc de brutal. On ne touche donc ni
   aux couleurs de famille — elles portent de l'information — ni au contraste de
   quoi que ce soit qui se lit. Une seule variable monte, de 0 à 1 sur onze
   titres : la chaleur du fond. Le joueur ne verra jamais le changement se
   produire ; il verra qu'un mois plus tard son jeu n'a plus tout à fait la même
   lumière. */
:root { --eclat: 0 }

/* ── LE FOND DE L'ACCUEIL ──────────────────────────────────────────────────
   Louis : « est-ce que sur le menu principal, tu peux ajouter en background
   l'image suivante […] J'aimerais voir si on peut habiller le background de
   l'app avec quelque chose ».

   L'image est sa propre constellation, délavée — donc du jeu, pas un décor
   plaqué dessus. Trois précautions, et chacune répare une faute qu'un fond
   d'écran commet presque toujours :

   · `background-attachment: fixed` sur le calque d'image : elle ne défile pas
     avec le contenu. Un motif qui glisse sous des cartes fixes donne le mal de
     mer, et il désigne l'image comme un ajout au lieu d'un support.

   · Un voile crème PAR-DESSUS, dans le même dégradé : l'image est déjà pâle,
     mais elle porte des dizaines de petites formes, et du texte posé sur des
     formes se lit moins bien que sur du plein — même très clair. Le voile est
     plus dense en haut, là où vivent les cartes et le bandeau, et s'ouvre vers
     le bas où il n'y a que le bouton JOUER.

   · L'ordre des couches : voile, PUIS éclat des titres, PUIS image. En CSS, la
     première couche déclarée est la plus proche de l'œil. L'éclat doré doit
     rester au-dessus de l'image — sinon la récompense de onze titres passerait
     sous un motif, et le seul changement lent du jeu deviendrait invisible.

   `--fond-voile` est là pour que tu règles la densité d'une seule ligne : 0
   montre l'image telle quelle, 1 la fait disparaître sous le crème. */
:root { --fond-voile: .62 }
.accueil {
  background-image:
    linear-gradient(180deg,
      rgba(250, 246, 238, calc(var(--fond-voile) + .16)) 0%,
      rgba(250, 246, 238, var(--fond-voile)) 46%,
      rgba(250, 246, 238, calc(var(--fond-voile) - .18)) 100%),
    radial-gradient(120% 80% at 50% 0%,
      rgba(201, 154, 46, calc(.10 * var(--eclat))) 0%,
      rgba(201, 154, 46, 0) 62%),
    url('../images/eikosi-fond.jpeg');
  background-size: cover, cover, cover;
  background-position: center, center, center;
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-attachment: scroll, scroll, fixed;
}
.jouer { box-shadow: var(--pose), 0 0 0 calc(1px * var(--eclat)) rgba(201,154,46,.55) }
.carte { border-top: 2px solid rgba(201, 154, 46, calc(.55 * var(--eclat))) }

/* ── LA RÉCOMPENSE DE COLLECTION ───────────────────────────────────────────
   Elle COUVRE les quatre directions au lieu de les remplacer : elles restent
   dessous, à leur place, et reparaissent intactes une fois la récompense prise.
   Louis : « ce bouton doit être le seul bouton cliquable » — d'où le voile
   opaque et le `pointer-events` coupé sur tout le reste de la moitié basse. */
.aller { position: relative }
.recompense {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 14px;
  border: 0; border-radius: var(--rond-m);
  background:
    radial-gradient(120% 90% at 50% 30%, rgba(255, 214, 122, .22), rgba(0,0,0,0) 70%),
    #14161f;
  color: #fff; font: inherit; cursor: pointer;
}
.recompense[hidden] { display: none !important }
.recompense__coffre {
  width: 96px; height: 96px; object-fit: contain; display: block;
  filter: drop-shadow(0 6px 14px rgba(255, 190, 80, .34));
}
.recompense__dit { display: flex; flex-direction: column; align-items: center; gap: 3px }
.recompense__dit b { font-size: 15.5px; font-weight: 780; letter-spacing: .01em }
.recompense__dit i { font-style: normal; font-size: 12px; color: rgba(255,255,255,.72) }

/* Les quatre directions cessent d'écouter tant que la récompense est due. */
.horizons--fete .horizon { pointer-events: none }

/* ── LE COFFRE QUI SE SECOUE ───────────────────────────────────────────────
   Il ne tourne pas et ne grandit pas : il TREMBLE, ce qui est le mouvement de
   quelque chose qui contient quelque chose de vivant. Une rotation ferait un
   logo qui charge ; un grossissement ferait un bouton qui répond. */
@keyframes coffreTremble {
  0%, 100% { transform: translateX(0) rotate(0) }
  12% { transform: translateX(-5px) rotate(-4deg) }
  24% { transform: translateX(5px) rotate(4deg) }
  36% { transform: translateX(-4px) rotate(-3deg) }
  48% { transform: translateX(4px) rotate(3deg) }
  60% { transform: translateX(-3px) rotate(-2deg) }
  72% { transform: translateX(3px) rotate(2deg) }
  84% { transform: translateX(-1px) rotate(-1deg) }
}
.recompense--secoue .recompense__coffre { animation: coffreTremble 900ms ease-in-out }

@media (prefers-reduced-motion: reduce) {
  .recompense--secoue .recompense__coffre { animation: none }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LE PROFIL — le parcours, le portfolio dessous, et les thèmes
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── LA LIGNE DU PARCOURS PORTE SA COULEUR ─────────────────────────────────
   Un liseré gauche, comme les cases du menu. Il ne coûte rien à la lisibilité —
   il n'y a pas de texte dessus — et il rend la liste parcourable sans lire :
   au bout de trois visites, on sait que le vert est en bas. */
.bilan__ligne {
  position: relative;
  width: 100%; text-align: left;
  border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer;
  border-radius: var(--rond-s);
  border-left: 4px solid var(--teinte, transparent);
  padding-left: 9px;
  transition: background 140ms;
}
.bilan__ligne:active { background: var(--pale, var(--creme-fort)) }
.bilan__ligne--vide { border-left-color: var(--creme-fort) }

/* ── LE DÉPLIANT ───────────────────────────────────────────────────────────
   Il ferme le Parcours et ouvre le portfolio. Replié par défaut : 758 vignettes
   ouvertes d'emblée noieraient les dix lignes qui résument tout. */
.deplie {
  display: flex; align-items: center; gap: 9px; width: 100%;
  margin: 16px 0 0; padding: 11px 12px;
  border: 1px solid var(--cadre-bord); border-radius: var(--rond-m);
  background: var(--creme); color: inherit; font: inherit;
  font-size: 13px; font-weight: 700; cursor: pointer;
  transition: background 140ms;
}
.deplie:active { background: var(--creme-fort) }
.deplie i { margin-left: auto; font-style: normal; font-size: 11.5px;
  font-weight: 500; color: var(--ghost) }
.deplie__chevron { font-size: 15px; color: var(--ghost); line-height: 1;
  transition: transform 180ms }
.deplie[aria-expanded="true"] .deplie__chevron { transform: rotate(180deg) }
.deplie[aria-expanded="true"] { border-bottom-left-radius: 0; border-bottom-right-radius: 0 }

#portfolio { margin-top: 0 }
#portfolio .coll { padding-top: 12px }

/* ── CHAQUE FAMILLE DU PORTFOLIO PORTE SA PALETTE ──────────────────────────
   Louis : « il faudrait que chaque famille de cette sélection portfolio dispose
   d'un fond ou de contour ou quelque chose de visuellement agréable qui rappelle
   la palette de couleur dédiée. »

   Le fond est très dilué et le liseré à peine plus franc. Sur des vignettes déjà
   colorées, un aplat vif entrerait en concurrence avec les icônes — qui sont le
   sujet. Il doit se voir en faisant défiler, pas en lisant. */
.coll__fam {
  padding: 11px 11px 13px;
  border-radius: var(--rond-m);
  border: 1px solid var(--teinte);
  background: color-mix(in srgb, var(--pale) 16%, #fff);
  margin-bottom: 14px;
}
/* Repli pour les moteurs sans `color-mix` : le pâle seul, à faible opacité. */
@supports not (background: color-mix(in srgb, red 10%, white)) {
  .coll__fam { background: var(--pale); }
  .coll__fam .coll__grille { background: rgba(255,255,255,.5); border-radius: 8px; padding: 4px }
}
.coll__titre { color: var(--encre, var(--teinte)) }
.coll__fam:target, .coll__fam { scroll-margin-top: 8px }

/* ── LA FLÈCHE « RETOUR EN HAUT » ──────────────────────────────────────────
   Louis : « le style de cette flèche est contour circulaire noir, centre
   transparent (donnant sur la colorimétrie de la fenêtre derrière), et flèche
   centrée noire ».

   Grisée en haut de page plutôt qu'absente : une commande qui apparaît et
   disparaît déplacerait le bouton « Fermer » sous le pouce au moment précis où
   l'on s'apprêtait à l'appuyer. La place est réservée, toujours. */
.boite__pied { display: flex; align-items: center; gap: 10px }
.boite__pied .bouton { flex: 1 }
.au-haut {
  flex: none; width: 42px; height: 42px;
  display: flex; align-items: center; justify-content: center;
  border: 1.8px solid var(--ink); border-radius: 99px;
  background: transparent; color: var(--ink);
  cursor: pointer; padding: 0;
  transition: opacity 160ms, background 140ms;
}
.au-haut svg { width: 20px; height: 20px; display: block }
.au-haut:disabled { opacity: .26; cursor: default; border-color: var(--ghost); color: var(--ghost) }
.au-haut:not(:disabled):active { background: rgba(0,0,0,.06) }

/* ── LES THÈMES ────────────────────────────────────────────────────────────
   Rien n'existe encore. La page le dit, au lieu de faire semblant : un onglet
   vide sans un mot ressemble à une panne, un onglet qui annonce ce qu'il attend
   est une promesse. */
.themes { display: flex; flex-direction: column; gap: 14px }
.themes__bourse {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 16px 12px 14px; border-radius: var(--rond-m);
  background: linear-gradient(168deg, #FFF8E6 0%, #FBEFCF 100%);
  border: 1px solid #EBD9A6;
}
.themes__coffre { width: 64px; height: 64px; object-fit: contain;
  filter: drop-shadow(0 3px 6px rgba(140,104,24,.28)) }
.themes__dit { display: flex; align-items: center; gap: 6px }
.themes__dit b { font-size: 22px; font-weight: 780; color: #6E5310;
  font-variant-numeric: tabular-nums; line-height: 1 }
.themes__jeton { width: 21px; height: 21px; object-fit: contain; display: block }
.themes__titre { font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: #8A6A16 }
.themes__mot { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-soft) }
.themes__note { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--ghost) }
.themes__grille { display: grid; grid-template-columns: 1fr 1fr; gap: 9px }
.theme {
  aspect-ratio: 4 / 5; border-radius: var(--rond-m);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px;
}
.theme--vide {
  border: 1.5px dashed var(--cadre-bord); background: var(--creme);
  color: var(--ghost);
}
.theme__cadenas { font-size: 17px; opacity: .5 }
.theme__nom { font-size: 11.5px }

/* ── LES QUATRE ANIMAUX ────────────────────────────────────────────────────
   Une case cliquable, l'animal assis dedans, son nom dessous. Le choix se voit
   au LISERÉ et à la coche, pas à une couleur de fond : les quatre vignettes ont
   déjà chacune la leur, et une cinquième couleur par-dessus les rendrait toutes
   moins lisibles. */
.theme--animal {
  border: 1.5px solid var(--cadre-bord);
  background: var(--creme);
  color: var(--ink);
  font: inherit; cursor: pointer; padding: 8px 4px;
  position: relative;
  transition: transform 90ms, border-color 140ms, box-shadow 140ms;
}
.theme--animal:active { transform: scale(.97) }
.theme__image { width: auto; height: 62%; object-fit: contain; display: block }
.theme--animal .theme__nom { font-weight: 640 }
.theme--pris {
  border-color: var(--or, #C99A2E); border-width: 2px;
  box-shadow: 0 0 0 3px rgba(201,154,46,.16);
}
/* La coche ne remplace pas le liseré, elle le double : un liseré doré seul se
   perd pour qui distingue mal les couleurs. */
.theme__coche {
  position: absolute; top: 6px; right: 6px;
  width: 19px; height: 19px; border-radius: 50%;
  background: var(--or, #C99A2E); color: #fff;
  font-size: 12px; font-weight: 800; line-height: 19px; text-align: center;
}

/* Le prix, sur ce qui n'est pas encore acquis. Discret : on veut qu'on regarde
   l'animal d'abord et son prix ensuite, pas l'inverse. */
.theme__prix {
  display: flex; align-items: center; gap: 4px;
  font-size: 11.5px; font-weight: 760; color: var(--or, #C99A2E);
  font-variant-numeric: tabular-nums;
}
.theme__jeton { width: 13px; height: 13px; object-fit: contain; display: block }
.theme--vendu .theme__image { opacity: .55; filter: saturate(.35) }

/* « Sans compagnon » : une case en pointillé, comme les emplacements vides du
   portfolio. Elle dit « rien ici » avec le vocabulaire que le jeu emploie déjà
   pour ça. */
.theme--aucun {
  border: 1.5px dashed var(--cadre-bord); background: var(--creme);
  color: var(--ghost); font: inherit; cursor: pointer;
  position: relative; padding: 8px 4px;
}
.theme--aucun.theme--pris { color: var(--ink) }
.theme__vide { font-size: 26px; opacity: .45; line-height: 1 }

/* ── LA MAIN DU TUTORIEL ───────────────────────────────────────────────────
   Elle vit dans la scène, en coordonnées de la toile : le module lui donne
   directement les coordonnées d'un échantillon du contour, sans conversion.
   `pointer-events: none` parce qu'elle passe sur la zone de tracé — et que le
   joueur doit pouvoir poser son doigt dès qu'elle a fini, sans attendre qu'elle
   s'efface. */
.main-tuto {
  position: absolute; left: 0; top: 0;
  width: 40px; height: 54px;
  margin: -6px 0 0 -13px;         /* la pointe de l'index sur le point */
  pointer-events: none;
  z-index: 6;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,.35));
  will-change: transform, opacity;
}
.main-tuto svg { width: 100%; height: 100%; display: block }
/* Pendant la démonstration, le pied ne propose rien : le seul geste à
   regarder est celui de la main. */
.jeu--demo .bas > :not(#maison) { opacity: 0; pointer-events: none }

/* ── L'AVATAR SUR L'ACCUEIL ────────────────────────────────────────────────
   Une toile qui couvre TOUT l'accueil, posée par-dessus, et qui n'attrape
   aucun doigt.

   C'était d'abord une bande de 88 px au-dessus du bouton Jouer. Elle ne pouvait
   pas répondre à ce que Louis a demandé ensuite — « le faire marcher sur des
   boutons ou sur le haut du cadre de la constellation » : un animal enfermé
   dans une bande ne peut aller nulle part.

   `pointer-events: none` n'est PAS une précaution, c'est la condition pour que
   l'accueil reste utilisable : sans elle, une toile plein écran avalerait
   chaque appui sur Jouer, sur les cartes et sur le profil. C'est le module qui
   décide, appui par appui, si le doigt visait l'animal.

   Hors du flux : elle ne prend pas un pixel à la constellation. */
.avatar__toile {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 3;
}
.avatar__toile[hidden] { display: none }

/* Le perchoir survolé pendant qu'on traîne l'animal. Discret : c'est une
   indication, pas une alarme. */
.perchoir--vise { box-shadow: 0 0 0 2px rgba(201,154,46,.55) !important }
/* Le bouton Jouer, lui, se réchauffe franchement : y lâcher l'animal LANCE la
   partie, et une action irréversible mérite d'être annoncée plus fort qu'un
   simple rangement. */
/* La toile de l'animal pendant la partie : la colonne du milieu du pied, celle
   que le bouton Rejouer occupe quand il est là. Elle ne prend aucune place
   propre — `position: absolute` dans un pied déjà dimensionné — et n'attrape
   aucun doigt. */
.avatar__jeu {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
.avatar__jeu[hidden] { display: none }

.jouer--recoit {
  background: #2b2f3d;
  box-shadow: 0 4px 0 #C9C3B6, 0 0 0 3px rgba(201,154,46,.75),
              0 12px 22px -12px rgba(60,50,35,.55);
}

/* ── LA BOUÉE « CENTRER » ──────────────────────────────────────────────────
   Elle ne paraît que lorsque plus aucune case connue n'est à l'écran. Un bouton
   « centrer » permanent serait une commande de plus à ignorer neuf fois sur
   dix ; celui-ci n'existe qu'à l'instant où il répond à une question qu'on
   vient de se poser.

   Et il clignote, parce qu'il apparaît sur un écran devenu vide : sans
   pulsation, un petit bouton sombre dans un coin de damier passerait
   inaperçu — or c'est le seul chemin de retour. */
.ciel__cadre { position: relative }
.ciel__centrer {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  padding: 7px 14px;
  border: 1.5px solid var(--ink);
  border-radius: 99px;
  background: rgba(255, 255, 255, .92);
  color: var(--ink);
  font: inherit; font-size: 12.5px; font-weight: 700;
  cursor: pointer;
  box-shadow: var(--pose);
  animation: bouee 1.5s ease-in-out infinite;
}
.ciel__centrer[hidden] { display: none }
.ciel__centrer:active { background: #fff; transform: scale(.95) }
@keyframes bouee {
  0%, 100% { opacity: 1; transform: scale(1) }
  50% { opacity: .55; transform: scale(.96) }
}
@media (prefers-reduced-motion: reduce) {
  .ciel__centrer { animation: none }
}

/* La pastille du dépliant vit DANS le flux, comme celle des lignes de famille :
   posée en absolu, elle se serait mise sur le chevron. */
.pastille--dans { position: static; flex: none; margin-left: 2px }

/* ── LE TRÉSOR, DANS LA BARRE DU BAS ──────────────────────────────────────
   À droite, sur la même ligne que la maison, comme Louis l'a demandé. Il était
   dans le coin du terrain : il venait se coucher sur le titre de la
   sous-famille, et surtout il était hors de portée du pouce — ce qui ne pouvait
   pas durer maintenant qu'on peut l'ouvrir.

   Il respire, lentement. Il ne doit pas disputer l'attention au contour — c'est
   lui qu'on trace — mais il doit se remarquer une fois, à l'ouverture de la
   case. Une pulsation lente fait exactement ça : on la voit du coin de l'œil,
   on l'oublie, et elle est encore là à la fin. */
.tresor-ici {
  grid-column: 3;
  display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; padding: 0;
  border-radius: 99px;
  background: rgba(0, 0, 0, .26);
  border: 1px solid rgba(255, 214, 122, .40);
  cursor: pointer;
  animation: tresorRespire 2.6s ease-in-out infinite;
}
.tresor-ici[hidden] { display: none !important }
.tresor-ici:active { background: rgba(255, 214, 122, .18); transform: scale(.94) }
.tresor-ici__image {
  width: 32px; height: 32px; object-fit: contain; display: block;
  filter: drop-shadow(0 2px 5px rgba(255, 190, 80, .45));
}
@keyframes tresorRespire {
  0%, 100% { transform: scale(1) }
  50% { transform: scale(1.06) }
}
/* Pendant les quatre directions, tout ce qui appartient au niveau s'efface —
   le trésor y compris : il a été pris, ou il ne l'a pas été et il est trop
   tard. */
.jeu--monde .tresor-ici, .jeu--attente .tresor-ici { opacity: 0 }
@media (prefers-reduced-motion: reduce) { .tresor-ici { animation: none } }

/* ── LE MONTANT QUI S'ÉLÈVE ────────────────────────────────────────────────
   Il vit dans la même couche que les pièces, au-dessus de tout, en coordonnées
   d'écran : posé dans la boîte de dialogue, il serait rogné par son
   `overflow` — exactement le défaut qui a mis les pièces là. */
.gain {
  position: absolute;
  font: inherit; font-size: 15px; font-weight: 820;
  color: var(--or, #C99A2E);
  text-shadow: 0 0 6px rgba(255,255,255,.9), 0 1px 3px rgba(120,90,40,.5);
  font-variant-numeric: tabular-nums;
  pointer-events: none; white-space: nowrap;
  will-change: transform, opacity;
}


/* ═══════════════════════════════════════════════════════════════════════════
   LE TITRE PORTÉ, ET SON SÉLECTEUR

   Louis : « le joueur pourrait cliquer dessus et faire défiler tous les titres
   qu'il a débloqués, avec des titres qui brilleraient plus que les autres, du
   fait qu'ils ont été trouvés en coffre. »

   Deux choses à dire dans une même liste : la HIÉRARCHIE (un numéro, à gauche,
   en chiffres tabulaires pour qu'ils s'alignent) et la RARETÉ (un éclat doré).
   Elles ne se disputent pas : l'une se lit en colonne, l'autre se voit d'un
   coup d'œil sans être lue.
   ═══════════════════════════════════════════════════════════════════════════ */
.themes__porte {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; margin: 0 0 10px; padding: 7px 12px;
  border: 0; background: none; cursor: pointer; color: inherit;
  font: inherit; font-weight: 800; font-size: 17px; letter-spacing: .01em;
  border-radius: var(--rond-m);
}
.themes__porte:active { transform: scale(.98) }
.themes__porte-chevron { font-size: 13px; opacity: .5; transition: transform .18s }
.themes__porte[aria-expanded="true"] .themes__porte-chevron { transform: rotate(180deg) }

.titres-choix {
  max-height: 42dvh; overflow: auto; margin: -4px 0 12px;
  border-radius: var(--rond-m); background: rgba(0,0,0,.035);
  padding: 4px;
}
.titres-choix[hidden] { display: none }
.titres-choix__ligne {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border: 0; background: none; cursor: pointer;
  color: inherit; font: inherit; font-size: 14px; text-align: left;
  border-radius: 9px;
}
.titres-choix__ligne:active { background: rgba(0,0,0,.06) }
.titres-choix__ligne--porte { background: rgba(0,0,0,.07); font-weight: 800 }
.titres-choix__no {
  flex: none; width: 26px; font-style: normal; font-size: 11.5px;
  color: var(--ghost); font-variant-numeric: tabular-nums; text-align: right;
}
/* ── CEUX QUI VIENNENT D'UN COFFRE BRILLENT ────────────────────────────────
   Un doré franc sur le nom, et un éclat en bout de ligne. Pas de fond coloré :
   six lignes teintées au milieu de quarante-deux en feraient un tableau à
   colonnes, alors qu'on veut une DISTINCTION — quelque chose qui se remarque
   sans qu'on ait à comparer. */
.titres-choix__ligne--coffre { color: #a8791a; font-weight: 800 }
.titres-choix__rare { margin-left: auto; font-style: normal; color: #e0b23a }
@media (prefers-color-scheme: dark) {
  .titres-choix { background: rgba(255,255,255,.05) }
  .titres-choix__ligne:active { background: rgba(255,255,255,.09) }
  .titres-choix__ligne--porte { background: rgba(255,255,255,.10) }
  .titres-choix__ligne--coffre { color: #e6c05e }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA PAROLE DU COMPAGNON

   Louis : « une phrase à afficher quand le joueur tape sur l'icône du thème,
   ou aussi sur des animations spécifiques du thème. »

   Une bulle, pas une infobulle : elle est ancrée AU-DESSUS de la tête et suit
   l'animal, parce que c'est lui qui parle. Elle ne prend aucun clic — le doigt
   qui la traverse doit atteindre ce qu'il y a dessous, y compris l'animal
   lui-même, sinon on ne pourrait plus le toucher deux fois de suite.
   ═══════════════════════════════════════════════════════════════════════════ */
.compagnon-bulle {
  position: absolute; z-index: 3; pointer-events: none;
  transform: translate(-50%, -100%) scale(.86);
  max-width: 62vw; padding: 6px 11px;
  border-radius: 13px 13px 13px 4px;
  background: var(--carte, #fff); color: var(--ink, #23272f);
  box-shadow: 0 6px 18px rgba(0,0,0,.16), 0 0 0 1px rgba(0,0,0,.05);
  font-size: 13px; font-weight: 600; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transition: opacity .18s, transform .22s cubic-bezier(.2,1.4,.4,1);
}
.compagnon-bulle--dit { opacity: 1; transform: translate(-50%, -100%) scale(1) }
@media (prefers-reduced-motion: reduce) {
  .compagnon-bulle { transition: opacity .18s }
  .compagnon-bulle--dit { transform: translate(-50%, -100%) }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LES JUMEAUX — le second mode

   Douze icônes posées librement, tournées au hasard sur le tour complet. Tout
   est en DOM et non sur la toile : un bouton tourné reçoit le doigt exactement
   là où il est dessiné, ce qu'un canevas obligerait à recalculer à la main —
   et un bouton se lit à voix haute, ce qu'un pixel ne fait pas.
   ═══════════════════════════════════════════════════════════════════════════ */
.paires { position: absolute; inset: 0; touch-action: manipulation }
.paires[hidden] { display: none }

.paire {
  position: absolute; padding: 0; border: 0; background: none; cursor: pointer;
  /* Le recentrage est DANS la transformation, avec la rotation : posé en
     `margin`, il se ferait avant elle et la tuile pivoterait autour de son coin
     au lieu de son milieu. */
  transform: translate(-50%, -50%) rotate(var(--tour, 0deg));
  transition: transform .18s cubic-bezier(.2, 1.5, .4, 1), filter .18s;
  -webkit-tap-highlight-color: transparent;
}
.paire img { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none }

/* ── LE HALO SUIT LE CONTOUR, IL N'ENCADRE PAS ─────────────────────────────
   Louis : « la sélection d'éléments serait un halo autour du contour blanc de
   chaque icône. »
   Un cadre carré autour d'une tuile tournée dirait la BOÎTE, pas l'objet — et
   la boîte est précisément ce que la rotation cherche à faire oublier.
   `drop-shadow` empilé quatre fois suit l'alpha de l'image : il épouse le
   liseré blanc, donc la silhouette. Quatre passes plutôt qu'une pour que le
   halo soit continu tout autour et non plus dense d'un côté. */
.paire--vue {
  transform: translate(-50%, -50%) rotate(var(--tour, 0deg)) scale(1.12);
  filter:
    drop-shadow(0 0 2px var(--halo, #f0932b)) drop-shadow(0 0 4px var(--halo, #f0932b))
    drop-shadow(0 0 7px var(--halo, #f0932b)) drop-shadow(0 0 12px var(--halo, #f0932b));
  z-index: 3;
}
/* Deux qui ne vont pas ensemble : le halo vire au rouge le temps qu'on le voie,
   puis tout retombe. C'est la seule sanction visible — le score, lui, se dit à
   la fin. */
.paire--faux { --halo: #e2564a }
.paire--faux { animation: paire-non .34s ease-out }
@keyframes paire-non {
  0%, 100% { margin-left: 0 }
  25% { margin-left: -5px }
  75% { margin-left: 5px }
}
.paire--pris {
  --halo: #3fbf6a;
  transform: translate(-50%, -50%) rotate(var(--tour, 0deg)) scale(1.2);
  z-index: 4;
}
/* Quand la paire tombe, les autres s'effacent sans disparaître : elles ont
   participé, et les faire s'évanouir d'un coup donnerait l'impression d'une
   erreur d'affichage. */
.paires--gagne .paire:not(.paire--pris) { filter: grayscale(.75); opacity: .38 }
@media (prefers-reduced-motion: reduce) {
  .paire, .paire--vue, .paire--pris { transition: none }
  .paire--faux { animation: none }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA PUBLICITÉ D'ESSAI

   Un rectangle qui prend TOUT l'écran, comme une interstitielle réelle, et une
   sortie qui ne s'ouvre qu'au bout du décompte. Le reste — l'étiquette, la
   trame, le panneau de mesure — sert à ce qu'on ne la confonde jamais avec le
   jeu, y compris sur une capture d'écran.

   `z-index: 70` la place au-dessus de la boîte de dialogue (50) et du calque de
   révélation : rien ne doit passer devant une publicité, sinon la mesure de la
   gêne est fausse.
   ═══════════════════════════════════════════════════════════════════════════ */
.pub {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center;
  padding: max(18px, env(safe-area-inset-top)) 18px max(18px, env(safe-area-inset-bottom));
  /* OPAQUE, et non translucide. Une interstitielle ne laisse rien voir du jeu
     derrière elle — et une simulation qui laisse deviner la carte sous la
     trame donne l'impression d'un bogue d'affichage plutôt que d'une annonce. */
  background: #0a0c12;
}
.pub[hidden] { display: none }

.pub__cadre {
  width: 100%; max-width: 360px;
  display: flex; flex-direction: column; gap: 12px;
}
.pub__etiquette {
  align-self: flex-start;
  padding: 3px 9px; border-radius: 4px;
  background: #ffd54a; color: #3a2c00;
  font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
}
/* La trame diagonale dit « emplacement réservé » dans toutes les langues : c'est
   la convention des maquettes, et elle évite d'inventer une fausse annonce qui
   finirait par ressembler à une vraie. */
.pub__toile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  min-height: 250px;                    /* le format 300×250, le plus répandu */
  border: 2px dashed rgba(255,255,255,.28);
  border-radius: 10px;
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,.05) 0 12px, transparent 12px 24px),
    #14171f;
  color: rgba(255,255,255,.72);
}
.pub__mot { font-size: 22px; font-weight: 800; letter-spacing: .16em }
.pub__mot--fin { font-size: 15px; letter-spacing: .22em; color: rgba(255,255,255,.5) }
.pub__note { margin-top: 8px; font-size: 11px; color: rgba(255,255,255,.42) }

.pub__mesure {
  padding: 11px 13px; border-radius: 10px;
  background: #171a22;
  color: rgba(255,255,255,.88); font-size: 12.5px; line-height: 1.55;
}
.pub__mesure b { display: block; margin-bottom: 5px; font-size: 11px;
  letter-spacing: .08em; text-transform: uppercase; color: rgba(255,255,255,.5) }
.pub__chiffres { display: grid; grid-template-columns: 1fr auto; gap: 2px 16px }
.pub__chiffres i { font-style: normal; color: rgba(255,255,255,.55) }
.pub__chiffres span { font-variant-numeric: tabular-nums; font-weight: 700; text-align: right }

/* Le bouton est CLAIR sur fond sombre : c'est la seule sortie de l'écran, et
   une sortie qu'on cherche des yeux est une sortie mal placée. Désactivé, il
   doit avoir l'air d'attendre et non d'être cassé — on garde sa forme, on
   retire sa promesse. */
.pub .bouton { background: #fff; color: #12141a; border-color: #fff }
.pub .bouton[disabled] { background: #2a2e39; color: rgba(255,255,255,.5);
  border-color: #2a2e39; cursor: default }

/* ═══════════════════════════════════════════════════════════════════════════
   LE COFFRE QUI S'OUVRE — voir `js/jeu/coffre.js`

   Pas de cadre. Louis, en voyant l'ancienne version : « je trouve que le cadre
   est trop gros ». Il avait raison au-delà de la taille : un cadre dit « fiche
   à lire », et une récompense ne se lit pas. Il ne reste donc que le coffre, le
   nombre, et la sortie — sur un fond assombri qui n'est là que pour que la
   pluie se voie.
   ═══════════════════════════════════════════════════════════════════════════ */
.coffret {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  background: rgba(10, 8, 4, 0);
  transition: background 240ms ease;
}
.coffret--vu { background: rgba(10, 8, 4, .90) }
.coffret--part { background: rgba(10, 8, 4, 0); transition: background 200ms ease }

.coffret__scene {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  width: 100%; max-width: 320px;
  padding: 0 20px;
}

/* Le tremblement : court, sec, et sur PLACE. Un coffre qui se déplace a l'air
   poussé ; un coffre qui vibre a l'air de contenir quelque chose qui pousse. */
.coffret__coffre {
  width: 156px; height: 156px; object-fit: contain;
  filter: drop-shadow(0 10px 22px rgba(0,0,0,.6));
  opacity: 0; transform: scale(.82);
  transition: opacity 240ms ease, transform 240ms ease;
}
.coffret--vu .coffret__coffre {
  opacity: 1; transform: scale(1);
  animation: coffretTremble 820ms 240ms ease-in-out;
}
.coffret--ouvert .coffret__coffre {
  animation: coffretSurgit 420ms cubic-bezier(.2,1.5,.4,1);
  filter: drop-shadow(0 10px 26px rgba(0,0,0,.6)) drop-shadow(0 0 26px rgba(255,196,64,.55));
}
@keyframes coffretTremble {
  0%, 100% { transform: scale(1) rotate(0) }
  8%  { transform: scale(1.02) rotate(-3.5deg) }
  20% { transform: scale(1.01) rotate(3deg) }
  32% { transform: scale(1.03) rotate(-2.5deg) }
  46% { transform: scale(1.01) rotate(2deg) }
  60% { transform: scale(1.04) rotate(-1.5deg) }
  76% { transform: scale(1.01) rotate(1deg) }
}
@keyframes coffretSurgit {
  0%   { transform: scale(1) }
  40%  { transform: scale(1.17) }
  100% { transform: scale(1) }
}

/* La pluie part DU coffre : la couche se cale sur son milieu et déborde vers le
   bas. `pointer-events: none` parce qu'une pièce qui tombe ne doit jamais voler
   le doigt qui vise le bouton. */
/* Le socle : un repère, et rien d'autre. Il fait exactement la taille du
   coffre, donc son milieu EST le milieu du coffre. */
.coffret__socle { position: relative; display: flex; line-height: 0 }
.coffret__pluie {
  position: absolute; left: 50%; top: 50%;
  width: 0; height: 0;
  pointer-events: none;
}

/* Le montant passe DEVANT la pluie. Sans `position`, il restait un élément non
   positionné : le navigateur peint alors les pièces par-dessus, et le nombre —
   la seule information de tout l'écran — se lisait à travers une averse. */
.coffret__somme {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; justify-content: center;
  color: #ffe9a8;
  opacity: 0; transform: translateY(9px);
  transition: opacity 300ms ease, transform 300ms ease;
}
.coffret--somme .coffret__somme { opacity: 1; transform: none }
.coffret__somme i {
  width: 100%; text-align: center;
  font-style: normal; font-size: 11.5px; letter-spacing: .12em;
  text-transform: uppercase; color: rgba(255,233,168,.66);
}
.coffret__somme b { font-size: 34px; font-weight: 800; font-variant-numeric: tabular-nums }
.coffret__jeton { width: 28px; height: 28px; object-fit: contain }

.coffret__bouton {
  position: relative; z-index: 2;
  min-width: 168px;
  background: #fff; color: #1a1408; border-color: #fff;
  opacity: 0; transform: translateY(9px); pointer-events: none;
  transition: opacity 260ms ease, transform 260ms ease;
}
.coffret--pret .coffret__bouton { opacity: 1; transform: none; pointer-events: auto }

@media (prefers-reduced-motion: reduce) {
  .coffret, .coffret__coffre, .coffret__somme, .coffret__bouton { transition: none }
  .coffret--vu .coffret__coffre, .coffret--ouvert .coffret__coffre { animation: none }
}

/* ── L'ÉCLAT ET LES JETS — voir `js/jeu/coffre.js` ─────────────────────────
   Tout vit dans la couche `.coffret__pluie`, déjà calée sur le centre du
   coffre. Une seconde couche aurait demandé un second calage, et deux calages
   d'un même point finissent toujours par diverger d'un pixel. */
.coffret__onde {
  position: absolute; top: 0; left: 0;
  width: 190px; height: 190px;
  margin: -95px 0 0 -95px;
  border: 16px solid rgba(255, 208, 110, .8);
  border-radius: 999px;
  pointer-events: none;
}
.coffret__paillette {
  position: absolute; top: 0; left: 0;
  background: #ffd77a;
  /* Un losange, pas un rond : une paillette ronde est une bulle. L'angle
     accroche la lumière, et c'est l'angle qui dit « éclat ». */
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  pointer-events: none;
  will-change: transform, opacity;
}
.coffret__jet {
  position: absolute; top: 0; left: 0;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.45));
  pointer-events: none;
  will-change: transform, opacity;
}

/* ── LE COMPTEUR D'EIKO DE L'ANIMATION ────────────────────────────────────
   En haut à droite, exactement là où la case dorée vit sur l'accueil. Il
   n'existe que pendant le coffre — c'est la destination des jets, et le seul
   chiffre qui compte vraiment : ce qu'on a MAINTENANT.

   `z-index: 3` : au-dessus de la scène, donc les pièces qui arrivent passent
   derrière lui et ont l'air d'y entrer. Une pièce qui s'arrête DEVANT sa cible
   n'est pas arrivée, elle s'est posée dessus. */
.coffret__compteur {
  position: absolute; z-index: 3;
  right: 14px; top: max(14px, env(safe-area-inset-top));
  display: flex; align-items: center; gap: 7px;
  padding: 7px 11px 7px 13px;
  border: 1px solid rgba(255, 213, 122, .3);
  border-radius: 999px;
  background: rgba(28, 20, 8, .92);
  opacity: 0; transform: translateY(-10px) scale(.94);
  transition: opacity 280ms ease, transform 280ms cubic-bezier(.2,1.4,.4,1);
}
.coffret--compte .coffret__compteur { opacity: 1; transform: none }
.coffret--part .coffret__compteur { opacity: 0; transform: translateY(-8px) }

.coffret__total {
  color: #ffe9a8; font-size: 17px; font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.coffret__ejeton { width: 17px; height: 17px; object-fit: contain }
/* Le coffre du menu, celui qui change au fil de la fortune. Un filet le sépare
   du nombre : sans lui, l'œil lit « 12 300 coffres ». */
.coffret__ecoffre {
  width: 26px; height: 26px; object-fit: contain;
  margin-left: 3px; padding-left: 8px;
  border-left: 1px solid rgba(255, 213, 122, .22);
  box-sizing: content-box;
}
/* Un battement court quand le compte se pose : c'est ce qui dit « c'est fini,
   voilà ce que tu as » sans écrire un mot. */
.coffret__compteur--plein { animation: coffretPose 420ms ease-out }
@keyframes coffretPose {
  0% { transform: scale(1) }
  38% { transform: scale(1.09) }
  100% { transform: scale(1) }
}
@media (prefers-reduced-motion: reduce) {
  .coffret__compteur { transition: none }
  .coffret__compteur--plein { animation: none }
}
/* Un titre n'est pas un nombre : il se lit, donc il respire, et il n'a pas
   besoin de la taille d'un chiffre pour se faire remarquer. */
.coffret__somme--titre b { font-size: 26px; letter-spacing: .01em; text-align: center }

/* ── LE COFFRE À TITRE, DANS LE PIED DU NIVEAU ────────────────────────────
   Il partage la colonne du trésor, mais il n'a ni bouton ni fond : le trésor
   invite à être touché, celui-ci ne s'ouvre qu'en gagnant. Une chose qui ne
   réagit pas au doigt ne doit pas ressembler à une chose qui y réagit.

   `grid-column: 1` — à l'opposé du trésor, du côté du bouton d'accueil, pour
   qu'une case qui porte les deux ne les empile jamais. */
.coffre-ici {
  grid-column: 2;
  justify-self: center;
  width: 38px; height: 38px; object-fit: contain;
  filter: drop-shadow(0 2px 7px rgba(255, 190, 80, .55));
  animation: coffreIciFlotte 3.1s ease-in-out infinite;
  pointer-events: none;
}
.coffre-ici[hidden] { display: none !important }
/* Il flotte au lieu de battre : la case courante bat déjà sur la carte, et deux
   pulsations dans un même écran cessent de se distinguer. */
@keyframes coffreIciFlotte {
  0%, 100% { transform: translateY(0) rotate(-2deg) }
  50%      { transform: translateY(-5px) rotate(2deg) }
}
@media (prefers-reduced-motion: reduce) {
  .coffre-ici { animation: none }
}

/* ── LES RAYONS ET LES ÉTOILES DE L'ÉCLAT ─────────────────────────────────
   Le disque de rayons est un dégradé CONIQUE : douze secteurs alternés, une
   seule couche, aucune image. Un éventail de douze éléments tournés à la main
   coûterait douze compositions pour un résultat identique.

   Il est DERRIÈRE le coffre — la couche de pluie est déclarée avant l'image
   dans la scène — ce qui est exactement ce que Louis a demandé : « les rayons
   lumineux qui tournent derrière l'icône ». */
.coffret__rayons {
  position: absolute; top: 0; left: 0;
  width: 330px; height: 330px;
  border-radius: 999px;
  background: conic-gradient(
    rgba(255,214,122,.55) 0deg 12deg, transparent 12deg 30deg,
    rgba(255,214,122,.55) 30deg 42deg, transparent 42deg 60deg,
    rgba(255,214,122,.55) 60deg 72deg, transparent 72deg 90deg,
    rgba(255,214,122,.55) 90deg 102deg, transparent 102deg 120deg,
    rgba(255,214,122,.55) 120deg 132deg, transparent 132deg 150deg,
    rgba(255,214,122,.55) 150deg 162deg, transparent 162deg 180deg,
    rgba(255,214,122,.55) 180deg 192deg, transparent 192deg 210deg,
    rgba(255,214,122,.55) 210deg 222deg, transparent 222deg 240deg,
    rgba(255,214,122,.55) 240deg 252deg, transparent 252deg 270deg,
    rgba(255,214,122,.55) 270deg 282deg, transparent 282deg 300deg,
    rgba(255,214,122,.55) 300deg 312deg, transparent 312deg 330deg,
    rgba(255,214,122,.55) 330deg 342deg, transparent 342deg 360deg);
  /* Le cœur s'efface : sans ce masque, les douze secteurs se rejoignent au
     centre en une tache pleine qui masquerait le coffre au lieu de l'auréoler. */
  mask-image: radial-gradient(closest-side, transparent 22%, #000 46%, #000 78%, transparent 100%);
  -webkit-mask-image: radial-gradient(closest-side, transparent 22%, #000 46%, #000 78%, transparent 100%);
  pointer-events: none;
}
.coffret__etoile {
  position: absolute; top: 0; left: 0;
  background: #fff8dc;
  /* Une étoile à quatre branches — la forme du scintillement, celle que l'œil
     lit comme « lumière » et non comme « objet ». */
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  filter: drop-shadow(0 0 4px rgba(255, 232, 160, .9));
  pointer-events: none;
}
/* Le gain, sous le titre : plus petit que lui, parce que c'est le titre qui est
   rare. Deux lignes de même taille se disputeraient le regard. */
.coffret__somme--titre .coffret__gain { width: 100%; justify-content: center }
.coffret__gain { display: flex; align-items: center; gap: 7px }
.coffret__somme--titre .coffret__gain b { font-size: 22px }

/* ── LE CALQUE DES SEULS JETS ──────────────────────────────────────────────
   Il se pose PAR-DESSUS la révélation — z-index 90 contre 80 — mais il ne
   prend aucun clic et n'assombrit rien : ce n'est pas une fenêtre, c'est une
   surimpression. Sans `pointer-events: none`, le bouton « Collecter » de la
   révélation deviendrait inatteignable, et le joueur serait bloqué devant sa
   propre récompense. */
.coffret--verse {
  z-index: 90;
  background: transparent !important;
  pointer-events: none;
}
.coffret--verse .coffret__compteur { pointer-events: none }

/* ── LA CASE « ÉMOTIONS · NOUS SOUTENIR » ──────────────────────────────────
   Elle est dans la grille des thèmes, à la même taille qu'eux — c'est une
   proposition parmi les autres, pas une bannière. Ce qui la distingue est sa
   couleur, pas son encombrement : une case deux fois plus grosse aurait
   ressemblé à de la publicité au milieu d'un inventaire. */
.theme--soutien {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  border-color: rgba(201, 154, 46, .55);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255, 214, 122, .30), rgba(255, 214, 122, 0) 68%),
    var(--cadre);
}
.soutien__titre { font-size: 13px; font-weight: 800; color: var(--ink) }
.soutien__sous {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  color: #8a6410;
}
.soutien__tetes { display: flex; gap: 3px; margin-top: 4px }
/* Chaque tête est la PREMIÈRE image de sa bande, recadrée sur le haut : la
   position 0 0 la cale à gauche, la hauteur réduite coupe le corps. Le module
   fournit `background-size` — lui seul connaît le nombre d'images. */
.soutien__tete {
  width: 26px; height: 22px;
  background-position: 0 0;
  background-repeat: no-repeat;
  border-radius: 5px;
}

/* ── LA FENÊTRE DE L'OFFRE ────────────────────────────────────────────────
   Le prix en grand, les quatre promesses en liste, et la mention qui dit que
   ça n'encaisse pas encore. Cette dernière n'est pas en petit : une réserve
   qu'on cache est une réserve qui trompe. */
.offre { margin: 0 }
.offre__prix {
  text-align: center; font-size: 30px; font-weight: 800; color: var(--ink);
  margin-bottom: 12px;
}
.offre__liste { margin: 0 0 12px; padding-left: 18px; line-height: 1.55 }
.offre__liste li { margin-bottom: 5px }
.offre__liste b { color: var(--ink) }
.offre__mot { margin: 0 0 10px; color: var(--ink-soft); font-size: 13px }
.offre__bientot {
  margin: 0; padding: 9px 11px; border-radius: 8px;
  background: #fff4e0; color: #8a5a08;
  font-size: 12.5px; font-weight: 700; text-align: center;
}
/* La condition d'arrêt : lisible, pas minuscule. Une clause qu'on rétrécit est
   une clause qu'on espère voir sautée. */
.offre__fin { margin: 0 0 9px; color: var(--ink-soft); font-size: 12px; line-height: 1.5 }

/* ── LE BOUTON DE LA RICHESSE ─────────────────────────────────────────────
   Même forme que celui du parcours, autre couleur de halo. Deux boutons
   identiques mèneraient au même endroit dans la tête du joueur ; deux halos
   différents suffisent à dire qu'ils mènent à deux inventaires — le violet
   pour ce qu'on a tracé, le doré pour ce qu'on a trouvé. Aucun mot à écrire. */
.rond--richesse {
  position: relative;
  border-color: #e6d29a;
  background: linear-gradient(180deg, #fff 0%, #fdf7e8 100%);
  animation: richesseRespire 2.6s ease-in-out infinite;
  /* Un temps de retard sur le halo violet : les deux au même rythme battraient
     à l'unisson et formeraient un seul objet à deux têtes. */
  animation-delay: 1.3s;
}
@keyframes richesseRespire {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 154, 46, .40), 0 0 9px 2px rgba(201, 154, 46, .22) }
  50%      { box-shadow: 0 0 0 4px rgba(201, 154, 46, .12), 0 0 17px 5px rgba(201, 154, 46, .38) }
}
.rond--richesse:active { background: #f6ecd4 }
@media (prefers-reduced-motion: reduce) {
  .rond--richesse { animation: none;
    box-shadow: 0 0 0 2px rgba(201,154,46,.14), 0 0 12px 3px rgba(201,154,46,.30) }
}

/* ── LA FENÊTRE DE LA RICHESSE ────────────────────────────────────────────
   Elle emprunte la structure du parcours — des lignes qui comptent, puis un
   portfolio — parce que c'est la même question posée sur une autre matière :
   « qu'est-ce que j'ai, et qu'est-ce qu'il me manque ». Réutiliser la forme
   évite au joueur d'apprendre deux fois le même écran. */
.riche__ligne {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 11px; margin-bottom: 6px;
  border: 1px solid var(--cadre-bord); border-left: 4px solid var(--or, #c99a2e);
  border-radius: var(--rond-s); background: var(--cadre);
}
.riche__img { width: 30px; height: 30px; object-fit: contain; flex: none }
.riche__nom { flex: 1; min-width: 0; font-weight: 700; font-size: 13.5px }
.riche__compte { font-variant-numeric: tabular-nums; font-weight: 800; font-size: 13.5px }
.riche__compte i { font-style: normal; color: var(--ink-soft); font-weight: 600 }

.riche__mot { margin: 10px 0; color: var(--ink-soft); font-size: 12.5px; line-height: 1.5 }

.riche__grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(58px, 1fr));
  gap: 6px; margin-bottom: 12px;
}
.riche__case {
  position: relative; aspect-ratio: 1;
  display: grid; place-items: center;
  padding: 4px; border: 1px solid var(--cadre-bord); border-radius: var(--rond-s);
  background: var(--cadre); cursor: pointer;
}
.riche__case img { width: 100%; height: 100%; object-fit: contain }
/* Jamais trouvé : une silhouette, pas un trou. Une grille pleine de trous ne
   dit pas ce qu'il reste à faire ; une grille pleine d'ombres, si. */
.riche__case--vide { border-style: dashed; opacity: .55 }
.riche__case--vide img { filter: grayscale(1) brightness(0) opacity(.28) }
.riche__case b {
  position: absolute; right: 2px; bottom: 1px;
  font-size: 9.5px; font-weight: 800; color: #8a6410;
  font-variant-numeric: tabular-nums;
}
.riche__titre {
  margin: 12px 0 6px; font-size: 11px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft);
}
/* Un trésor sans chance réglée : présent, mais visiblement en attente. Le trait
   pointillé plus pâle et le point d'interrogation disent « pas encore », là où
   une silhouette ordinaire dirait « pas encore trouvé » — deux états distincts
   qui ne doivent pas se confondre. */
.riche__case--muet { opacity: .3; border-style: dotted }
.riche__case--muet::after {
  content: '?'; position: absolute; right: 3px; top: 1px;
  font-size: 10px; font-weight: 800; color: var(--ink-soft);
}

/* ── LA FICHE D'UN TRÉSOR ─────────────────────────────────────────────────
   Elle se déplie DANS la grille, sur toute la largeur, juste après la vignette
   touchée. `grid-column: 1 / -1` suffit : la grille la renvoie d'elle-même à la
   ligne, sans qu'on ait à calculer combien de colonnes l'écran a servies — un
   calcul qui se serait trompé au premier changement de largeur.

   La vignette ouverte garde un liseré doré : sans lui, on ne sait plus de quel
   objet on lit les chiffres dès que la fiche a poussé les voisines. */
.riche__case--ouverte {
  border-color: var(--or, #c99a2e); box-shadow: 0 0 0 2px rgba(201,154,46,.28);
}
/* Pas encore trouvé : la vignette ne s'ouvre pas, et elle ne fait pas semblant
   de s'ouvrir. Un curseur de main sur une case inerte promet un geste qui
   n'arrivera pas. */
.riche__case--secret { cursor: default }

/* ── LE RACCOURCI VERS LES ICÔNES NON OUVERTES ─────────────────────────────
   Il porte la couleur de la pastille, et pas celle du jeu : c'est de PASTILLES
   qu'il parle, et le joueur doit faire le lien sans qu'on le lui écrive.

   ── ET IL BRILLE ──────────────────────────────────────────────────────────
   Louis : « faire en sorte que ce bouton soit brillant pour qu'il soit tout de
   suite plus visible. » Il était un contour clair sur un fond clair, au milieu
   de deux lignes de texte : correct, et invisible.

   Le reflet qui traverse est le seul mouvement de la fiche — donc le seul
   endroit où l'œil va. On ne l'a pas payé en agitation : la bande passe en
   trois secondes et demie et reste ensuite immobile près de deux secondes, ce
   qui attire sans harceler. Un clignotement à la seconde aurait fait fuir. */
.fiche__neuve {
  position: relative; overflow: hidden;
  display: block; margin: 12px auto 10px; padding: 9px 18px;
  border: 1px solid rgba(214,32,32,.55); border-radius: 999px;
  background: linear-gradient(180deg, #fff4f4, #ffe3e3);
  color: #b81f1f;
  font: inherit; font-size: 13px; font-weight: 800; letter-spacing: .01em;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 2px 10px rgba(214,32,32,.22);
}
/* Le reflet lui-même. Il est posé en `::after` pour ne pas toucher au texte, et
   `pointer-events: none` pour ne pas voler le clic à ce qu'il traverse. */
.fiche__neuve::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg,
    transparent 38%, rgba(255,255,255,.85) 48%, rgba(255,255,255,.85) 52%, transparent 62%);
  transform: translateX(-120%);
  animation: eclat 5.2s ease-in-out infinite;
}
@keyframes eclat {
  0%, 66% { transform: translateX(-120%) }
  100% { transform: translateX(120%) }
}
.fiche__neuve:active { transform: scale(.97); box-shadow: 0 1px 4px rgba(214,32,32,.24) }

/* ── QUAND IL EMMÈNE AILLEURS, IL CHANGE DE COULEUR ────────────────────────
   Le même bouton fait deux choses : rester dans la famille, ou en changer. Le
   texte le dit — le nom affiché n'est plus celui du bandeau au-dessus — mais un
   nom se lit, une couleur se voit. L'or est celle du jeu, celle des coffres et
   des cadres : elle dit « on part ailleurs » sans un mot de plus. */
.fiche__neuve--ailleurs {
  border-color: rgba(201,154,46,.6); color: #8a6412;
  background: linear-gradient(180deg, #fffaef, #ffefcd);
  box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 2px 10px rgba(201,154,46,.26);
}
.fiche__neuve--ailleurs:active { box-shadow: 0 1px 4px rgba(201,154,46,.3) }

/* Un reflet qui tourne en boucle derrière un texte est exactement ce dont on se
   passe quand on a demandé moins d'animation. Le bouton reste brillant — le
   dégradé et l'ombre ne bougent pas — il cesse seulement de scintiller. */
@media (prefers-reduced-motion: reduce) {
  .fiche__neuve::after { animation: none; opacity: 0 }
}
.riche__fiche {
  grid-column: 1 / -1;
  display: flex; gap: 11px; align-items: flex-start;
  margin: 2px 0 4px; padding: 11px 12px;
  border: 1px solid var(--cadre-bord); border-left: 4px solid var(--or, #c99a2e);
  border-radius: var(--rond-s); background: var(--cadre);
  animation: riche-deplie .18s ease-out;
}
@keyframes riche-deplie { from { opacity: 0; transform: translateY(-4px) } }
@media (prefers-reduced-motion: reduce) { .riche__fiche { animation: none } }
.riche__ficheImg { width: 54px; height: 54px; object-fit: contain; flex: none }
.riche__ficheTxt { flex: 1; min-width: 0 }
.riche__ficheNom {
  display: block; font-size: 13.5px; font-weight: 800; margin-bottom: 6px;
}
.riche__dl { margin: 0; display: grid; gap: 3px }
/* Étiquette à gauche, valeur à droite : trois lignes qui se lisent en colonne,
   et trois trésors qui se comparent d'un coup d'œil sans avoir à relire. */
.riche__f { display: grid; grid-template-columns: 74px 1fr; gap: 8px; align-items: baseline }
.riche__f dt {
  font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-soft);
}
.riche__f dd { margin: 0; font-size: 13px; font-variant-numeric: tabular-nums }
.riche__f dd b { font-weight: 800 }
/* La lecture « au ressenti » passe à la ligne, en plus pâle : elle explique le
   nombre, elle ne le concurrence pas. */
.riche__f dd i {
  display: block; font-style: normal; font-size: 11.5px; color: var(--ink-soft);
}
.riche__precision {
  margin: 7px 0 0; font-size: 12px; line-height: 1.45; color: var(--ink-soft);
}
