/* 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;
  /* La partition : son papier et son encre, qui suivent le thème (en sombre, ceux de la page). */
  --paper: #FFFFFF; --paper-ink: #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;
    --paper: #251C2B; --paper-ink: #EEE7F1;
    --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;
  --paper: #251C2B; --paper-ink: #EEE7F1;
  --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; }
/* La page d'un morceau : la zone de jeu sous le titre. Sur grand écran, elle tient dans la hauteur de l'écran sans que la
   page défile : les vues à gauche (la carte de .midi), les onglets à droite, qui défilent seuls, avec le lecteur en bas ;
   la poignée entre les deux partage la largeur (--stage-share, workspace_controller.js). Le crédit du son de piano se
   tient en bas des onglets, au-dessus du lecteur, sans le pousser. Sur petit écran, la page défile ; les vues et le
   lecteur sont dans la barre du bas, la carte du morceau et les onglets dans la page, le crédit en bas des onglets. */
.board { container: board / inline-size; }
.workspace { margin-top: 18px; }
.split-handle { display: none; }
.credits.desk-credits { margin: 20px 0 0; }
@media (max-width: 999px) {
  .wrap.wide { padding-bottom: calc(16px + var(--dock-height, 0px) + var(--dock-pull, 0px)); }
  .player-home:empty { display: none; }
  /* La page défile dans sa propre zone, pas le document : la barre du bas, fixée, reste collée au bas de l'écran, sans
     bouger quand Safari replie ses barres en défilant, ni dans l'appli installée sur l'écran d'accueil. */
  html:has(.wrap.wide), html:has(.wrap.wide) body { height: 100%; overflow: hidden; }
  .wrap.wide { height: 100%; overflow: hidden auto; overscroll-behavior-y: contain; }
}
@media (min-width: 1000px) {
  .wrap.wide { max-width: 1640px; height: 100vh; height: 100dvh; display: flex; flex-direction: column; padding: 14px 16px 12px; }
  .wrap.wide > .play { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .workspace { flex: 1 1 auto; min-height: 0; margin-top: 14px;
    display: grid; grid-template-columns: minmax(0, calc(var(--stage-share, .55) * (100% - 16px))) 16px minmax(0, 1fr); }
  .board-home { display: none; }
  .desk { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
  .desk nav.tabs { flex: none; margin-top: 0; }
  .desk-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden auto; overscroll-behavior: contain;
    padding-right: 4px; }
  .desk-body > .panel { flex: none; }
  .credits.desk-credits { margin: auto 0 0; padding-top: 12px; }
  .desk .cols { grid-template-columns: 1fr; }
  /* Le lecteur, carte posée en bas de la partie droite. */
  .player-home { flex: none; margin-top: 10px; container: player / inline-size; }
  .player-home .player { margin: 0; padding: 8px 12px; border: 1px solid var(--rule); border-radius: 12px; background: var(--surface); }
  /* La poignée entre les deux parties : toute sa hauteur se tire de côté ; un trait au milieu, qui s'éclaire au survol. */
  .split-handle { display: block; position: relative; cursor: ew-resize; touch-action: none; }
  .split-handle::after { content: ""; position: absolute; top: 50%; left: 50%; width: 6px; height: 48px; margin: -24px 0 0 -3px;
    border-radius: 999px; background: var(--rule); }
  .split-handle:hover::after, .split-handle:focus-visible::after, .resizing-columns .split-handle::after { background: var(--plum); }
  .split-handle:focus-visible { outline: none; }
  /* Plein écran : la zone de jeu couvre tout l'écran, titre compris. La partie droite peut alors se masquer : les vues
     prennent toute la largeur, le lecteur passe dessous. */
  .workspace.full { position: fixed; inset: 0; z-index: 30; margin: 0; padding: 12px 16px; background: var(--bg); }
  .workspace.full.side-hidden { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  .workspace.full.side-hidden .split-handle, .workspace.full.side-hidden .desk > :not(.player-home) { display: none; }
}
@media (min-width: 1500px) { .desk .cols { grid-template-columns: 1.1fr .9fr; } }
.play-full { overflow: hidden; }
.resizing-columns, .resizing-columns * { cursor: ew-resize; user-select: none; }
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; }
/* Wolfi se présente comme dans le dialogue d'un jeu vidéo : la boîte du texte, son buste à gauche, coupé par le bord du
   bas, et son nom en onglet sur le bord du haut, au début du texte. */
.mascot-says { position: relative; max-width: 780px; margin-top: 24px; padding: 18px 20px 16px 106px; border-radius: 16px;
  border: 2px solid color-mix(in srgb, var(--plum) 30%, var(--rule)); background: var(--surface); box-shadow: 0 10px 22px -18px rgb(0 0 0 / .6); }
.mascot-says .lede { margin: 0; color: var(--ink); }
.mascot-name { position: absolute; top: -13px; left: 106px; padding: 1px 12px; border-radius: 8px; background: var(--plum); color: var(--surface);
  font-family: var(--serif); font-weight: 700; font-size: .95rem; letter-spacing: .02em; }
.wolfi-bust { position: absolute; left: 18px; bottom: 0; width: 72px; height: 110px; }
@media (max-width: 560px) {
  .mascot-says { padding-left: 80px; }
  .mascot-name { left: 80px; }
  .wolfi-bust { left: 10px; width: 56px; height: 86px; }
}
.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; }
/* Sur grand écran, la zone de jeu prend toute la hauteur sous l'en-tête : il tient sur une ligne, les informations du
   morceau à la suite du titre (tronquées au besoin). */
@media (min-width: 1000px) {
  .wrap.wide .piece-head { display: flex; align-items: baseline; gap: 14px; }
  .wrap.wide .piece-title-row { flex: 0 1 auto; max-width: 60%; }
  .wrap.wide .piece-head .meta { flex: 1 1 0; min-width: 0; }
  .wrap.wide .guest-banner { margin-bottom: 10px; }
}
@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); }
/* En haut à gauche : la lettre de la section qu'ouvre la mesure, puis ses boutons (au survol). */
.bar-head { position: absolute; top: 2px; left: 6px; z-index: 1; display: flex; align-items: center; min-height: 22px; }
.bar .sec { padding: 0 3px 0 2px; font-family: var(--serif); font-size: .85rem; line-height: 1; 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); }
/* Une boucle qui attend sa fin (⟲ sur une mesure) : sa mesure de début porte un trait plein, son ⟲ enfoncé et l'étiquette
   « début » ; au survol, la mesure de fin candidate se cerne de tirets et les mesures prises s'éclairent. */
.chart.picking-end .bar { cursor: cell; }
.bar.loop-preview { background: color-mix(in srgb, var(--plum-soft) 75%, transparent); }
.bar.loop-from { box-shadow: inset 0 0 0 2px var(--plum); }
.bar.loop-from .bar-actions { opacity: 1; pointer-events: auto; }
.bar.loop-from::after { content: attr(data-loop-hint); position: absolute; right: 6px; bottom: 0; font-size: .66rem; line-height: 12px;
  font-weight: 700; color: var(--plum); }
.chart.picking-end .bar:hover:not(.loop-from) { outline: 2px dashed var(--plum); outline-offset: -3px; }
.bar:hover { background: color-mix(in srgb, var(--plum-soft) 55%, transparent); }
/* Sur une mesure, au survol (une fois touchée sur un écran tactile, ou au clavier) : lire à partir d'elle, ou la boucler.
   Discrets : l'icône seule, sans fond ; le survol et le focus l'entourent d'un fond léger. Le reste de la mesure sert à se
   placer sur un temps. */
.bar-actions { display: flex; opacity: 0; pointer-events: none; transition: opacity .12s; }
.bar-actions button { display: grid; place-items: center; width: 24px; height: 22px; padding: 0; border: 0; border-radius: 6px;
  background: none; color: var(--plum); cursor: pointer; }
.bar-actions button::before { content: ""; width: 11px; height: 11px; background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat; }
.bar-play { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 1.6v8.8L10.4 6z'/%3E%3C/svg%3E"); }
.bar-play::before { margin-left: 2px; }
.bar-loop { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.2 6.2A5.3 5.3 0 0 0 3.4 4.4M2.8 9.8a5.3 5.3 0 0 0 9.8 1.8'/%3E%3Cpath d='M3.1 1.6v3.2h3.2M12.9 14.4v-3.2H9.7'/%3E%3C/svg%3E"); }
.bar-actions button:hover, .bar-actions button:focus-visible, .bar-loop[aria-pressed="true"] { background: color-mix(in srgb, var(--plum) 16%, transparent); }
@media (hover: hover) { .bar:hover .bar-actions { opacity: 1; pointer-events: auto; } }
.bar.armed .bar-actions, .bar:has(.bar-actions :focus-visible) .bar-actions { opacity: 1; pointer-events: auto; }
@media (pointer: coarse) {
  .bar-actions button { width: 32px; height: 30px; }
  .bar-actions button::before { width: 14px; height: 14px; }
}
@media (prefers-reduced-motion: reduce) { .bar-actions { transition: none; } }
.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, .bar-head),
.chart.has-focus .bar:not(.focus):not(.sel):not(.from):not(.playing) .sec { opacity: .35; }
.bar.focus { border-top: 3px solid var(--plum); padding-top: 8px; }
/* La vue Grille, la carte du morceau (sa forme) en tête. Sur petit écran, la carte et la grille, dans la barre du bas ;
   sur grand écran, la carte, la grille et ses notes-cibles, qui défilent de haut en bas dans la partie gauche (la carte
   laisse la place de la croix). */
.grid-strip { --strip-row: 74px; container: strip / inline-size; margin-top: 8px; display: flex; flex-direction: column; }
.grid-strip > .board { flex: 1 1 auto; min-height: 0; overflow: hidden auto; overscroll-behavior: contain; padding: 0 2px 8px 0; }
.grid-strip > .board .map { margin-right: calc(var(--pane-close, 24px) + 8px); }
.grid-strip > .map { flex: none; margin: 0 0 6px; }
/* En bande : une ligne de mesures qui défile de côté avec la lecture (la grille de la barre du bas, ou la vue Grille trop
   basse pour deux lignes de mesures). */
.grid-strip > .chart, .grid-strip:not(.rows) > .board .chart { display: flex; overflow-x: auto; overflow-y: hidden; margin: 0; border-right: 0;
  scrollbar-width: thin; overscroll-behavior-x: contain; }
.grid-strip > .chart .bar, .grid-strip:not(.rows) > .board .bar { flex: none; width: 124px; min-height: 0; padding: 4px 6px 7px; }
.grid-strip > .chart .bar-head, .grid-strip:not(.rows) > .board .bar-head { top: 0; left: 4px; }
.grid-strip > .chart .bar.focus, .grid-strip:not(.rows) > .board .bar.focus { padding-top: 2px; }
.grid-strip > .chart .bar.pickup, .grid-strip:not(.rows) > .board .bar.pickup { min-height: 0; }
.grid-strip > .chart .bar:last-child, .grid-strip:not(.rows) > .board .bar:last-child { border-right: 4px double var(--ink); }
.grid-strip > .chart .bar .beats, .grid-strip:not(.rows) > .board .bar .beats { margin-top: 14px; }
.grid-strip > .chart .bar .chord b, .grid-strip > .chart .bar .chord small,
.grid-strip:not(.rows) > .board .bar .chord b, .grid-strip:not(.rows) > .board .bar .chord small { line-height: 1.25; }
/* Dans la barre du bas, 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. En plein écran, quand elle remplit la hauteur libre, elle la prend toute. */
.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 > .chart .bar { width: auto; }
.grid-strip.rows > .chart .bar:last-child { border-right: 0; }
.midi[data-fill="grid"] .grid-strip.rows > .chart { flex: 1 1 0; min-height: 0; max-height: none; }
/* La grille partie en bas, les onglets remontent sous la carte du morceau. */
@media (max-width: 999px) { .desk nav.tabs { margin-top: 12px; } }

/* Lecteur : en bas de la partie droite, ou la première ligne de la barre du bas ; il reste toujours visible. */
.player { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 0 0 8px; padding: 0 0 8px; border-bottom: 1px solid var(--rule); }
/* Petit écran, ou partie droite étroite : le lecteur se replie sur « Écouter » et le tempo (plus la position pendant la
   lecture) ; le chevron déplie le reste. Quand il a la place, tout s'affiche, et le chevron disparaît. */
.player-extra { display: contents; }
.player-more, .dock-more, .dock-open { 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, .dock-open { margin-left: auto; }
.dock-open { transform: rotate(180deg); }
@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); }
}
@container player (max-width: 560px) {
  .player-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 { transform: rotate(180deg); }
}
.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; }
/* Une liste du lecteur ou de la barre des vues a la largeur du choix affiché, pas de sa plus longue option : le lecteur
   tient sur deux lignes dans la partie droite. */
.player select, .midi select { field-sizing: content; }
.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; }
@media (max-width: 999px) { .player-status { min-width: 0; } }
@container player (max-width: 560px) { .player-status { min-width: 0; } }
.player-status .position { display: none; color: var(--ink); font-variant-numeric: tabular-nums; }
.player.is-playing .position { display: inline-flex; align-items: baseline; }
/* À l'arrêt, la position choisie (un temps, un endroit de la partition, la molette) s'affiche après la boucle, en or :
   c'est de là que « Écouter » partira. */
.player.cued:not(.is-playing) .position { display: inline-flex; align-items: baseline; }
.player.cued:not(.is-playing) .position b { color: var(--gold); }
/* 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; }
/* Une boucle qui attend sa fin le reste dit pendant la lecture, à la place de la position, et dans le lecteur replié. */
.player.is-playing .loop-label:has(.picking) { display: inline-flex; }
.player:has(.loop-label .picking) .position { display: none; }
.player:has(.loop-label .picking) .player-status { display: inline-flex; flex: 1 1 auto; justify-content: flex-start; white-space: normal; }
.loop-label:has(.picking) { flex-wrap: wrap; gap: 2px 10px; }
.loop-label .picking b { color: var(--plum); }
/* Le décompte de la boucle ne paraît qu'avec la boucle. */
.player:not(:has([data-practice-target="repeat"]:checked)) .loop-count { 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); }
@media (pointer: coarse) { .loop-label .clear { width: 30px; height: 30px; font-size: .8rem; } }
.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, une carte à la fois (timer_controller.js) : tout tient sans faire défiler l'onglet. L'en-tête du jour
   est entier au départ ; pendant les étapes, il se réduit à une ligne. Les jours défilent sur une seule ligne. */
.session-day { border-left: 4px solid var(--gold); padding: 2px 0 2px 14px; margin-bottom: 14px; }
.session-day .eyebrow { color: var(--muted); font-size: .9rem; }
.session-day h3 { font-size: 1.3rem; margin: 2px 0 4px; }
.session-day .goal { margin: 0; }
.session-day .compact-only { display: none; }
.session-day.compact { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; margin-bottom: 12px; }
.session-day.compact h3 { font-size: 1.1rem; margin: 0; }
.session-day.compact .compact-only { display: inline; }
.session-day.compact .goal, .session-day.compact .day-meta, .session-day.compact .day-strip { display: none; }
.session-day .day-meta .linkish { font-size: inherit; }
.day-strip { position: relative; display: flex; gap: 4px; margin-top: 8px; padding: 7px 0 2px; overflow-x: auto; scrollbar-width: none; }
.day-strip::-webkit-scrollbar { display: none; }
.day-strip a { flex: none; 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-strip 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-strip a.done { position: relative; border-color: var(--hit); color: var(--ink); }
.day-strip 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-strip 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; }

/* Une carte : le départ, une étape ou la fin. Celle d'une étape a son minuteur et un seul bouton (lancer, pause,
   reprendre) ; la démo et la validation sont à côté, la note de l'étape à côté de son nom. */
.step-cards { margin: 0; padding: 0; list-style: none; }
.step-card { background: var(--surface); border: 1px solid var(--rule); border-left: 4px solid var(--gold); border-radius: 10px; padding: 12px 16px 14px; }
.step-card .card-top { display: flex; align-items: center; gap: 10px; min-height: 26px; }
.step-card .eyebrow { font-size: .74rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.step-card .playing-note { display: none; }
.session.running .step-card .playing-note { display: inline; }
.step-card .minutes { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: .85rem; white-space: nowrap; }
.step-card .minutes input { width: 52px; padding: 2px 6px; border: 1px solid var(--rule); border-radius: 5px; background: var(--surface); text-align: right; }
.step-card h3 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; font-size: 1.4rem; margin: 2px 0 6px; }
.step-card h3 .grade { margin-left: auto; font-family: var(--sans); }
.step-tip { margin: 0 0 10px; font-size: .95rem; }
.step-run { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding-top: 10px; border-top: 1px solid var(--rule); }
.clock { font-family: var(--serif); font-size: 2.1rem; line-height: 1; font-variant-numeric: tabular-nums; }
/* Le temps de l'étape écoulé : le minuteur le dit, en or, et attend que tu passes à la suite (« Suivante » ressort). */
.step-card.over .clock { font-size: 1.2rem; color: var(--gold); }
.step-run .btn.primary { min-width: 9.5em; text-align: center; }
.step-run .linkish.check { color: var(--muted); font-size: .9rem; }
.step-run .linkish.check:hover { color: var(--plum); }
.btn.big { padding: 10px 22px; font-size: 1.05rem; }
/* Départ : le déroulé du jour, sur deux colonnes (✓ bleu pour une étape validée), puis « Démarrer ». */
.outline { margin: 6px 0 12px; padding: 0; list-style: none; columns: 2; column-gap: 18px; }
.outline li { display: flex; align-items: baseline; gap: 8px; padding: 4px 0; border-bottom: 1px dotted var(--rule); break-inside: avoid; }
.outline .n { flex: none; width: 22px; height: 22px; display: inline-grid; place-items: center; border-radius: 50%; font-size: .78rem; background: var(--rule); }
.outline li.validated .n { background: var(--hit); color: var(--surface); }
.outline li.validated .n::before { content: "✓"; }
.outline li.validated .n span { display: none; }
.outline small { margin-left: auto; color: var(--muted); white-space: nowrap; }
.outline .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Une longue séance passe sur trois colonnes. */
.outline.long { columns: 3; font-size: .9rem; }
.resume { flex: 1 1 240px; color: var(--muted); font-size: .9rem; }
@media (max-width: 560px) { .outline, .outline.long { columns: 1; } }
/* Sous la carte : d'une étape à l'autre (✓ bleu une fois validée, en or l'étape affichée, le drapeau pour la fin), et ce
   qui valide une étape. */
.step-nav { display: flex; align-items: center; gap: 8px; margin: 10px 0 6px; }
.step-dots { display: flex; gap: 6px; margin: 0 auto; padding: 4px 6px 0; list-style: none; overflow-x: auto; scrollbar-width: none; }
.step-dots button { position: relative; flex: none; width: 30px; height: 30px; display: grid; place-items: center; padding: 0; border: 1px solid var(--rule);
  border-radius: 50%; background: var(--surface); font-size: .85rem; color: var(--muted); cursor: pointer; }
.step-dots button:hover { border-color: var(--plum); color: var(--plum); }
.step-dots button.now { background: var(--gold); border-color: var(--gold); color: var(--surface); font-weight: 700; }
.step-dots button.validated { border-color: var(--hit); color: var(--ink); }
.step-dots button.validated::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); }
.step-dots button.end { border-style: dashed; }
@media (pointer: coarse) { .step-dots button { width: 34px; height: 34px; } }
.check-intro { margin: 0; font-size: .82rem; color: var(--muted); }
/* Fin de séance : la note de chaque étape (un clic y retourne), le quiz du jour et la note de séance. */
.step-card.end { border-left-color: var(--hit); }
.step-card.end h3 { font-size: 1.25rem; margin-bottom: 4px; }
.step-card.end .summary { font-family: var(--sans); font-size: .9rem; color: var(--muted); }
.tally { display: flex; flex-wrap: wrap; gap: 4px 6px; margin: 2px 0 8px; padding: 0; list-style: none; }
.tally li { display: inline-flex; align-items: baseline; gap: 6px; padding: 1px 9px; border: 1px solid var(--rule); border-radius: 999px; font-size: .82rem;
  background: var(--bg); }
.tally li.validated { border-color: var(--hit); }
.tally .linkish { color: var(--ink); text-decoration: none; }
.end-quiz { padding: 8px 0; border-top: 1px solid var(--rule); }
.end-quiz-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.end-quiz-head p { flex: 1 1 260px; margin: 0; font-size: .9rem; }
form.log.log-mini { gap: 8px; padding-top: 10px; border-top: 1px solid var(--rule); }
.log-mini .log-row { display: flex; flex-wrap: wrap; align-items: end; gap: 8px 16px; }
.log-mini .log-row label.f { width: 100px; }
.log-mini .log-row legend { margin-bottom: 4px; }
.log-mini .log-note { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 10px; }
.log-mini .log-note textarea { flex: 1 1 240px; min-width: 0; padding: 6px 10px; border: 1px solid var(--rule); border-radius: 7px; background: var(--surface); }
/* L'onglet Séance commence plus près de ses onglets : tout doit tenir sans défiler. */
section.panel:has(> .session) { padding-top: 14px; }
.log-more summary { margin: 0; font-size: .9rem; }
.log-more-fields { display: grid; gap: 12px; margin-top: 10px; }
.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: 0 0 10px; 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, .step-card.validated .phrase-row.whole { border-left-color: var(--hit); }
.phrase-row.validated .phrase-label::before, .step-card.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; }
.step-stage { margin: 10px 0 2px; padding: 10px 12px; border: 1px solid var(--rule); border-radius: 8px; background: var(--bg); }
.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 .step-card .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; }
/* Quiz : on écoute, on devine l'accord qui arrive. */
.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); }
/* Ranger ses morceaux : derniers joués, titre ou genre (le genre choisi à l'import, ou le rayon de la bibliothèque). */
.piece-sort { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 4px; font-size: .9rem; }
.piece-sort .pill { text-decoration: none; }
.piece-sort .pill[aria-current="page"] { background: var(--plum-soft); border-color: var(--plum); color: var(--plum); }
.piece-group { margin: 18px 0 0; font-size: 1rem; color: var(--muted); }
/* « Mes morceaux » sur toute la page : le titre et « Ajouter un morceau » sur une ligne, puis la liste, en deux colonnes
   quand il y a la place. */
.my-pieces { margin-top: 30px; }
.my-pieces-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-bottom: 10px; }
.my-pieces-head h2 { margin: 0; }
.my-pieces .pieces { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); column-gap: 36px; }
/* Sous le titre, de la largeur d'une colonne de .two. */
.genre-field { width: calc(50% - 6px); min-width: min(100%, 200px); }

/* 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); }
/* Les vues du morceau et le clavier MIDI (midi_controller.js). Sur grand écran, la partie gauche de la zone de jeu : une
   carte, la barre des vues en haut, les vues dessous. Sur petit écran, une barre fixée en bas de l'écran, repliable et
   agrandissable, avec le lecteur en haut ; ses poignées sont en onglet, qui dépasse au-dessus de la barre (--dock-pull)
   et n'y entre que de --dock-tab. */
.midi { container: stage / inline-size;
  --k-tone: color-mix(in srgb, var(--plum) 28%, var(--key-white)); --k-guide: color-mix(in srgb, var(--gold) 50%, var(--key-white)); }
:root { --dock-tab: 0px; --dock-pull: 0px; }
@media (max-width: 999px) {
  :root { --dock-tab: 8px; --dock-pull: 28px; }
  .midi { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; padding: calc(8px + var(--dock-tab) / 2) 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); }
  .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; }
}
@media (pointer: coarse) and (max-width: 999px) { :root { --dock-pull: 38px; } }
/* Partie gauche : les vues se partagent la hauteur de la carte ; celle qui la remplit (data-fill) prend ce que les autres
   laissent, les poignées entre elles règlent la répartition. */
@media (min-width: 1000px) {
  .midi { display: flex; flex-direction: column; min-width: 0; min-height: 0; padding: 10px 12px 12px;
    background: var(--surface); border: 1px solid var(--rule); border-radius: 12px; }
  .midi-inner { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
  .midi-inner > * { flex: none; }
}
/* La barre des vues ; sur grand écran, en haut à droite, le plein écran et, en plein écran, de quoi masquer la partie droite. */
.views-head { display: flex; align-items: flex-start; gap: 8px; }
.views-head > .midi-bar { flex: 1 1 auto; min-width: 0; }
.stage-tools { display: none; }
@media (min-width: 1000px) {
  .stage-tools { display: flex; flex: none; gap: 2px; }
  .stage-tools button { display: inline-grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%;
    background: none; color: var(--muted); cursor: pointer; }
  .stage-tools button:hover { color: var(--plum); background: var(--plum-soft); }
  .stage-tools button[aria-pressed="true"] { color: var(--plum); }
  .stage-tools .stage-side { display: none; }
  .workspace.full .stage-tools .stage-side { display: inline-grid; }
}
.stage-full .leave, .stage-full[aria-pressed="true"] .enter, .dock-full .leave, .dock-full[aria-pressed="true"] .enter { display: none; }
.stage-full[aria-pressed="true"] .leave, .dock-full[aria-pressed="true"] .leave { display: inline; }
.stage-side[aria-pressed="true"] .shown { display: none; }
/* Sur petit écran, les poignées de la barre du bas : replier ou déplier les vues d'un clic, la poignée (on la tire pour
   agrandir ou rabattre les vues), le plein écran. Des boutons ronds, plus gros sous le doigt, en onglet à cheval sur le
   bord haut de la barre. */
.dock-tab { display: none; }
@media (max-width: 999px) {
  .dock-tab { position: absolute; bottom: calc(100% - var(--dock-tab)); left: 50%; z-index: 3; display: flex; align-items: center; gap: 2px;
    padding: 2px; transform: translateX(-50%); background: var(--surface); border: 1px solid var(--rule); border-radius: 999px;
    box-shadow: 0 -5px 12px -9px rgb(0 0 0 / .45); }
}
.dock-tab button { display: inline-grid; place-items: center; flex: none; width: 30px; height: 30px; padding: 0; border: 0; border-radius: 50%;
  background: none; color: var(--muted); cursor: pointer; }
.dock-tab button:hover { color: var(--plum); background: var(--plum-soft); }
.dock-fold svg { transition: transform .15s; }
.midi.folded .dock-fold svg { transform: rotate(180deg); }
/* Barre du bas fermée par son chevron (plus aucune vue) : il ne reste que le lecteur replié, « Écouter » et le tempo,
   avec à droite le chevron qui la rouvre (elle revient alors avec son onglet) ; onglet, pastilles, état du clavier et
   ligne « Attendu » disparaissent. Le lecteur garde ses réglages dépliés ou non pour la réouverture. En plein écran, la
   barre des vues reste : c'est elle qui les rouvre. */
@media (max-width: 999px) {
  .midi.folded:not(.full) .midi-inner > :not(.player-slot), .midi.folded:not(.full) .player-extra, .midi.folded:not(.full) .dock-tab,
  .midi.folded:not(.full) .player-more, .midi.folded:not(.full) .player:not(.is-playing) .player-status { display: none; }
  .midi.folded:not(.full) { padding-top: 8px; }
  .midi.folded:not(.full) .player { margin: 0; padding: 0; border-bottom: 0; }
  .midi.folded:not(.full) .dock-open { display: inline-grid; place-items: center; }
}
.dock-full[aria-pressed="true"] { color: var(--plum); }
.dock-tab .grip { position: relative; width: 58px; border-radius: 999px; cursor: ns-resize; touch-action: none; }
.dock-tab .grip::before, .dock-tab .grip::after { content: ""; position: absolute; left: 50%; width: 26px; height: 2.5px; margin-left: -13px;
  border-radius: 2px; background: currentColor; }
.dock-tab .grip::before { top: calc(50% - 4px); }
.dock-tab .grip::after { top: calc(50% + 1.5px); }
.resizing-keyboard .dock-tab .grip { color: var(--plum); background: var(--plum-soft); }
@media (pointer: coarse) {
  .dock-tab { gap: 4px; padding: 3px; }
  .dock-tab button { width: 38px; height: 38px; }
  .dock-tab .grip { width: 72px; }
  .dock-tab .grip::before, .dock-tab .grip::after { width: 32px; margin-left: -16px; height: 3px; }
}
@media (prefers-reduced-motion: reduce) { .dock-fold svg { transition: none; } }
.resizing-keyboard, .resizing-keyboard * { cursor: ns-resize; user-select: none; }
/* Plein écran sur petit écran : la barre du bas prend tout l'écran, de haut en bas de ce qu'on voit (100dvh suit les
   barres de Safari), sous l'heure et la batterie comprises ; de ses poignées ne reste que le bouton pour en sortir, en
   haut de l'écran, sous l'encoche. Comme dans la partie gauche, la première vue affichée remplit la hauteur libre.
   Autour, la page se cache et prend le fond des vues : rien ne transparaît quand les barres de Safari bougent. */
@media (max-width: 999px) {
  .midi.full { top: 0; bottom: auto; height: 100vh; height: 100dvh; display: flex; flex-direction: column; border-top: 0;
    padding-top: calc(58px + env(safe-area-inset-top, 0px)); }
  .midi.full .dock-tab { top: calc(8px + env(safe-area-inset-top, 0px)); bottom: auto; }
  .play-full, .play-full body { background: var(--surface); }
  .play-full .wrap { visibility: hidden; }
  .play-full .midi { visibility: visible; }
  .midi.full .dock-fold, .midi.full .grip { display: none; }
  .midi.full .midi-inner { flex: 1 1 auto; min-height: 0; width: 100%; max-height: none; display: flex; flex-direction: column; overflow: hidden; }
  .midi.full .midi-inner > * { flex: none; }
}
/* Les vues s'affichent chacune à part (pastilles de la barre, pressées quand elles sont visibles). La pastille Partition
   n'apparaît que quand il y a une partition ; de mémoire, la pastille Cascade disparaît aussi. */
.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, .from-memory .cascade-toggle { display: none; }
.midi.hide-grid .grid-strip, .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, 90px); 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 + .pane-close + .keyboard { margin-top: 0; }
/* Tailles des vues sans réglage retenu, dans la partie gauche et en plein écran (la barre du bas garde les siennes). */
@media (min-width: 1000px) {
  .cascade { height: var(--cascade-height, 150px); }
  .midi .keyboard { height: var(--key-height, 96px); }
}
.midi.full .cascade { height: var(--cascade-height, 180px); }
.midi.full .keyboard { height: var(--key-height, 150px); }
/* La vue qui remplit la hauteur libre (data-fill). La partition en bande s'y agrandit ; en feuille, sa zone la prend. */
.midi[data-fill="grid"] .grid-strip { flex: 1 1 0; min-height: 0; }
.midi[data-fill="score"] .score-strip:not([hidden]) { flex: 1 1 0; min-height: 0; }
.midi[data-fill="score"] .score-strip svg { height: 100%; }
.midi[data-fill="score"] .score-strip.sheet { height: auto; }
.midi[data-fill="score"] .score-strip.sheet svg { height: auto; }
.midi[data-fill="cascade"] .cascade { flex: 1 1 0; height: auto; min-height: 0; }
.midi[data-fill="keys"] .keyboard { flex: 1 1 0; height: auto; min-height: 0; }
/* Un piano crédible, même seul à remplir la hauteur ou tiré par une poignée : une touche blanche au plus six fois plus
   longue que large (la largeur du clavier, 100cqw, sur ses touches blanches), et jamais plus de 160 px sur un téléphone,
   260 px ailleurs (KEY_HEIGHT et KEY_RATIO, midi_controller.js). */
.midi .keyboard { max-height: min(260px, 100cqw / var(--whites, 36) * 6); }
@media (max-width: 999px) { .midi .keyboard { max-height: min(160px, 100cqw / var(--whites, 36) * 6); } }
/* Seul à remplir la hauteur, il se pose en bas, sous les doigts, la place libre au-dessus. */
.midi[data-fill="keys"] .keyboard { margin-top: auto; }
/* Chaque vue a sa croix, en haut à droite, qui la ferme (son bouton, dans la barre, la rouvre) ; posée sur la vue, elle ne
   prend pas de place : la partition, la cascade et le piano gardent toute la largeur. */
.midi { --pane-close: 24px; }
@media (pointer: coarse) { .midi { --pane-close: 32px; } }
.pane-close { position: relative; z-index: 3; height: 0; }
.pane-close button { position: absolute; top: 12px; right: 4px; display: grid; place-items: center; width: var(--pane-close); height: var(--pane-close);
  padding: 0; border: 1px solid var(--rule); border-radius: 50%; background: color-mix(in srgb, var(--surface) 90%, transparent); color: var(--muted);
  cursor: pointer; }
.pane-close button:hover { color: var(--plum); border-color: var(--plum); }
.pane-close.for-grid button { top: 10px; }
.midi:not(.hide-cascade) .cascade + .splitter + .pane-close.for-keys button { top: 4px; }
.midi.hide-grid .pane-close.for-grid, .midi.hide-score .pane-close.for-score, .pane-close.for-score:has(+ .score-strip[hidden]),
.midi.hide-cascade .pane-close.for-cascade, .midi.hide-keys .pane-close.for-keys { display: none; }
/* Sur un téléphone, la bande de grille garde toute la largeur : sa pastille, juste au-dessus, la ferme. */
@media (max-width: 999px) { .pane-close.for-grid { display: none; } }
/* La partition et la cascade se cachent dans un quiz ou une étape de mémoire : leurs croix aussi. */
.quizzing .pane-close.for-cascade, .quiz-blind .pane-close.for-cascade, .from-memory .pane-close.for-cascade,
.quizzing .pane-close.for-score, .quizzing-notes .pane-close.for-score, .quiz-blind .pane-close.for-score { visibility: hidden; }
/* 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 : un bouton rond au milieu, et
   toute la jointure se tire à la souris. Sous le doigt, seul le bouton, plus gros, se tire : le haut des touches reste
   libre pour jouer. Au repos, le bouton s'efface pour ne rien cacher de la vue du dessus : il paraît au survol, au
   toucher et tant qu'on tire ; sa zone, elle, reste là pour être attrapée. */
.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: -8px; width: 48px; height: 16px; margin-left: -24px; border-radius: 999px;
  border: 1px solid var(--rule); box-shadow: 0 2px 6px -3px rgb(0 0 0 / .4); pointer-events: none; opacity: 0; transition: opacity .15s;
  background: linear-gradient(var(--muted), var(--muted)) center / 22px 2.5px no-repeat, var(--surface); }
.splitter:hover::after, .splitter:active::after, .resizing-keyboard .splitter::after { opacity: 1;
  border-color: var(--plum); background: linear-gradient(var(--plum), var(--plum)) center / 22px 2.5px no-repeat, var(--plum-soft); }
@media (prefers-reduced-motion: reduce) { .splitter::after { transition: none; } }
@media (pointer: coarse) {
  .splitter::before { left: 50%; right: auto; width: 112px; margin-left: -56px; top: -18px; height: 36px; }
  .splitter::after { width: 64px; height: 22px; margin-left: -32px; top: -11px;
    background: linear-gradient(var(--muted), var(--muted)) center / 28px 3px no-repeat, var(--surface); }
}
/* Juste au-dessus du piano, la poignée se pose sur la vue du dessus : les touches restent libres pour jouer. */
.splitter.on-keys::before { top: -16px; }
.splitter.on-keys::after { top: -19px; }
@media (pointer: coarse) {
  .splitter.on-keys::before { top: -36px; }
  .splitter.on-keys::after { top: -27px; }
}
.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; }
/* Le bout de partition du passage : il défile avec la lecture, sur son papier (--paper), avec un peu de marge autour. */
.score-strip { margin-top: 8px; padding: 4px 8px; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--rule); border-radius: 6px;
  background: var(--paper); scrollbar-width: thin; }
/* La partition se met à l'échelle réglée par les poignées (--score-scale) : plus haute, elle s'élargit d'autant. Sans
   réglage, elle part à l'échelle de lecture (--score-reading) : à sa taille dans la partie gauche et en plein écran,
   réduite sur un téléphone pour laisser la place aux autres vues. */
.midi { --score-reading: .55; }
@media (min-width: 1000px) { .midi { --score-reading: 1; } }
.midi.full { --score-reading: 1; }
@media (max-width: 999px) { .midi.full { --score-reading: .7; } }
.score-strip svg { display: block; height: calc(var(--natural, 96px) * var(--score-scale, var(--score-reading))); width: auto; }
/* Mode feuille : dès que sa zone peut montrer deux lignes, la partition passe à la ligne comme une page, à l'échelle de
   lecture, et se lit de haut en bas ; sa zone garde la hauteur réglée et descend avec la lecture (practice_controller.js). */
.score-strip.sheet { height: calc(var(--natural, 96px) * var(--score-scale, var(--score-reading))); overflow: hidden auto; scrollbar-gutter: stable; }
.score-strip.sheet svg { width: 100%; height: auto; }
.score-strip .staff { stroke: color-mix(in srgb, var(--paper-ink) 40%, var(--paper)); stroke-width: 1; }
.score-strip .barline { stroke: var(--paper-ink); stroke-width: 1.2; }
.score-strip .barline.end { stroke-width: 2.4; }
.score-strip .head { fill: var(--paper-ink); }
.score-strip .head.open { fill: var(--paper); stroke: var(--paper-ink); stroke-width: 1.4; }
.score-strip .stem, .score-strip .flag, .score-strip .ledger { stroke: var(--paper-ink); stroke-width: 1.2; fill: none; }
.score-strip .dot { fill: var(--paper-ink); }
.score-strip .clef, .score-strip .acc { font-family: "Noto Music", var(--serif); fill: var(--paper-ink); }
.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(--paper-ink) 55%, var(--paper)); }
.score-strip .cursor { stroke: var(--gold); stroke-width: 2; }
/* Un clic dans la partition s'y place. */
.score-strip svg { cursor: pointer; }
/* La partition suit le thème : le bleu et l'orange du thème, lisibles sur son papier. 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 .head.ok { fill: var(--hit); }
.score-strip .head.ko { fill: var(--paper); stroke: var(--miss); stroke-width: 1.8; stroke-dasharray: 2.2 1.4; }
.score-strip .head.open.ok { fill: var(--paper); stroke: var(--hit); stroke-width: 2; }
.score-strip .head.open.ko { fill: var(--paper); 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 des vues tient sur une ligne : l'aide se tronque (texte entier au survol). Dans une partie
   gauche étroite, le son du clavier et la vérification attendent le chevron, comme sur un téléphone ; dépliée, la barre
   passe sur deux lignes. */
@media (min-width: 1000px) {
  .midi.collapsed .midi-bar { flex-wrap: nowrap; }
  .midi-status { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
@container stage (max-width: 979px) {
  .dock-more { display: inline-grid; place-items: center; }
  .midi.collapsed .midi-bar .dock-extra { display: none; }
  .midi:not(.collapsed) .dock-more { transform: rotate(180deg); }
}
@container stage (max-width: 899px) { .midi .wide-only { display: none; } }
@container stage (max-width: 619px) {
  .midi.collapsed .midi-bar { flex-wrap: wrap; }
  .midi.collapsed .midi-status { flex: 1 1 calc(100% - 200px); }
  .midi.collapsed .midi-feedback:not(:has(span:not(:empty))) { display: none; }
}
/* Petit écran, barre repliée : les quatre vues et le chevron sur une ligne, puis l'état (tronqué) et le branchement, puis
   ce qui est attendu ; le son du clavier, la vérification et la légende attendent le chevron. Ce qui est attendu, joué et
   vérifié a sa ligne à lui, de hauteur réservée même vide : rien ne saute dessous quand l'accord change ou qu'une note
   se tait, pendant la lecture. */
@media (max-width: 999px) {
  .midi-bar { column-gap: 8px; }
  .dock-more { order: 1; margin-left: auto; }
  .midi.collapsed .dock-extra { display: none; }
  .midi.collapsed .midi-status { order: 2; flex: 1 1 calc(100% - 140px); min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .midi.collapsed [data-midi-target="connect"] { order: 3; }
  .midi .midi-feedback, .midi.collapsed .midi-feedback:not(:has(span:not(:empty))) { order: 4; display: flex; flex: 1 1 100%; height: 1.45em;
    line-height: 1.45em; }
}
.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, iPhone, iPad), le clavier à l'écran montre seulement l'accord attendu. Le chevron de la barre
   n'aurait plus rien à déplier (le son du clavier et la vérification servent au clavier MIDI) : il s'efface, et le
   message qui dit pourquoi se lit en entier. */
.no-midi .midi-only, .no-midi .dock-more { display: none; }
@media (max-width: 999px) {
  .no-midi .midi.collapsed .midi-status { flex-basis: 100%; white-space: normal; overflow: visible; }
}
.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; }
/* Au-dessus de la barre du clavier, et de son onglet. */
:root:has(.midi[data-controller~="midi"]) .toast { bottom: calc(18px + var(--dock-pull) + max(var(--dock-height, 0px), env(safe-area-inset-bottom, 0px))); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* ---------- Le niveau au piano ---------- */
/* Le quiz de niveau (level_quiz_controller.js), sous l'avatar à la bienvenue ; dans « Mon compte », sans cadre (in-card). */
.level-quiz { display: grid; gap: 14px; padding: 16px 18px 18px; border: 1px solid var(--rule); border-radius: 12px; background: var(--surface); }
.level-quiz.in-card { padding: 0; border: 0; background: none; }
.level-quiz > [data-level-quiz-target="intro"], .level-quiz .quiz-stage, .level-quiz .level-result { display: grid; gap: 12px; }
.level-quiz p { margin: 0; }
.level-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.level-head:not(:has(h2)):has(.count:empty) { display: none; }
.level-head h2 { font-size: 1.25rem; margin: 0; }
.level-head .count { margin-left: auto; color: var(--muted); font-size: .88rem; }
.wolfi-says { display: flex; gap: 12px; align-items: flex-start; }
.wolfi-says .logo { width: 46px; height: 46px; }
/* L'escalier : trois paliers (les niveaux) de deux marches (les questions). Réussie : bleue avec ✓ ; celle où l'on s'arrête :
   rayée d'orange avec ✗ ; celle en cours : cerclée de prune. */
.stairs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 0; padding: 0; list-style: none; }
.landing { display: grid; gap: 5px; }
.landing-steps { display: flex; align-items: flex-end; gap: 4px; height: 56px; border-bottom: 2px solid var(--rule); }
.landing-name { text-align: center; font-size: .8rem; color: var(--muted); }
.landing.here .landing-name { color: var(--ink); font-weight: 700; }
.landing.here .landing-steps { border-bottom-color: var(--plum); }
.marche { flex: 1; display: flex; align-items: center; justify-content: center; gap: 3px; min-height: 20px; border: 1px solid var(--rule);
  border-bottom: 0; border-radius: 6px 6px 0 0; background: var(--bg); color: var(--muted); font-size: .8rem; font-weight: 700; line-height: 1;
  white-space: nowrap; }
.marche.m1 { height: 32%; } .marche.m2 { height: 44%; } .marche.m3 { height: 56%; }
.marche.m4 { height: 68%; } .marche.m5 { height: 84%; } .marche.m6 { height: 100%; }
.marche.done { background: var(--hit); border-color: var(--hit); color: var(--surface); }
.marche.done::after { content: "✓"; }
.marche.now { border: 2px solid var(--plum); border-bottom: 0; background: var(--plum-soft); color: var(--plum); }
.marche.missed { border-color: var(--miss); color: var(--surface);
  background: repeating-linear-gradient(135deg, var(--miss) 0 4px, color-mix(in srgb, var(--miss) 45%, var(--surface)) 4px 7px); }
.marche.missed::after { content: "✗"; }
/* Une question : l'énoncé, comment répondre, le piano, le verdict. Young Serif n'a pas ♭ ni ♯ : Noto Music les dessine
   (A♭maj7), sans le trou d'une police de secours. */
.quiz-prompt, .chord-slots li, .quiz-picked b { font-family: "Young Serif", "Noto Music", Georgia, serif; }
.quiz-prompt { font-size: clamp(1.35rem, 4vw, 1.7rem); line-height: 1.25; }
.level-quiz .quiz-how { margin-top: -6px; color: var(--muted); font-size: .92rem; }
.level-hint { padding: 8px 12px; border-radius: 8px; background: var(--plum-soft); font-size: .95rem; }
.quiz-keys { --key-height: 112px; margin: 0; }
/* Le piano du quiz : touches cochées (un accord composé à l'écran) en prune ; juste en bleu avec ✓ ; fausse rayée d'orange
   avec ✗. Pas de nom sur les touches : c'est ce qu'on mesure. */
.keyboard .picked, .keyboard .black.picked { background: var(--plum); }
.keyboard .right, .keyboard .black.right { background: var(--hit); }
.keyboard .wrong, .keyboard .black.wrong {
  background: repeating-linear-gradient(135deg, var(--miss) 0 4px, color-mix(in srgb, var(--miss) 45%, var(--key-white)) 4px 7px); }
.keyboard .right::after, .keyboard .wrong::after { position: absolute; left: 0; right: 0; bottom: 8px; text-align: center; font-weight: 700;
  font-size: .95rem; line-height: 1; color: var(--surface); }
.keyboard .right::after { content: "✓"; }
.keyboard .wrong::after { content: "✗"; color: var(--key-black); }
.keyboard .black.right::after, .keyboard .black.wrong::after { bottom: 5px; font-size: .75rem; color: var(--key-white); }
.quiz-verdict { display: flex; gap: 8px; align-items: baseline; }
.quiz-verdict:empty { display: none; }
.quiz-verdict::before { flex: none; font-weight: 700; }
.quiz-verdict.hit::before { content: "✓"; color: var(--hit); }
.quiz-verdict.miss::before { content: "✗"; color: var(--miss); }
.quiz-tries { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-size: .85rem; }
.quiz-tries i { width: 10px; height: 10px; border: 1.5px solid var(--muted); border-radius: 50%; }
.quiz-tries i.used { border-color: var(--miss);
  background: repeating-linear-gradient(135deg, var(--miss) 0 2px, color-mix(in srgb, var(--miss) 40%, var(--surface)) 2px 4px); }
.quiz-picked { font-size: .95rem; }
.quiz-picked b { font-weight: 400; font-size: 1.1rem; }
.quiz-small { font-size: .88rem; }
.chord-slots { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.chord-slots li { min-width: 60px; padding: 6px 10px; border: 1px dashed var(--rule); border-radius: 8px; text-align: center;
  font-size: 1.1rem; color: var(--muted); }
.chord-slots li.hit { border: 1px solid var(--hit); color: var(--ink); }
.chord-slots li.hit::after { content: " ✓"; color: var(--hit); font-family: var(--sans); font-weight: 700; font-size: .9rem; }
.chord-slots li.now { border: 2px solid var(--plum); color: var(--plum); }
.quiz-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.quiz-actions .push { margin-left: auto; }
.quiz-source { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; padding-top: 10px; border-top: 1px solid var(--rule);
  color: var(--muted); font-size: .88rem; }
/* Le résultat : le niveau, ce qu'il change. */
.level-badge { display: flex; align-items: center; gap: 14px; }
.level-badge p { margin: 0; }
.level-badge strong { display: block; font-family: var(--serif); font-size: 1.6rem; font-weight: 400; line-height: 1.2; }
.level-effects { margin: 0; padding-left: 1.2em; }
.level-effects li + li { margin-top: 4px; }
/* Un morceau plus dur que son niveau : or et triangle (la prudence), jamais l'orange des fautes ; sur la règle des niveaux,
   « toi » est un rond, « ce morceau » un losange. */
.level-warning { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; margin: 4px 0; padding: 12px 16px;
  border: 1px solid color-mix(in srgb, var(--gold) 50%, var(--rule)); border-left: 4px solid var(--gold); border-radius: 10px;
  background: color-mix(in srgb, var(--gold) 9%, var(--surface)); }
.level-warning .warn-icon { width: 26px; height: 26px; margin-top: 2px; color: var(--gold); }
.level-warning > div { display: grid; gap: 6px; min-width: 0; }
.level-warning strong { font-size: 1.02rem; }
.level-warning p, .level-warning .song-links { margin: 0; }
/* Les morceaux proposés, l'un sous l'autre : la grille de la bibliothèque (260 px au moins) déborderait d'un petit téléphone. */
.level-warning .song-links { grid-template-columns: 1fr; }
.panel .level-warning { margin: 0 0 14px; }
.level-ruler { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; max-width: 420px; margin: 2px 0; padding: 0; list-style: none; }
.level-ruler li { position: relative; padding: 4px 6px 4px 22px; border-top: 3px solid var(--rule); font-size: .82rem; color: var(--muted); }
.level-ruler li.you, .level-ruler li.piece { color: var(--ink); font-weight: 700; }
.level-ruler li.you { border-top-color: var(--plum); }
.level-ruler li.piece { border-top-color: var(--gold); }
.level-ruler li.you::before { content: ""; position: absolute; left: 4px; top: 8px; width: 11px; height: 11px; border-radius: 50%; background: var(--plum); }
.level-ruler li.piece::before { content: ""; position: absolute; left: 5px; top: 8px; width: 10px; height: 10px; transform: rotate(45deg); background: var(--gold); }
/* Le jour 0 : une étiquette qui dit pourquoi il est là. Un niveau qui monte, dans le résultat d'une étape. */
.for-you { display: inline-block; padding: 0 8px; border: 1px solid var(--plum); border-radius: 999px; color: var(--plum); font-size: .78rem; font-weight: 700; }
.result .level-up { padding: 6px 10px; border-left: 4px solid var(--hit); border-radius: 6px; background: color-mix(in srgb, var(--hit) 10%, var(--surface)); font-weight: 700; }
/* « Trouve le sol » : des notes à trouver sur le clavier (le jour 0). */
.find-note { display: grid; gap: 6px; }
.find-note .quiz-prompt { font-size: 1.4rem; }
