/* trama — feuille unique, sans dependance ni build.

   Direction : la TRAME. Les emplacements horizontaux et verticaux sont la
   chaine et la trame d'un tissage ; le chemin est le fil unique qui les
   traverse. Ce n'est pas une metaphore plaquee : c'est la these du jeu, que
   les deux couches sont inseparables. Le fil de l'en-tete obeit d'ailleurs aux
   regles du jeu -- orthogonal, jamais diagonal, jamais branche.

   Palette re-teintee a luminance quasi constante : l'identite change, les
   rapports de contraste mesures ne bougent presque pas. Les couleurs
   SEMANTIQUES (--good, --bad, --lesson) gardent leur role et ne servent
   jamais a l'identite. */

/* Polices auto-hebergees : `font-src 'self'` interdit toute origine tierce, et
   c'est tres bien ainsi -- aucun appel ne part vers Google. Sous-ensemble latin
   seul : le francais y tient en entier, accents et ligature oe compris. */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("fonts/bricolage-grotesque-latin.woff2") format("woff2");
  font-weight: 500 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Martian Mono";
  src: url("fonts/martian-mono-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Le fond n'est pas du papier : c'est de la toile ecrue. Les surfaces
     blanches se lisent alors comme des pieces d'etoffe posees dessus. */
  --bg: #efede6;
  --surface: #ffffff;
  --cell-bg: #ffffff;
  --ink: #1a1815;
  --ink-soft: #5a554b;
  --line: #d6d1c4;
  --line-strong: #2f2b25;
  --rock: #211e19;
  --rock-bord: #211e19;
  --rock-vignette: #211e19;
  --path: #d9d4c7;
  --path-edge: #9c968a;
  --path-survol: #c8c2b3;
  --excluded: #b5aea0;
  /* Indigo : la teinture du fil. Plus profonde que le bleu precedent, et
     mieux contrastee sur blanc (9,0:1 contre 6,0:1). */
  --accent: #2c3f8f;
  --accent-soft: #e6e6f4;
  --good: #0f5c34;
  --good-soft: #e7f4ed;
  --bad: #9b2323;
  --bad-soft: #fbe6e6;
  --focus: #2c3f8f;
  --lesson: #9a6700;
  --target: #eeeef8;
  --accent-fort: #1f2e6d;
  --ombre: 0 1px 2px rgba(28, 24, 16, .05), 0 8px 24px -12px rgba(28, 24, 16, .18);
  --ombre-forte: 0 2px 6px rgba(28, 24, 16, .08), 0 16px 36px -14px rgba(28, 24, 16, .26);
  --radius: 14px;
  --cell: clamp(38px, 9vw, 72px);
  --affiche: "Bricolage Grotesque", "Segoe UI", system-ui, sans-serif;
  --mono: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Le sombre n'est pas le clair inverse. Deux principes le gouvernent ici :
       1. l'elevation se dit par une surface PLUS CLAIRE, pas par une ombre --
          une ombre ne porte pas sur du noir ;
       2. l'element le plus contraste doit rester celui qui structure le jeu.
          En clair, ce sont les rochers, noirs sur blanc. En sombre, les
          rendre encore plus noirs les effacait : ils sont donc traites comme
          des TROUS sous un plateau eclairci. */
    --bg: #131419;
    --surface: #1d1f28;
    --cell-bg: #1c1e26;
    --ink: #edeef1;
    --ink-soft: #a3a9b3;
    --line: #383a46;
    --line-strong: #5e6070;
    --rock: #0b0b11;
    --rock-bord: #767786;
    /* Les vignettes du catalogue ne portent aucun chiffre : rien n'y oblige
       le rocher a rester sombre. A 16 px, un bloc clair se lit, un bloc
       presque noir borde ne se lit pas -- mesure : 1,19:1 contre la case. */
    --rock-vignette: #6e6f80;
    --path: #3c3e4a;
    --path-edge: #9698aa;
    --path-survol: #4c4e5d;
    --excluded: #8f91a1;
    --accent: #a3aef0;
    --accent-fort: #c2c9f7;
    --accent-soft: #2b2e50;
    --good: #63c795;
    --good-soft: #17332a;
    --bad: #ff9d9d;
    --bad-soft: #40262a;
    --focus: #a3aef0;
    --lesson: #e0a33a;
    --target: #262a45;
    /* Ombres presque nulles : c'est la bordure claire qui porte l'elevation. */
    --ombre: 0 1px 0 rgba(255, 255, 255, .04), 0 10px 28px -18px rgba(0, 0, 0, .9);
    --ombre-forte: 0 1px 0 rgba(255, 255, 255, .06), 0 20px 44px -22px rgba(0, 0, 0, 1);
  }
}

* { box-sizing: border-box; }

/* L'attribut `hidden` s'appuie sur `[hidden] { display: none }` de la feuille
   du navigateur. Toute regle d'auteur portant `display:` l'emporte sur elle :
   `.chooser { display: flex }` rendait la fenetre de choix impossible a fermer,
   et `main { display: grid }` affichait le plateau avant son chargement.
   On reprend donc la main explicitement. */
[hidden] { display: none !important; }

/* Lisible par les lecteurs d'ecran, invisible a l'ecran. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  padding: 0 clamp(12px, 3vw, 32px) 48px;
}

/* Meme gabarit et meme centrage pour les trois blocs, sinon le titre se
   desaligne du contenu des que l'ecran depasse la largeur maximale. */
.masthead, main, .footer { max-width: 1180px; margin-inline: auto; }

.masthead { padding: 28px 0 4px; }

/* --- Emplacements publicitaires ------------------------------------------
   La place est RESERVEE, le contenu est VIDE : aucun script tiers, aucun
   cookie, et la CSP reste `default-src 'self'`.

   Les rails sont sortis du flux (`position: fixed`) plutot que places dans la
   grille du document : un rail integre au flux decale tout le contenu des
   qu'une regle de placement se resout mal, et surtout un remplissage ulterieur
   ne doit provoquer aucun decalage de mise en page (Cumulative Layout Shift).
   Leur position se calcule depuis le centre, donc elle suit le contenu centre.

   Formats IAB : 160x600 (wide skyscraper), puis 300x600 (half page) quand
   l'ecran le permet. En dessous du seuil, aucun rail : le jeu passe avant. */
.rail {
  position: fixed;
  top: 96px;
  display: none;
  z-index: 1;
}

.ad-slot {
  width: 160px;
  height: 600px;
  border: 1px dashed var(--line);
  border-radius: 6px;
  background: transparent;
}

/* 590 px = demi-gabarit du contenu ; le reste est la gouttiere. */
@media (min-width: 1560px) {
  .rail { display: block; }
  .rail-gauche { right: calc(50% + 590px + 24px); }
  .rail-droite { left: calc(50% + 590px + 24px); }
}

@media (min-width: 1900px) {
  .ad-slot { width: 300px; }
  .rail-gauche { right: calc(50% + 590px + 32px); }
  .rail-droite { left: calc(50% + 590px + 32px); }
}


/* La marque n'est plus un h1 : chaque page a desormais le sien, qui dit son
   sujet. Repeter « trama » en h1 sur les onze pages n'apprenait rien. */
.marque {
  margin: 0;
  font-family: var(--affiche);
  font-size: clamp(26px, 4.6vw, 38px);
  letter-spacing: 0.02em;
  text-transform: lowercase;
  font-weight: 800;
  line-height: 1;
}

/* --- Le fil : element de signature ---------------------------------------
   Un fil de trame qui traverse l'en-tete de part en part, avec les memes
   pastilles que les portes d'une grille. Il se tisse au chargement -- une
   seule fois, sur une seule chose : c'est la ou l'audace se depense. */
.fil { display: block; width: 100%; height: 30px; margin: 12px 0 2px; overflow: visible; }
.fil-trace {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
  stroke-linecap: square;
  stroke-linejoin: miter;
  stroke-dasharray: 1500;
  stroke-dashoffset: 1500;
  animation: fil-tisse 900ms cubic-bezier(.33, 0, .1, 1) 80ms forwards;
}
.fil-porte { fill: var(--accent); }
@keyframes fil-tisse { to { stroke-dashoffset: 0; } }
/* Deux etapes numerotees : la boucle de jeu doit se saisir d'un coup d'oeil. */
.etapes {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: grid; gap: 10px; max-width: 900px;
}
@media (min-width: 720px) { .etapes { grid-template-columns: 1fr 1fr; } }

.etape {
  display: flex; gap: 12px; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px;
  box-shadow: var(--ombre);
}
.etape-num {
  flex: 0 0 auto;
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: #fff;
  font-weight: 700; font-size: .9rem;
}
.etape-texte { font-size: .92rem; line-height: 1.5; color: var(--ink-soft); }
.etape-texte strong { color: var(--ink); }

.hero { margin: 0 0 clamp(14px, 2vw, 22px); }
/* Sur la page de jeu, le heros est le plateau : le titre annonce, il ne
   monopolise pas. Les pages editoriales, elles, ont droit a la pleine echelle. */
.hero-titre {
  margin: 0 0 14px;
  font-family: var(--affiche);
  font-weight: 800;
  font-size: clamp(1.5rem, 4vw, 2.3rem);
  line-height: 1.04;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
.presentation { margin-top: clamp(20px, 3vw, 32px); }
/* Sous le plateau, la presentation n'est pas une carte de plus : un simple
   filet la separe, et elle rend la largeur au jeu. */
.page-jeu .presentation {
  background: transparent; border: 0; border-top: 1px solid var(--line);
  border-radius: 0; padding: clamp(18px, 3vw, 28px) 0 0;
}
.familles { margin-top: 18px; }

.hero-lien { margin: 18px 0 0; font-size: .9rem; color: var(--ink-soft); }
.hero-lien a { color: var(--accent); }

/* Ecran bas ou etroit : le jeu doit rester le premier contact. Les etapes
   sont reprises en entier sur la page des regles, on peut donc les alleger --
   puis les retirer quand la hauteur ne suffit plus. */
@media (max-width: 460px) {
  /* Sur un tres petit ecran, chaque pixel d'en-tete se paie sur le plateau :
     la grille doit rester visible a l'arrivee sans defiler. */
  .fil { height: 18px; margin: 6px 0 0; }
  .jeu-page .hero { margin-bottom: 8px; }
  .jeu-page .hero-titre { font-size: 1.15rem; margin-bottom: 8px; }
  .etapes { gap: 6px; margin-top: 12px; }
  .etape { padding: 8px 10px; gap: 8px; }
  .etape-num { width: 22px; height: 22px; font-size: .8rem; }
  .etape-texte { font-size: .84rem; line-height: 1.4; }
  /* Seul le titre de l'etape subsiste : la phrase explicative passe a la trappe. */
  .etape-texte { display: block; }
  .etape-texte strong { display: block; }
  .hero-lien { margin-top: 8px; }
}

@media (max-height: 620px) {
  .masthead { padding: 10px 0 2px; }
  .marque { font-size: 1.5rem; }
  /* En paysage, chaque ligne d'en-tete se paie sur la grille. */
  .fil, .etapes, .hero-lien { display: none; }
  .jeu-page .hero { display: none; }
  .nav { margin-top: 6px; }
  .nav a { padding: 4px 10px; font-size: .84rem; }
  .board-panel { padding: 10px 12px; }
  .panel-head { margin-bottom: 8px; }
  .progres { margin-bottom: 8px; }

  /* `--cell` est pose en ligne par le script, donc une regle `:root` ne peut
     pas le contredire. On plafonne ici, sur l'element lui-meme, par la hauteur
     disponible : en paysage, c'est elle qui manque, pas la largeur. */
  .cell { width: min(var(--cell), 8vh); height: min(var(--cell), 8vh); }
  .cell .digit { font-size: min(calc(var(--cell) * 0.36), 3vh); }
  .ring { width: min(calc(var(--cell) * 0.72), 5.8vh) !important;
          height: min(calc(var(--cell) * 0.72), 5.8vh) !important; }
}
.titre-lien { color: inherit; text-decoration: none; }
.titre-lien:hover { color: var(--accent); }

/* Aucun lien ne doit se contenter du contour par defaut du navigateur. */
a:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 2px; }

/* --- Navigation ---------------------------------------------------------- */
.nav {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 14px 0 0; padding-bottom: 2px;
}
.nav a {
  padding: 6px 12px; border-radius: 999px;
  color: var(--ink-soft); text-decoration: none;
  font-size: .9rem; border: 1px solid transparent;
}
.nav a:hover { color: var(--accent); border-color: var(--line); }
.nav a[aria-current="page"] { background: var(--accent); color: #fff; }
.nav a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* --- Pages editoriales --------------------------------------------------- */
.page { display: block; padding-top: 8px; }

.prose {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(16px, 3vw, 32px);
  max-width: 68ch;
}
.prose h1 {
  margin: 0 0 10px;
  font-family: var(--affiche);
  font-weight: 800;
  font-size: clamp(1.9rem, 5vw, 3rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.prose h2 {
  margin: 30px 0 6px;
  font-family: var(--affiche);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}
/* Un cran de chaine devant chaque section : le repere vient du tissage, pas
   d'une puce decorative. */
.prose h2::before {
  content: ""; display: inline-block;
  width: 14px; height: 2px; margin-right: 9px; vertical-align: middle;
  background: var(--accent);
}
.presentation h2:first-child { margin-top: 0; }
.prose p, .prose li { line-height: 1.65; }
.prose .chapeau { font-size: 1.05rem; color: var(--ink); margin-top: 0; }
.prose ol, .prose ul { padding-left: 1.3em; }
.prose li { margin-bottom: 6px; }
.prose a { color: var(--accent); }

.suite { margin-top: 26px; display: flex; flex-wrap: wrap; gap: 10px; }
.bouton {
  display: inline-block; padding: 9px 16px;
  font-family: var(--affiche); font-weight: 600;
  border: 1px solid var(--line-strong); border-radius: 6px;
  color: var(--ink); text-decoration: none; font-size: .95rem;
}
.bouton:hover { border-color: var(--accent); color: var(--accent); }
.bouton:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.chiffres { border-collapse: collapse; margin-top: 10px; width: 100%; }
.chiffres th, .chiffres td {
  text-align: left; padding: 7px 10px 7px 0;
  border-bottom: 1px solid var(--line); font-weight: 400;
}
.chiffres th { color: var(--ink-soft); }
.chiffres td { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Techniques ----------------------------------------------------------- */
.techniques {
  display: grid; gap: 14px; margin-top: 16px;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
.technique {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px;
}
.technique h2 { margin: 0 0 8px; font-size: 1rem; font-family: var(--affiche); font-weight: 700; }
.technique p { margin: 0 0 8px; font-size: .93rem; line-height: 1.55; }
.etiquette-couche {
  display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px;
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  background: var(--accent-soft); color: var(--accent); vertical-align: middle;
}
.exemple {
  border-left: 3px solid var(--line); padding-left: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .85rem;
}
.exemple .motif, .exemple .provenance { color: var(--ink-soft); }
.exemple .provenance { font-size: .78rem; }
.frequence { color: var(--ink-soft); font-size: .85rem; }

/* --- Catalogue ------------------------------------------------------------ */
.famille { margin-top: 26px; }
.famille { margin-top: 14px; }
.famille .compte { color: var(--ink-soft); font-weight: 400; font-family: var(--mono); font-size: .76rem; letter-spacing: -.04em; }

.grilles {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.carte-grille a {
  display: grid; justify-items: center; gap: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 10px;
  color: var(--ink); text-decoration: none; height: 100%;
}
.carte-grille a:hover { border-color: var(--accent); }
.carte-grille a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.carte-nom { font-family: var(--affiche); font-weight: 700; font-size: .98rem; }

/* Le sommaire du catalogue : cinq familles au lieu de cent vignettes. */
.grilles-familles { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.carte-famille { display: flex; flex-direction: column; }
.carte-famille a { flex: 1 1 auto; }
.carte-famille .carte-nom { font-size: 1.25rem; }
.carte-mot {
  margin: 8px 2px 0; font-size: .82rem; line-height: 1.45; color: var(--ink-soft);
}
.carte-detail { color: var(--ink-soft); font-size: .78rem; text-align: center; }

.vignette { display: block; max-width: 100%; height: auto; }
.v-case { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1; }
.v-rocher { fill: var(--rock-vignette); stroke: var(--rock-vignette); stroke-width: 1; }
.v-porte { fill: var(--accent); }

.boot { padding: 40px 0; color: var(--ink-soft); }
.boot.error { color: var(--bad); }

.jeu {
  display: grid;
  gap: clamp(16px, 2.5vw, 28px);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 900px) {
  .jeu { grid-template-columns: minmax(0, 1fr) 340px; }
  /* Le plateau reste visible pendant qu'on parcourt la colonne laterale --
     indispensable au mode guide, dont la lecon est plus bas dans la page. */
  .board-panel { position: sticky; top: 16px; }
}

.board-panel, .card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(12px, 2vw, 20px);
  box-shadow: var(--ombre);
}
.board-panel { box-shadow: var(--ombre-forte); }

@media (prefers-color-scheme: dark) {
  /* Un liseré clair au sommet suggère la lumière rasante : c'est ce qui donne
     du relief en sombre, la ou l'ombre ne porte pas. */
  .board-panel, .card, .etape { border-color: #3d3f4b; }
  .progres-piste { background: #3d3f4b; }

  /* Les aplats des cases sont contraints par la lisibilite des chiffres : ils
     ne peuvent pas remonter. Le plateau reprend donc sa presence par un cadre
     net et un halo froid, sans toucher aux fonds valides. */
  .grid {
    filter: none;
    box-shadow: 0 0 0 1px var(--line-strong),
                0 0 48px -12px rgba(163, 174, 240, .14);
  }
  .cell { border-color: #565866; }
  .cell.rock { border-color: var(--rock-bord); }
  /* Les rochers gagnent un creux : ils se lisent comme des trous. */
  .cell.rock { box-shadow: inset 0 1px 3px rgba(0, 0, 0, .9); }
}

/* Le plateau se teinte discretement a la victoire : le moment merite un signal
   qui ne soit pas qu'une ligne de texte. */
.board-panel.gagne { border-color: var(--good); }

.panel-head {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}
.panel-head h2 { margin: 0; font-size: 1.15rem; font-weight: 650; }

.picker {
  display: flex; align-items: center; gap: 8px;
  /* Sans `min-width: 0`, un element flex refuse de descendre sous la largeur
     de son contenu : le selecteur debordait de la carte et de l'ecran. */
  min-width: 0; flex: 1 1 12rem;
}
.picker label {
  font-size: .76rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-soft);
}
.picker select {
  min-width: 0; flex: 1 1 auto; max-width: 100%;
  font: inherit; font-size: .92rem;
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
}
.picker select:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }



.modes {
  display: flex; gap: 0; border: 1px solid var(--line);
  border-radius: 999px; overflow: hidden;
  /* Ne jamais retrecir : le conteneur ecrete ce qui depasse, et « Chemin »
     se faisait couper des que le selecteur reclamait de la place. */
  flex: 0 0 auto;
}
.mode {
  border: 0; background: transparent; color: var(--ink-soft);
  padding: 9px 16px 9px 12px; font: inherit; font-size: 0.92rem; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  /* C'est le conteneur qui porte la forme en pilule : un arrondi propre au
     bouton laissait une couture visible sur le segment actif. */
  border-radius: 0;
}
.mode[aria-checked="true"] { background: var(--accent); color: #fff; }
.mode-num {
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: .74rem; font-weight: 700;
  background: var(--line); color: var(--ink-soft);
}
.mode[aria-checked="true"] .mode-num { background: rgba(255, 255, 255, .28); color: #fff; }

/* Avancement de la couche cartouches : la seule qui ait un denominateur. */
.progres { display: flex; align-items: center; gap: 12px; margin: 0 0 14px; }
.progres-piste {
  flex: 1 1 auto; height: 6px; border-radius: 999px;
  background: var(--line); overflow: hidden;
}
.progres-part {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--accent), var(--accent-fort));
  border-radius: 999px;
  transition: width .25s ease;
}
.progres-texte {
  flex: 0 0 auto; font-size: .82rem; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.board-panel.gagne .progres-part { background: var(--good); }

.grid-scroll { overflow-x: auto; padding-bottom: 4px; }

/* Anneau exterieur d'une case pour loger les fleches. */
.grid {
  display: grid; gap: 0; width: max-content; margin: 0 auto;
  filter: drop-shadow(0 6px 18px rgba(16, 20, 28, .10));
  /* Un glisser sur la grille ne doit pas selectionner les chiffres, et
     `touch-action: none` est indispensable pour que le trace fonctionne au
     doigt : sans lui le navigateur interprete le geste comme un defilement. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.ring { display: flex; align-items: center; justify-content: center; color: var(--ink); }
.ring .arrow { font-size: calc(var(--cell) * 0.42); line-height: 1; }

.cell {
  width: var(--cell);
  height: var(--cell);
  margin: 0;
  padding: 0;
  border: 1px solid var(--line-strong);
  background: var(--cell-bg);
  color: var(--ink);
  font: inherit;
  position: relative;
  cursor: pointer;
  display: grid;
  place-items: center;
  border-radius: 0;
  opacity: 1;
  transition: background-color .08s linear;
}

/* Affordance de survol : la grille doit se sentir cliquable. */
.cell:not(.rock):hover { background: var(--target); }
.cell.on-path:hover { background: var(--path-survol); }
.cell:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; z-index: 3; }
/* Les rochers sont l'ossature du puzzle. En sombre, un remplissage encore
   plus noir les effacait : leur bordure claire les detache desormais des
   cases jouables, ce qu'aucun ecart d'aplat ne peut faire entre deux
   surfaces sombres sans rendre le texte illisible ailleurs. */
.cell.rock {
  background: var(--rock); border-color: var(--rock-bord);
  cursor: default; opacity: 1;
}
.cell.on-path { background: var(--path); box-shadow: inset 0 0 0 1px var(--path-edge); }
/* Cumule avec l'ombre interieure du chemin : sans cela, une fleche posee
   sur le chemin perdait son lisere. */
.cell.gate { box-shadow: inset 0 0 0 2px var(--accent); }
.cell.gate.on-path { box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 0 1px var(--path-edge); }

.cell .digit {
  font-family: var(--mono);
  /* Martian Mono est large : on la resserre pour qu'un chiffre tienne dans sa
     case aux petites tailles sans reduire sa hauteur d'x. */
  font-size: calc(var(--cell) * 0.36);
  letter-spacing: -.04em;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* Poids ET couleur : un indice reconnaissable sans percevoir le bleu. */
.cell .digit.given { color: var(--accent); font-weight: 800; }
.cell .digit.conflict { color: var(--bad); background: var(--bad-soft); border-radius: 4px; padding: 0 .25em; }
.cell .digit.impossible { color: var(--bad); text-decoration: line-through; }
.cell .digit.satisfied { color: var(--good); }

/* Marque « hors chemin » : un point discret, lisible sous un chiffre. */
.cell.off-path::after {
  content: "";
  position: absolute;
  inset: auto 3px 3px auto;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--ink-soft);
}
/* Guider sans dominer : le conflit et le chemin doivent rester lisibles. */
.cell.empty-slot-target { background: var(--target); }

/* Apercu de l'emplacement survole dans la fenetre de choix : il doit se voir
   par-dessus la teinte des candidats, y compris sous le menu. */
.cell.slot-preview { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); }
/* Trait tirete et teinte propre : la lecon ne doit pas se confondre avec le
   liseré plein des fleches. */
.cell.tuto-focus {
  outline: 3px dashed var(--lesson);
  outline-offset: -3px;
  z-index: 2;
}

.status { margin: 14px 0 0; font-weight: 600; min-height: 1.55em; }

/* La victoire est le seul moment ou la page doit hausser le ton. */
.status.win {
  color: var(--good);
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--good-soft); border: 1px solid var(--good);
  border-radius: 999px; padding: 8px 18px 8px 14px;
}
.status.win::before {
  content: "✓";
  display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--good); color: var(--surface);
  font-size: .8rem; font-weight: 700;
}
.hint { margin: 4px 0 0; color: var(--ink-soft); font-size: .9rem; min-height: 1.4em; }

.side { display: grid; gap: clamp(12px, 2vw, 18px); }
.card h3 { margin: 0 0 6px; font-size: 1rem; font-weight: 650; }
.card > summary { cursor: pointer; list-style: revert; }
.card > summary h3 { display: inline; }
.card-note { margin: 0 0 10px; color: var(--ink-soft); font-size: .88rem; }

.tray { display: flex; flex-wrap: wrap; gap: 6px; }
.tray-group { width: 100%; }
.tray-group h4 {
  margin: 8px 0 4px; font-size: .76rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ink-soft);
}
.tray-group .row { display: flex; flex-wrap: wrap; gap: 6px; }

.cartridge {
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: .1em;
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.cartridge[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
.cartridge.used { opacity: .32; cursor: default; text-decoration: line-through; }

/* Le chiffre qui distingue une cartouche de sa quasi-jumelle. Sans lui, il
   fallait comparer « 3 4 4 3 2 », « 3 4 5 3 2 » et « 3 4 3 3 2 » chiffre a
   chiffre pour les departager. */
.cartridge .distinctif {
  color: var(--accent);
  font-weight: 700;
  border-bottom: 2px solid var(--accent);
}
.cartridge[aria-pressed="true"] .distinctif { color: #fff; border-bottom-color: #fff; }

.buttons { display: flex; flex-wrap: wrap; gap: 8px; }
/* `:not(.cell)` : ce style habille les boutons de l'interface, jamais les
   cases de la grille, qui ont leur propre apparence. */
button:not(.cell) {
  font: inherit; font-size: .9rem;
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
button:not(.cell):hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
button:not(.cell):disabled { opacity: .4; cursor: default; }
button.danger:hover { border-color: var(--bad); color: var(--bad); }
button:not(.cell):focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.tuto { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 12px; }
.tuto-progress { margin: 0 0 6px; font-size: .82rem; color: var(--ink-soft); }
.layer-tag {
  display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px;
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  background: var(--accent-soft); color: var(--accent);
}
.tuto-technique { margin: 0; font-weight: 650; }
.tuto-conclusion { margin: 4px 0 0; }
.tuto-reason { margin: 4px 0 12px; color: var(--ink-soft); font-size: .9rem; }

.metrics { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; margin: 0; font-size: .9rem; }
.metrics dt { color: var(--ink-soft); }
.metrics dd {
  margin: 0; text-align: right;
  font-family: var(--mono); font-size: .8rem; letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}

.rules-list { margin: 8px 0 0; padding-left: 1.2em; font-size: .9rem; color: var(--ink-soft); }
.rules-list li { margin-bottom: 4px; }

.chooser {
  position: absolute;
  z-index: 20;
  display: flex;
  gap: 6px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
}

.footer { margin-top: 28px; color: var(--ink-soft); font-size: .85rem; }

/* --- Fil d'Ariane ---------------------------------------------------------
   Affiche uniquement la ou il y a une vraie profondeur -- les pages de
   famille. Les pages du menu n'en ont pas : la navigation, qui marque la page
   courante, en tient deja lieu, et la donnee structuree suit ce choix. */
.ariane {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  margin: 0 0 12px; font-size: .84rem; color: var(--ink-soft);
}
.ariane a { color: var(--ink-soft); }
.ariane a:hover { color: var(--accent); }
.ariane [aria-current="page"] { color: var(--ink); font-weight: 600; }
.ariane-sep { color: var(--line-strong); }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  /* La regle precedente ne portait que sur les transitions : le tissage du fil
     est une animation, il faut l'eteindre explicitement. */
  *, *::before, *::after { animation: none !important; }
  .fil-trace { stroke-dashoffset: 0; }
}
