/* Wolfi — palette et typographie reprises du carnet d'impro. */
:root {
  --bg: #F1EEF3; --surface: #FBFAFC; --ink: #241B2C; --muted: #6E6375;
  --plum: #6B2D5C; --plum-soft: #E7D7E3; --gold: #B7821A; --rule: #D9D0DF;
  --s0: #6B2D5C; --s1: #B7821A; --s2: #2F6F73; --s3: #A2482E; --s4: #4F5B8C; --sx: #8D8494;
  --ok: #2F7D4F; --ko: #B3261E; --key-white: #FFFFFF; --key-black: #241B2C;
  /* Juste ou faux au clavier : bleu contre orange, qu'on distingue même sans bien voir le rouge et le vert. */
  --hit: #0B66C3; --miss: #D05200;
  /* Les graphiques du tableau de bord, une seule série : un bleu lisible sur les deux fonds. */
  --chart: #2A78D6;
  --serif: "Young Serif", Georgia, "Times New Roman", serif;
  --sans: "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", sans-serif;
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #1B1420; --surface: #251C2B; --ink: #EEE7F1; --muted: #A99DB0;
    --plum: #D08DBD; --plum-soft: #3E2B3C; --gold: #E2B04C; --rule: #3B3042;
    --s0: #D08DBD; --s1: #E2B04C; --s2: #6CC3C0; --s3: #E08A6C; --s4: #9AA6E0; --sx: #8D8494;
    --ok: #6CC38A; --ko: #F2867E; --key-white: #E9E2EC; --key-black: #0F0B12;
    --hit: #6DB6F2; --miss: #FF9B57;
    --chart: #3987E5;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #1B1420; --surface: #251C2B; --ink: #EEE7F1; --muted: #A99DB0;
  --plum: #D08DBD; --plum-soft: #3E2B3C; --gold: #E2B04C; --rule: #3B3042;
  --s0: #D08DBD; --s1: #E2B04C; --s2: #6CC3C0; --s3: #E08A6C; --s4: #9AA6E0; --sx: #8D8494;
  --ok: #6CC38A; --ko: #F2867E; --key-white: #E9E2EC; --key-black: #0F0B12;
  --hit: #6DB6F2; --miss: #FF9B57;
  --chart: #3987E5;
  color-scheme: dark;
}
*, *::before, *::after { box-sizing: border-box; }
/* Un élément masqué le reste, même quand sa classe lui donne un display (.btn, .row…). */
[hidden] { display: none !important; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--sans); font-size: 16px; line-height: 1.5; }
/* Le bas de page garde la place du clavier fixé en bas de l'écran (--dock-height) : rien ne reste caché dessous. */
.wrap { max-width: 1040px; margin: 0 auto; padding: 28px 16px calc(64px + var(--dock-height, 0px)); }
/* Rien ne déborde sur le côté : un téléphone élargirait toute la page (clip garde la barre de lecture collée en haut). */
.wrap { overflow-x: clip; }
/* En paysage, la page d'un morceau s'élargit : grille à gauche, onglets à droite.
   La grille reste visible sous la barre de lecture. */
.board { container: board / inline-size; }
@media (min-width: 1000px) {
  .wrap.wide { max-width: 1640px; }
  .workspace { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 0 28px; align-items: start; }
  .board { position: sticky; top: calc(var(--player-height, 64px) + 18px);
    max-height: calc(100vh - var(--player-height, 64px) - var(--dock-height, 0px) - 30px);
    overflow-y: auto; padding: 0 4px 16px 0; overscroll-behavior: contain; }
  .desk nav.tabs { margin-top: 0; }
  .desk .cols { grid-template-columns: 1fr; }
}
@media (min-width: 1500px) { .desk .cols { grid-template-columns: 1.1fr .9fr; } }
h1, h2, h3 { font-family: var(--serif); font-weight: 400; margin: 0; }
h2 { font-size: 1.45rem; margin: 0 0 10px; }
h3 { font-size: 1.1rem; margin: 0 0 4px; }
a { color: var(--plum); }
button, input, select, textarea { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.muted { color: var(--muted); }
.serif { font-family: var(--serif); }
small { font-size: .85rem; }
form.button_to { display: inline; }

/* En-tête */
header.top { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; }
/* Wolfi et son nom, pareils sur toutes les pages : le nom sur la ligne de base de l'en-tête, le louveteau centré sur
   la ligne puis remonté de 4 px. Son corps tire l'œil vers le bas : sans ça, le nom paraît trop haut à côté de lui. */
.brand { display: inline-flex; align-items: baseline; gap: 8px; flex: none; line-height: 36px; font-family: var(--serif); color: var(--ink);
  text-decoration: none; font-size: 1.15rem; }
.brand .logo { align-self: center; position: relative; top: -4px; }
/* Wolfi, la mascotte : un louveteau sans cadre, petit dans l'en-tête, plus grand quand il se présente. */
.logo { display: block; flex: none; width: 36px; height: 36px; }
.logo.big { width: 88px; height: 88px; }
.mascot-says { display: flex; align-items: center; gap: 16px; max-width: 780px; }
.mascot-says .lede { margin: 0; }
.tune-pick { display: flex; align-items: center; gap: 8px; }
.tune-pick select { background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: 6px 8px; max-width: 62vw; }
h1.title { font-size: clamp(1.9rem, 5vw, 3rem); line-height: 1.1; margin: 22px 0 6px; max-width: 22ch; }
/* Page d'un morceau : le titre tient sur une ligne, la place va à la grille. Wolfi, le titre (avec son crayon) et le
   choix du morceau partagent une ligne de 36 px, leurs textes sur la même ligne de base ; un trait sépare Wolfi du
   titre. Les informations du morceau passent dessous. */
header.piece-header { align-items: baseline; flex-wrap: nowrap; }
.piece-header .brand, .piece-header .tune-pick, .piece-title-row { min-height: 36px; }
.piece-header .brand { padding-right: 16px; border-right: 1px solid var(--rule); }
.piece-head { flex: 1; min-width: 0; display: grid; gap: 2px; }
.piece-title-row { position: relative; display: flex; align-items: center; gap: 4px; min-width: 0; }
.piece-title { font-size: 1.3rem; line-height: 1.2; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.piece-head .meta { font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Crayon à côté du titre : un petit formulaire flottant pour corriger le titre et le compositeur. Il se cale sous la
   ligne du titre, à partir de son bord gauche, pour ne jamais dépasser de l'écran (même fermé, il compte dans la page). */
.edit-info { flex: none; }
.edit-info summary { list-style: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 6px; cursor: pointer; color: var(--muted); }
.edit-info summary svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.edit-info summary::-webkit-details-marker { display: none; }
.edit-info summary:hover, .edit-info[open] summary { color: var(--plum); background: var(--plum-soft); }
.edit-info-form { position: absolute; top: calc(100% + 8px); left: 0; z-index: 40; width: min(420px, 100%); padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--rule); border-radius: 10px; box-shadow: 0 12px 28px -14px rgb(0 0 0 / .5); }
.piece-header .tune-pick select { max-width: 240px; }
@media (max-width: 720px) {
  header.piece-header { flex-wrap: wrap; }
  .piece-head { flex-basis: 100%; order: 3; }
  /* Wolfi et le choix du morceau partagent la première ligne, sans trait entre eux ; l'étiquette « Morceau » reste
     pour les lecteurs d'écran. */
  .piece-header .brand { padding-right: 0; border-right: 0; }
  .piece-header .tune-pick { flex: 1 1 0; min-width: 0; justify-content: flex-end; }
  .piece-header .tune-pick label { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .piece-header .tune-pick select { min-width: 0; width: 100%; max-width: 240px; }
}
.meta { color: var(--muted); }
.lede { max-width: 62ch; font-size: 1.05rem; }
.lead { max-width: 64ch; margin-top: 0; }

/* Carte du morceau : une frise de sections, détachée de la grille au-dessus de laquelle elle se tient. Ses bandes
   sont des moments du morceau (chacun une ligne de la grille), pas des colonnes : arrondies, décalées par le mot « Forme ».
   Elle tient sur une seule ligne et chaque bande a la largeur de sa section (flex-grow = ses mesures) : une section
   ne paraît jamais plus longue qu'elle n'est. Les plus courtes gardent juste de quoi lire leur nom ; une forme très
   longue défile. */
.map { display: flex; align-items: center; gap: 4px; margin: 0 0 14px; overflow-x: auto; scrollbar-width: thin; }
.map-label { flex: none; margin-right: 6px; font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.seg { flex: 1 1 0; min-width: 3.9em; container-type: inline-size; border: 0; border-radius: 7px; padding: 4px 7px; text-align: left; cursor: pointer; color: var(--surface);
  background: var(--tone); display: flex; gap: 6px; align-items: baseline; white-space: nowrap; overflow: hidden; }
.seg b { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-family: var(--serif); font-weight: 400; font-size: 1rem; }
.seg small { opacity: .85; overflow: hidden; text-overflow: ellipsis; }
/* Une section d'une mesure (levée, coda) garde son nom entier ; ses numéros de mesure disparaissent quand la case est étroite. */
@container (max-width: 6em) { .seg small { display: none; } }
.seg:hover { filter: brightness(1.08); }
.sec-0 { --tone: var(--s0); } .sec-1 { --tone: var(--s1); } .sec-2 { --tone: var(--s2); }
.sec-3 { --tone: var(--s3); } .sec-4 { --tone: var(--s4); } .sec-x { --tone: var(--sx); }

/* La grille : l'élément central */
/* Quatre mesures par ligne, comme une grille de jazz : une phrase de 8 mesures tient sur deux lignes. */
.chart { margin: 0 0 8px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-right: 4px double var(--ink); }
/* Quatre mesures par ligne quand la grille a la place ; sinon deux, pour garder les accords lisibles. */
@container board (min-width: 600px) { .chart { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.bar.pickup { grid-column: 1 / -1; min-height: 64px; }
.bar { position: relative; border: 0; border-left: 2px solid var(--ink); background: transparent; text-align: left; padding: 10px 8px 12px; min-height: 82px;
  cursor: pointer; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.bar.opens { border-left: 4px solid var(--tone); }
.bar .heat { position: absolute; inset: 0; background: var(--plum); opacity: 0; pointer-events: none; }
.bar .num { position: absolute; top: 4px; right: 8px; font-size: .75rem; color: var(--muted); }
.bar .sec { position: absolute; top: 3px; left: 8px; font-family: var(--serif); font-size: .85rem; color: var(--tone); }
/* Chaque mesure est découpée en temps : l'accord se place sur le temps où il arrive, le temps joué s'allume. */
.bar .beats { position: relative; display: grid; grid-template-columns: repeat(var(--pulses), minmax(0, 1fr)); grid-template-rows: auto 5px; gap: 4px 0; margin-top: 12px; }
.bar .chord { position: relative; grid-row: 1; display: grid; min-width: 0; padding-right: 2px; container-type: inline-size; }
/* Verdict du clavier MIDI ou du quiz sur chaque accord : en haut à droite, sans pousser la cellule. */
.bar .chord.hit::after, .bar .chord.miss::after, .bar .chord.quiz-right::after, .bar .chord.quiz-wrong::after {
  position: absolute; top: -13px; right: 2px; font-size: .78rem; font-weight: 700; }
.bar .chord.hit::after, .bar .chord.quiz-right::after { content: "✓"; color: var(--hit); }
.bar .chord.miss::after, .bar .chord.quiz-wrong::after { content: "✗"; color: var(--miss); }
.bar .chord + .chord { border-left: 1px dotted var(--rule); padding-left: 3px; }
/* La police se règle sur la largeur de la case et la longueur du symbole (--fit) : l'accord est toujours entier. */
.bar .chord b { font-family: var(--serif); font-weight: 400; font-size: clamp(.7rem, calc(100cqi / (var(--fit, 4) * .68)), 1.15rem);
  letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar .chord .quality { font-size: .8em; }
.bar .chord .slash { font-size: .72em; }
.bar .chord small { font-size: .72rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar .chord.guess b { font-style: italic; opacity: .7; }
.bar .chord.empty b { color: var(--muted); }
.bar .chord.active b { color: var(--plum); }
.bar .tick { grid-row: 2; height: 5px; margin: 0 2px; border-radius: 3px; background: var(--rule); }
.bar .tick.now { background: var(--gold); }
.quizzing .bar .chord b, .quizzing .bar .chord small { visibility: hidden; }
.quizzing .targets { visibility: hidden; }
/* Ce qui a été joué autour d'une question se découvre ; l'accord demandé dit si la réponse était juste. */
.quizzing .bar .chord.revealed b, .quizzing .bar .chord.revealed small { visibility: visible; }
.bar .chord.quiz-right b { color: var(--hit); }
.bar .chord.quiz-wrong b { color: var(--miss); }
.bar.sel { background: var(--plum-soft); }
.bar.sel .num { color: var(--plum); font-weight: 700; }
/* Point de départ choisi sans boucle : la lecture part de cette mesure. */
.bar.from { background: color-mix(in srgb, var(--gold) 16%, transparent); }
.bar.from .num { color: var(--gold); font-weight: 700; }
.bar.from .num::before { content: "▶ "; font-size: .65rem; }
.bar.playing { box-shadow: inset 0 -4px 0 var(--gold); }
.bar:hover { background: color-mix(in srgb, var(--plum-soft) 55%, transparent); }
.targets { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 8px; }
.targets span { white-space: nowrap; }
.targets b { font-family: var(--serif); font-weight: 400; }
.targets em { font-style: normal; color: var(--gold); font-weight: 700; }
/* Étape de mémoire : les noms et degrés des accords et les notes-cibles se cachent ; les ✓ et ✗ du clavier restent. */
.from-memory .bar .chord b, .from-memory .bar .chord small, .from-memory .targets { visibility: hidden; }
/* Zone du jour : ses mesures ressortent sous un trait épais, le reste de la grille s'estompe (sauf ce qu'on écoute ou
   qu'on boucle). */
.chart.has-focus .bar:not(.focus):not(.sel):not(.from):not(.playing) > :not(.heat) { opacity: .35; }
.bar.focus { border-top: 3px solid var(--plum); padding-top: 8px; }
/* Petit écran : la grille passe dans la barre du bas, en une bande d'une ligne qui défile de côté avec la lecture. */
.grid-strip .chart { display: flex; overflow-x: auto; overflow-y: hidden; margin: 8px 0 0; border-right: 0; scrollbar-width: thin;
  overscroll-behavior-x: contain; }
.grid-strip .bar { flex: none; width: 124px; min-height: 0; padding: 4px 6px 7px; }
.grid-strip .bar.focus { padding-top: 2px; }
.grid-strip .bar.pickup { min-height: 0; }
.grid-strip .bar:last-child { border-right: 4px double var(--ink); }
.grid-strip .bar .beats { margin-top: 14px; }
.grid-strip .bar .chord b, .grid-strip .bar .chord small { line-height: 1.25; }
/* Agrandie par les poignées, la bande s'empile sur deux ou trois lignes, qui défilent de haut en bas avec la lecture.
   Trois mesures par ligne sur un téléphone, pour que les accords restent entiers ; quatre, comme une grille de jazz,
   quand la barre a la place. */
.grid-strip { --strip-row: 74px; container: strip / inline-size; }
.grid-strip.rows .chart { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: var(--strip-row);
  max-height: calc(var(--strip-rows, 2) * var(--strip-row)); overflow: hidden auto; border-right: 4px double var(--ink); }
@container strip (min-width: 600px) { .grid-strip.rows .chart { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.grid-strip.rows .bar { width: auto; }
.grid-strip.rows .bar:last-child { border-right: 0; }
.midi:has(.grid-strip .chart) .keyboard { height: var(--key-height-strip, 58px); }
/* La grille partie en bas, les onglets remontent sous la forme du morceau. */
@media (max-width: 999px) { .desk nav.tabs { margin-top: 12px; } }

/* Lecteur : une barre toujours visible en haut, au-dessus de la grille et des onglets. */
.player { position: sticky; top: 8px; z-index: 20; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 14px 0 12px;
  padding: 8px 12px; background: var(--surface); border: 1px solid var(--rule); border-radius: 10px; box-shadow: 0 6px 14px -12px rgb(0 0 0 / .45); }
/* Le contenu qui défile ne se voit pas dans l'espace laissé au-dessus de la barre. */
.player::before { content: ""; position: absolute; left: -1px; right: -1px; top: -9px; height: 8px; background: var(--bg); }
@media (min-width: 1000px) { .player { flex-wrap: nowrap; } }
/* Petit écran : le lecteur se replie sur « Écouter » et le tempo (plus la position pendant la lecture) ; le chevron
   déplie le reste. Sur grand écran tout tient sur une ligne, et le chevron disparaît. */
.player-extra { display: contents; }
.player-more, .dock-more { display: none; flex: none; width: 34px; height: 34px; padding: 0; border: 1px solid var(--rule); border-radius: 50%;
  background: var(--bg); color: var(--ink); font-size: 1.05rem; line-height: 1; cursor: pointer; transition: transform .15s; }
.player-more { margin-left: auto; }
@media (max-width: 999px) {
  .player-more, .dock-more { display: inline-grid; place-items: center; }
  .player.collapsed .player-extra { display: none; }
  .player.collapsed:not(.is-playing) .player-status { display: none; }
  .player:not(.collapsed) .player-more, .midi:not(.collapsed) .dock-more { transform: rotate(180deg); }
}
/* Petit écran : le lecteur descend dans la barre du bas, sous le pouce, au-dessus des vues du clavier ; il n'y est plus
   une barre à part. */
.player-slot .player { position: static; margin: 0 0 8px; padding: 0 0 8px; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0;
  background: none; box-shadow: none; }
.player-slot .player::before { content: none; }
.player-slot .player-status { min-width: 0; }
.player select, .player input[type="number"] { background: var(--bg); border: 1px solid var(--rule); border-radius: 6px; padding: 5px 8px; }
.player select { width: auto; min-width: 0; }
.player input[type="number"] { width: 62px; }
.presets { display: inline-flex; gap: 4px; }
.pill { border: 1px solid var(--rule); border-radius: 999px; padding: 3px 9px; background: var(--bg); color: var(--muted); font-size: .82rem; cursor: pointer; }
.pill:hover { color: var(--ink); border-color: var(--muted); }
/* Interrupteurs en pastilles, comme les exercices du formulaire. */
.toggle { position: relative; display: inline-flex; flex: none; }
.toggle input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.toggle span { border: 1px solid var(--rule); border-radius: 999px; padding: 5px 12px; background: var(--bg); font-size: .9rem; white-space: nowrap; }
.toggle input:checked + span { background: var(--plum); border-color: var(--plum); color: var(--surface); }
.toggle input:focus-visible + span { outline: 2px solid var(--gold); outline-offset: 2px; }
/* Pendant la lecture, la position prend la place de l'état de la boucle : la barre ne change pas de largeur. */
.player-status { display: inline-flex; align-items: baseline; gap: 6px; margin-left: auto; min-width: 8.5em; justify-content: flex-end;
  font-size: .9rem; color: var(--muted); white-space: nowrap; }
.player-status .position { display: none; color: var(--ink); font-variant-numeric: tabular-nums; }
.player.is-playing .position { display: inline-flex; align-items: baseline; }
/* Position de lecture : la mesure en gros, le temps en petit et en retrait (« 14.4 »), alignés sur la même ligne de base. */
.position b { font-weight: 700; font-size: 1.35rem; line-height: 1; font-variant-numeric: tabular-nums lining-nums; }
.position small { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums lining-nums; }
.player.is-playing .loop-label { display: none; }
.loop-label { display: inline-flex; align-items: center; gap: 10px; }
.loop-label .clear { display: inline-grid; place-items: center; width: 22px; height: 22px; padding: 0; border: 1px solid var(--rule); border-radius: 50%;
  background: var(--bg); color: var(--muted); font-size: .7rem; line-height: 1; cursor: pointer; }
.loop-label .clear:hover { color: var(--ink); border-color: var(--muted); }
.inline-field, .check { display: inline-flex; align-items: center; gap: 6px; }

/* Onglets */
nav.tabs { display: flex; gap: 4px; margin: 34px 0 0; border-bottom: 1px solid var(--rule); overflow-x: auto; }
nav.tabs button { background: none; border: 0; padding: 10px 14px; border-bottom: 3px solid transparent; color: var(--muted); cursor: pointer; white-space: nowrap; }
nav.tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--plum); font-weight: 700; }
section.panel { padding-top: 26px; }
section.panel[hidden] { display: none; }
.cols { display: grid; grid-template-columns: 1fr; gap: 36px; }
@media (min-width: 860px) { .cols { grid-template-columns: 1.1fr .9fr; } }
.home { margin-top: 30px; }
/* Accueil : la colonne du nouveau morceau a la place de sa zone de dépôt ; la liste prend le reste. */
@media (min-width: 860px) { .cols.home { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: 48px; } }

/* Programme */
.days { list-style: none; padding: 0; margin: 0; display: grid; gap: 0; }
.day { padding: 18px 0 18px 16px; border-left: 4px solid var(--rule); border-bottom: 1px solid var(--rule); }
.day.now { border-left-color: var(--gold); }
.day.done { border-left-color: var(--plum); }
.day.done h3 { color: var(--muted); }
.day-head { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; color: var(--muted); font-size: .9rem; }
.day-num { font-weight: 700; color: var(--ink); }
.day .status { color: var(--gold); font-weight: 700; }
.day.done .status { color: var(--plum); }
.day h3 { font-size: 1.3rem; margin: 4px 0; }
.goal { margin: 0 0 6px; }
.insight { margin: 0 0 8px; color: var(--muted); max-width: 70ch; }
details summary { cursor: pointer; color: var(--plum); margin: 6px 0; }
.steps { margin: 6px 0 12px; padding-left: 22px; display: grid; gap: 8px; max-width: 70ch; }

/* Séance du jour */
.today { border-left: 4px solid var(--gold); padding: 4px 0 4px 16px; margin-bottom: 26px; }
.today .day { color: var(--muted); padding: 0; border: 0; }
.today h3 { font-size: 1.35rem; margin: 4px 0; }
.day-meta { margin-top: 4px; }
.day-pick { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 10px; }
.day-pick a { min-width: 30px; height: 30px; display: inline-grid; place-items: center; border: 1px solid var(--rule); border-radius: 6px; text-decoration: none;
  color: var(--muted); background: var(--surface); font-size: .85rem; }
.day-pick a.on { background: var(--plum); border-color: var(--plum); color: var(--surface); font-weight: 700; }
/* Un jour fait est coché, en haut à droite de son numéro. */
.day-pick a.done { position: relative; border-color: var(--hit); color: var(--ink); }
.day-pick a.done::after { content: "✓"; position: absolute; top: -7px; right: -5px; font-size: .72rem; font-weight: 700; line-height: 1;
  padding: 1px 2px; border-radius: 6px; background: var(--surface); color: var(--hit); }
.day.done .day-num { color: var(--hit); }
/* Le jour qui s'ouvre demain (porte douce) : en pointillés, sans le mettre en avant. */
.day.tomorrow { border-left-style: dashed; border-left-color: var(--gold); }
.day.tomorrow .status { color: var(--muted); font-weight: 400; }
.day-pick a.tomorrow { border-style: dashed; border-color: var(--gold); }

/* Bilan du jour : le jour est fait, la suite attend demain ; de quoi consolider si l'on veut continuer. */
.rest { max-width: 70ch; }
.rest-head { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.rest-day { color: var(--muted); font-size: .9rem; }
.rest-head h3 { font-size: 1.35rem; margin: 2px 0 4px; }
.rest-head p { margin: 0; }
.rest-next { padding: 10px 14px; border-left: 4px dashed var(--gold); background: var(--surface); border-radius: 0 8px 8px 0; }
.rest h4 { margin: 18px 0 6px; }
.rest-options { margin: 0; padding-left: 20px; display: grid; gap: 8px; }
.rest-anyway { margin-top: 18px; }

/* Minuteur */
.timer { background: var(--surface); border: 1px solid var(--rule); border-radius: 10px; padding: 18px; }
.clock { font-family: var(--serif); font-size: 3.2rem; line-height: 1; font-variant-numeric: tabular-nums; }
/* Étapes de la séance : toutes visibles, celle en cours mise en avant. */
.phases { list-style: none; padding: 0; margin: 16px 0 0; display: grid; gap: 8px; }
.phases > li { border: 1px solid var(--rule); border-left: 4px solid var(--rule); border-radius: 8px; padding: 10px 12px; background: var(--bg); }
.phases > li.on { border-left-color: var(--gold); background: var(--surface); box-shadow: 0 1px 0 var(--rule); }
.phases > li.done { opacity: .6; }
.phases li.done .step-name { text-decoration: line-through; }
.step-head { display: flex; align-items: center; gap: 10px; }
.step-head .n { width: 22px; height: 22px; border-radius: 50%; display: inline-grid; place-items: center; font-size: .8rem; background: var(--rule); flex: none; }
.phases li.on .n { background: var(--gold); color: var(--surface); }
.step-name { font-weight: 700; flex: 1; }
.step-head input { width: 52px; padding: 2px 6px; border: 1px solid var(--rule); border-radius: 5px; background: var(--surface); text-align: right; }
.step-tip { margin: 6px 0 4px 32px; color: var(--muted); font-size: .95rem; }
.phases li.on .step-tip { color: var(--ink); }
/* Validation des étapes : boutons sous le conseil, meilleure note à côté du nom, quiz ou note dans l'étape. */
.step-actions { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 16px; margin-left: 32px; font-size: .88rem; }
.step-actions .check { color: var(--muted); }
.step-actions .check:hover { color: var(--plum); }
.check-intro { margin: 8px 0 0; }
.grade { flex: none; color: var(--muted); font-size: .9rem; white-space: nowrap; }
.grade small { font-size: .78rem; }
.stars { color: var(--gold); letter-spacing: .08em; }
.stars .dim { color: var(--rule); }
/* Phrase par phrase : une ligne par phrase, puis le passage entier, chacune à lancer et à valider ;
   validée (✓, filet bleu), en cours (or). */
.phrases { display: grid; gap: 2px; margin: 2px 0 6px 32px; padding: 0; list-style: none; font-size: .88rem; }
.phrase-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; padding: 3px 8px; border-left: 3px solid var(--rule);
  border-radius: 0 6px 6px 0; }
.phrase-label { min-width: 5.5em; font-weight: 700; font-variant-numeric: tabular-nums; }
.phrase-row .grade { min-width: 6.5em; font-size: .85rem; }
.phrase-actions { display: inline-flex; flex-wrap: wrap; gap: 2px 14px; }
.phrase-actions .check { color: var(--muted); }
.phrase-actions .check:hover { color: var(--plum); }
.phrase-row.validated, .phases > li.validated .phrase-row.whole { border-left-color: var(--hit); }
.phrase-row.validated .phrase-label::before, .phases > li.validated .phrase-row.whole .phrase-label::before { content: "✓ "; color: var(--hit); }
.phrase-row.now { border-left-color: var(--gold); background: color-mix(in srgb, var(--gold) 14%, var(--surface)); }
.phrase-row .step-stage { flex-basis: 100%; }
.grade.passed small { color: var(--ok); font-weight: 700; }
.phases li.validated .n { background: var(--ok); color: var(--surface); }
.step-stage { margin: 10px 0 2px 32px; padding: 10px 12px; border: 1px solid var(--rule); border-radius: 8px; background: var(--surface); }
.step-stage .quiz-question { margin-top: 0; }
.result-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.result .score { font-family: var(--serif); font-size: 2rem; line-height: 1; }
.result .stars { font-size: 1.35rem; }
.result p { margin: 6px 0 0; }
.result.passed b { color: var(--ok); }
.day-head .validated-count { color: var(--ok); }
.days .steps .grade { margin-left: 6px; }
/* Quiz d'étape : ce qui donnerait la réponse (tonalité, carte, noms de sections, accords, conseils) se cache. */
.quiz-blind .piece-head .meta, .quiz-blind .seg b, .quiz-blind .seg small, .quiz-blind .bar .sec, .quiz-blind .bar .chord b,
.quiz-blind .bar .chord small, .quiz-blind .targets, .quiz-blind .phases .step-tip, .quiz-blind .midi-feedback [data-midi-target="expected"] { visibility: hidden; }
.quiz-blind .seg { background: var(--sx); }
.quiz-blind .bar.opens { border-left: 2px solid var(--ink); }
/* Vérification de la main droite ou des deux mains : chaque mesure jouée reçoit son verdict. */
.bar.hit .num::after { content: " ✓"; color: var(--hit); font-weight: 700; }
.bar.miss .num::after { content: " ✗"; color: var(--miss); font-weight: 700; }
.phase-tip { min-height: 1.2em; margin-top: 10px; color: var(--muted); font-size: .9rem; }
/* Quiz : on écoute, on devine l'accord qui arrive. */
.quiz { margin-top: 18px; background: var(--surface); border: 1px solid var(--rule); border-radius: 10px; padding: 16px 18px; }
.quiz h3 { font-size: 1.2rem; }
.quiz-question { font-size: 1.05rem; margin: 10px 0; }
.quiz-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin: 10px 0; }
.quiz-options button { font-family: var(--serif); font-size: 1.3rem; padding: 10px; border: 1px solid var(--rule); border-radius: 8px; background: var(--bg); cursor: pointer; }
/* Réponses en toutes lettres (quiz d'étape) : texte courant, sur plusieurs lignes si besoin. */
.quiz-options.words { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.quiz-options.words button { font-family: var(--sans); font-size: .95rem; text-align: left; line-height: 1.3; }
.quiz-options button.right { background: var(--plum); border-color: var(--plum); color: var(--surface); }
.quiz-options button.wrong { text-decoration: line-through; opacity: .6; }
.quiz-options button:disabled { cursor: default; }
.quiz-score { font-family: var(--serif); font-size: 2rem; }
details.more-chips summary { margin: 8px 0 6px; font-size: .9rem; }
.row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.row.spread { justify-content: space-between; }
.btn { display: inline-block; background: var(--surface); border: 1px solid var(--rule); border-radius: 7px; padding: 8px 14px; cursor: pointer; text-decoration: none; color: var(--ink); }
.btn.primary { background: var(--plum); color: var(--surface); border-color: var(--plum); font-weight: 700; }
.btn.ghost { background: none; }
.btn:disabled { opacity: .5; cursor: default; }
.linkish { background: none; border: 0; padding: 0; color: var(--plum); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.linkish.small { font-size: .85rem; }

/* Formulaires */
form.log { display: grid; gap: 14px; }
label.f { display: grid; gap: 4px; font-weight: 700; font-size: .92rem; }
label.f .hint { font-weight: 400; }
.field-title { font-weight: 700; font-size: .92rem; }
.field-title .hint { font-weight: 400; }
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* Dépôt d'un fichier : une grande zone facile à viser, qui s'allume quand un fichier la survole. */
.dropzone { position: relative; display: grid; justify-items: center; gap: 4px; padding: 28px 18px; border: 2px dashed var(--rule); border-radius: 12px;
  background: var(--surface); text-align: center; cursor: pointer; transition: border-color .15s, background-color .15s; }
.dropzone:hover, .dropzone:focus-within, .dropzone.over { border-color: var(--plum); background: color-mix(in srgb, var(--plum-soft) 40%, var(--surface)); }
.dropzone:focus-within { outline: 2px solid var(--gold); outline-offset: 2px; }
.dropzone.over { border-style: solid; }
.dropzone-icon { font-size: 1.9rem; line-height: 1; color: var(--plum); }
.dropzone-text u { color: var(--plum); text-underline-offset: 3px; }
.dropzone-hint { color: var(--muted); font-size: .82rem; }
.dropzone-file { font-weight: 700; }
.dropzone-file:empty { display: none; }
.dropzone.chosen { border-style: solid; border-color: var(--ok); }
.dropzone.chosen .dropzone-file { color: var(--ok); }
.dropzone.refused .dropzone-file { color: var(--ko); }
.dropzone.compact { grid-auto-flow: row; padding: 16px; }
.dropzone.compact .dropzone-icon { font-size: 1.4rem; }
@media (prefers-reduced-motion: reduce) { .dropzone { transition: none; } }
label.f input, label.f textarea, label.f select { font-weight: 400; background: var(--surface); border: 1px solid var(--rule); border-radius: 7px; padding: 8px 10px; width: 100%; }
.two { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px; }
fieldset { border: 0; padding: 0; margin: 0; }
legend { font-weight: 700; font-size: .92rem; margin-bottom: 6px; padding: 0; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { border: 1px solid var(--rule); border-radius: 999px; padding: 4px 11px; background: var(--surface); cursor: pointer; font-size: .88rem; }
.chip input { position: absolute; opacity: 0; pointer-events: none; }
.chip:has(input:checked) { background: var(--plum); border-color: var(--plum); color: var(--surface); }
.chip:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 2px; }
.dots { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.dots label { position: relative; }
.dots input { position: absolute; opacity: 0; }
.dots label span { width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--rule); background: var(--surface); cursor: pointer; display: grid; place-items: center; }
.dots input:checked + span { background: var(--gold); border-color: var(--gold); color: var(--surface); font-weight: 700; }
.dots input:focus-visible + span { outline: 2px solid var(--gold); outline-offset: 2px; }
.demo { margin-top: 14px; }
.form-hint { margin: -6px 0 0; }
.replace { margin-top: 18px; }

/* Structure */
.form-line { font-family: var(--serif); font-size: 2rem; margin: 0 0 12px; letter-spacing: .08em; }
table.sections { width: 100%; border-collapse: collapse; margin-bottom: 14px; }
table.sections th { text-align: left; font-size: .85rem; color: var(--muted); font-weight: 400; border-bottom: 1px solid var(--rule); padding: 6px 8px 6px 0; }
table.sections td { border-bottom: 1px solid var(--rule); padding: 8px 8px 8px 0; vertical-align: top; }
.tag { display: inline-block; min-width: 2em; text-align: center; padding: 1px 6px; border-radius: 5px; background: var(--tone); color: var(--surface); font-family: var(--serif); }
.facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0 0 22px; }
.facts dt { font-weight: 700; }
.facts dd { margin: 0; }
.facts-list { padding-left: 18px; margin: 0 0 18px; display: grid; gap: 6px; }
.difficulty-strip { display: flex; gap: 2px; height: 18px; margin: 6px 0 4px; }
.difficulty-strip span { flex: 1; background: var(--plum); border-radius: 2px; }
.tools { margin: 18px 0 8px; }

/* Progression */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 18px 24px; margin-bottom: 26px; }
.stat .v { font-family: var(--serif); font-size: 2rem; line-height: 1.1; }
.stat .l { color: var(--muted); font-size: .9rem; }
.chartbox { overflow-x: auto; margin-bottom: 28px; }
.chartbox svg { display: block; max-width: 100%; height: auto; }
.chartbox .axis { stroke: var(--rule); }
.chartbox rect.bar { fill: var(--plum); }
.chartbox rect.cur { fill: var(--gold); }
.chartbox text { font-size: 12px; fill: var(--ink); }
.chartbox text.l { font-size: 11px; fill: var(--muted); }
.chartbox .tempo { fill: none; stroke: var(--plum); stroke-width: 2.5; }
.chartbox .tempo-dot { fill: var(--plum); }
.chartbox .flow { fill: none; stroke: var(--gold); stroke-width: 2; stroke-dasharray: 5 4; }
.chartbox .flow-dot { fill: var(--gold); }
.chartbox .tempo-label { fill: var(--plum); }
.chartbox .flow-label { fill: var(--gold); }
.ideas, .sessions, .pieces { list-style: none; padding: 0; margin: 0; }
.ideas li { padding: 10px 0; border-bottom: 1px solid var(--rule); }
.ideas li .d { color: var(--muted); font-size: .85rem; }
.sessions li { padding: 12px 0; border-bottom: 1px solid var(--rule); display: grid; gap: 2px; }
.sessions .h { display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.pieces li { padding: 12px 0; border-bottom: 1px solid var(--rule); }
.piece-link { font-family: var(--serif); font-size: 1.25rem; color: var(--ink); text-decoration: none; }
.piece-link:hover { color: var(--plum); }
.piece-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.piece-row form { flex: none; margin: 0; }
.piece-remove { font-size: .9rem; color: var(--muted); }

/* Exercices */
.cat { margin-bottom: 28px; }
.ex { display: grid; grid-template-columns: 1fr auto; gap: 6px 16px; padding: 12px 0; border-bottom: 1px solid var(--rule); align-items: start; }
.ex p { margin: 2px 0 0; color: var(--muted); font-size: .93rem; max-width: 62ch; }
@media (max-width: 640px) { .ex { grid-template-columns: 1fr; } }
.lvl { display: flex; gap: 3px; flex-wrap: wrap; }
.lvl button { border: 1px solid var(--rule); background: var(--surface); padding: 4px 8px; font-size: .78rem; cursor: pointer; }
.lvl button:first-child { border-radius: 6px 0 0 6px; }
.lvl button:last-child { border-radius: 0 6px 6px 0; }
.lvl button[aria-pressed="true"] { background: var(--plum); color: var(--surface); border-color: var(--plum); }

.empty { color: var(--muted); font-style: italic; }
.credits { margin-top: 48px; color: var(--muted); }
/* Clavier MIDI, fixé en bas de l'écran, repliable et agrandissable : notes tenues, notes de l'accord attendu avec leur degré. */
.midi { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; padding: 8px 16px calc(8px + env(safe-area-inset-bottom, 0px));
  background: var(--surface); border-top: 1px solid var(--rule); box-shadow: 0 -6px 14px -12px rgb(0 0 0 / .45);
  --k-tone: color-mix(in srgb, var(--plum) 28%, var(--key-white)); --k-guide: color-mix(in srgb, var(--gold) 50%, var(--key-white)); }
.midi-inner { max-width: 1608px; margin: 0 auto; }
/* Filet de sécurité : si la barre dépassait quand même l'écran, son contenu défilerait au lieu de sortir par le haut. */
.midi-inner { max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow: hidden auto; overscroll-behavior: contain; }
/* Poignée : on tire le haut du clavier pour l'agrandir. */
.midi .grip { position: absolute; top: -9px; left: 50%; width: 64px; height: 17px; margin-left: -32px; padding: 0; border: 1px solid var(--rule);
  border-radius: 9px; background: var(--surface); cursor: ns-resize; touch-action: none; }
.midi .grip::before { content: ""; position: absolute; left: 50%; top: 50%; width: 28px; height: 3px; margin: -1.5px 0 0 -14px; border-radius: 2px;
  background: var(--muted); }
.midi .grip:hover::before, .resizing-keyboard .midi .grip::before { background: var(--plum); }
.resizing-keyboard, .resizing-keyboard * { cursor: ns-resize; user-select: none; }
/* Clavier et partition s'affichent chacun à part (boutons de la barre, pressés quand ils sont visibles). Le bouton
   Partition n'apparaît que quand il y a une partition, dans les étapes de mélodie. */
.midi .view[aria-pressed="true"] { background: var(--plum-soft); border-color: var(--plum); color: var(--plum); }
.midi:not(:has(.score-strip:not([hidden]))) .score-toggle { display: none; }
/* La poignée reste là clavier fermé : on la tire vers le haut pour le rouvrir. */
.midi.hide-keys .keyboard, .midi.hide-keys .midi-legend, .midi.hide-score .score-strip, .midi.hide-cascade .cascade { display: none; }
/* Cascade : les notes tombent vers le piano, posé juste dessous ; elle se tait dans un quiz et dans une étape de mémoire. */
.cascade { display: block; width: 100%; height: var(--cascade-height, 120px); margin-top: 8px; border-radius: 6px 6px 0 0;
  background: color-mix(in srgb, var(--bg) 55%, var(--surface)); }
.midi:not(.hide-cascade) .cascade + .splitter + .keyboard { margin-top: 0; }
@media (max-width: 999px) { .cascade { height: var(--cascade-height, 90px); } }
/* Poignées entre les vues de la barre (bande de grille, partition, cascade, piano) : on tire pour donner de la hauteur à
   l'une au détriment de l'autre. Posées sur la jointure, elles ne prennent pas de place. */
.splitter { position: relative; z-index: 2; height: 0; touch-action: none; }
.splitter::before { content: ""; position: absolute; left: 0; right: 0; top: -8px; height: 16px; cursor: ns-resize; }
.splitter::after { content: ""; position: absolute; left: 50%; top: -2px; width: 40px; height: 5px; margin-left: -20px; border-radius: 3px;
  background: var(--muted); opacity: .6; pointer-events: none; }
.splitter:hover::after, .resizing-keyboard .splitter::after { background: var(--plum); opacity: 1; }
.quizzing .cascade, .quiz-blind .cascade, .from-memory .cascade { visibility: hidden; }
.midi.melody .midi-legend.for-chords, .midi:not(.melody) .midi-legend.for-notes { display: none; }
/* Bout de partition des étapes de mélodie : il défile avec la lecture, au-dessus du clavier, sur fond de papier. */
.score-strip { margin-top: 8px; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--rule); border-radius: 6px; background: var(--key-white);
  scrollbar-width: thin; }
/* La partition se met à l'échelle choisie dans la barre (--score-scale) : plus haute, elle s'élargit d'autant. Sans
   réglage, elle part un peu réduite, plus encore sur un téléphone, pour laisser la place aux autres vues. */
.score-strip svg { display: block; height: calc(var(--natural, 96px) * var(--score-scale, .75)); width: auto; }
@media (max-width: 999px) { .score-strip svg { height: calc(var(--natural, 96px) * var(--score-scale, .55)); } }
.score-strip .staff { stroke: color-mix(in srgb, var(--key-black) 40%, var(--key-white)); stroke-width: 1; }
.score-strip .barline { stroke: var(--key-black); stroke-width: 1.2; }
.score-strip .barline.end { stroke-width: 2.4; }
.score-strip .head { fill: var(--key-black); }
.score-strip .head.open { fill: var(--key-white); stroke: var(--key-black); stroke-width: 1.4; }
.score-strip .stem, .score-strip .flag, .score-strip .ledger { stroke: var(--key-black); stroke-width: 1.2; fill: none; }
.score-strip .dot { fill: var(--key-black); }
.score-strip .clef, .score-strip .acc { font-family: "Noto Music", var(--serif); fill: var(--key-black); }
.score-strip .acc { font-size: 15px; }
.score-strip .chord { font-family: var(--serif); font-size: 13px; fill: var(--plum); }
.score-strip .num { font-size: 9px; fill: color-mix(in srgb, var(--key-black) 55%, var(--key-white)); }
.score-strip .cursor { stroke: var(--gold); stroke-width: 2; }
/* La partition reste claire en thème sombre : ses couleurs restent foncées. Juste : tête pleine, en bleu.
   Manquée : tête vide cerclée de tirets orange. La forme suffit à les distinguer, la couleur confirme. */
.score-strip { --hit: #0B66C3; --miss: #C84B00; }
.score-strip .head.ok { fill: var(--hit); }
.score-strip .head.ko { fill: var(--key-white); stroke: var(--miss); stroke-width: 1.8; stroke-dasharray: 2.2 1.4; }
.score-strip .head.open.ok { fill: var(--key-white); stroke: var(--hit); stroke-width: 2; }
.score-strip .head.open.ko { fill: var(--key-white); stroke: var(--miss); stroke-width: 1.8; stroke-dasharray: 2.2 1.4; }
.quizzing .score-strip, .quizzing-notes .score-strip, .quiz-blind .score-strip { visibility: hidden; }
/* Notes jouées au clavier, posées sur la partition à l'instant où elles sont jouées. */
/* Juste : une tête pleine bleue ; fausse : une croix orange. */
.score-strip .played { stroke-width: 1.3; }
.score-strip .played.ok { fill: var(--hit); fill-opacity: .9; stroke: var(--hit); }
.score-strip .played.ko { fill: none; stroke: var(--miss); }
.score-strip .played .cross { stroke-width: 2.4; stroke-linecap: round; }
.midi-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.midi-status { flex: 0 1 auto; font-size: .88rem; }
/* Sur ordinateur, la barre du clavier tient sur une ligne : l'aide se tronque (texte entier au survol). */
@media (min-width: 1000px) {
  .midi-bar { flex-wrap: nowrap; }
  .midi-status { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
/* Petit écran, barre repliée : les vues, le branchement et le chevron sur une ligne, puis l'état et ce qui est attendu,
   chacun sur une ligne tronquée ; le son du clavier, la vérification et la légende attendent le chevron. */
@media (max-width: 999px) {
  .dock-more { margin-left: auto; }
  .midi.collapsed .dock-extra { display: none; }
  .midi.collapsed .midi-status { flex: 1 1 100%; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .midi.collapsed .midi-feedback:not(:has(span:not(:empty))) { display: none; }
}
.midi select { background: var(--bg); border: 1px solid var(--rule); border-radius: 6px; padding: 4px 8px; }
/* Petit écran : « Brancher MIDI » tient sur la ligne des vues. */
@media (max-width: 999px) { .midi .wide-only { display: none; } }
/* Les boutons et réglages gardent leur taille : ce sont l'état et le verdict qui se tronquent. */
.midi-bar > .pill, .midi-bar > .btn, .midi-bar > .toggle, .midi-bar > .inline-field { flex: none; }
/* Sans Web MIDI (Safari, iPad), le clavier à l'écran montre seulement l'accord attendu. */
.no-midi .midi-only { display: none; }
.btn.small { padding: 5px 10px; font-size: .9rem; }
.keyboard { position: relative; display: flex; height: var(--key-height, 58px); margin: 8px 0 0; }
/* Le piano se joue au doigt : pas de défilement, de zoom ni de menu quand on appuie sur une touche. */
.keyboard { touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; cursor: pointer; }
.keyboard .key { position: relative; }
.keyboard .white { flex: 1; border: 1px solid var(--rule); border-left-width: 0; border-radius: 0 0 4px 4px; background: var(--key-white); }
.keyboard .white:first-child { border-left-width: 1px; }
.keyboard .black { position: absolute; top: 0; z-index: 1; width: calc(100% / var(--whites) * .62); height: 58%; transform: translateX(-50%);
  border-radius: 0 0 3px 3px; background: var(--key-black); }
.keyboard .tone { background: var(--k-tone); }
.keyboard .black.tone { background: color-mix(in srgb, var(--plum) 65%, var(--key-black)); }
.keyboard .guide { background: var(--k-guide); }
.keyboard .black.guide { background: color-mix(in srgb, var(--gold) 75%, var(--key-black)); }
.keyboard .down, .keyboard .black.down { background: var(--plum); }
/* Une touche fausse est rayée, en plus d'être orange. */
.keyboard .off, .keyboard .black.off, .swatch.off {
  background: repeating-linear-gradient(135deg, var(--miss) 0 4px, color-mix(in srgb, var(--miss) 45%, var(--key-white)) 4px 7px); }
.keyboard [data-label]::before { content: attr(data-label); position: absolute; left: 2px; bottom: 1px; font-size: .55rem;
  color: color-mix(in srgb, var(--key-black) 55%, var(--key-white)); }
/* Degré de la note dans l'accord attendu, en bas de la touche. */
.keyboard [data-degree]::after { content: attr(data-degree); position: absolute; left: 0; right: 0; bottom: 13px; text-align: center;
  font-size: .72rem; font-weight: 700; line-height: 1; color: var(--key-black); }
.keyboard .black[data-degree]::after { bottom: 5px; font-size: .62rem; color: var(--key-white); }
.keyboard .down[data-degree]::after { color: var(--surface); }
/* Clavier étroit (téléphone) : les touches n'y font que 10 px, les degrés rapetissent pour ne pas mordre sur la voisine. */
.keyboard { container: keys / inline-size; }
@container keys (max-width: 560px) {
  .keyboard [data-degree]::after { font-size: .6rem; letter-spacing: -.03em; }
  .keyboard .black[data-degree]::after { font-size: .52rem; }
}
/* Dans la barre, entre l'état du clavier et ses réglages : l'accord attendu, l'accord joué et le verdict. Ils prennent la place
   libre et se tronquent les premiers (cent fois plus vite que l'état), sans faire sauter la barre pendant la lecture. */
.midi-feedback { flex: 1 100 auto; min-width: 0; display: flex; gap: 14px; overflow: hidden; white-space: nowrap; font-size: .92rem; color: var(--muted); }
.midi-feedback span { flex: none; }
.midi-feedback .check-line { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--ink); }
/* Phrase par phrase, dans la barre du clavier : toujours visible pendant qu'on joue. */
.ladder { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 6px; font-size: .86rem; }
.ladder-title { color: var(--muted); }
.ladder ol { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.ladder li { display: inline-flex; align-items: baseline; gap: 6px; padding: 2px 10px; border: 1px solid var(--rule); border-radius: 999px;
  background: var(--bg); color: var(--muted); font-variant-numeric: tabular-nums; }
.ladder li b { font-weight: 600; }
.ladder li.done { border-color: var(--hit); color: var(--ink); }
.ladder li.done b { color: var(--hit); }
.ladder li.now { border-color: var(--gold); background: color-mix(in srgb, var(--gold) 16%, var(--surface)); color: var(--ink); }
.ladder .streak { color: var(--gold); letter-spacing: .1em; }
.ladder-total { font-weight: 700; font-variant-numeric: tabular-nums; }
/* Sous le clavier, ce que disent les touches. */
.midi-legend { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 2px 14px; margin-top: 5px; font-size: .78rem; color: var(--muted); }
.midi-legend span { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.swatch { width: 11px; height: 11px; border: 1px solid var(--rule); border-radius: 2px; }
.swatch.tone { background: var(--k-tone); }
.swatch.guide { background: var(--k-guide); }
.swatch.down { background: var(--plum); border-color: var(--plum); }
.swatch.off { border-color: var(--miss); }
.swatch.ok { border-radius: 50%; background: var(--hit); border-color: var(--hit); }
.swatch.ko { border: 0; background: none; color: var(--miss); font-style: normal; font-weight: 700; font-size: 13px; line-height: 11px; text-align: center; }
.swatch.ko::before { content: "✕"; }
.toast { position: fixed; z-index: 40; left: 50%; bottom: calc(18px + max(var(--dock-height, 0px), env(safe-area-inset-bottom, 0px)));
  transform: translateX(-50%); background: var(--ink); color: var(--bg);
  padding: 10px 16px; border-radius: 8px; opacity: 0; transition: opacity .2s; pointer-events: none; }
.toast.show { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }
