:root {
  --bg: #f6f4ff;
  --surface: #ffffff;
  --text: #1f1b3a;
  --muted: #6b6788;
  --primary: #5b4bdb;
  --primary-text: #ffffff;
  --border: #dcd8f0;
  --correct: #1f9d55;
  --correct-bg: #e5f7ec;
  --wrong: #d64545;
  --wrong-bg: #fdeaea;
  --star: #f5b301;
  --help: #3aa0ff; /* a star earned with help (💡) */
  --key-white: #ffffff;
  --key-black: #1f1b3a;
  /* Progress chart status colors, checked for color-blind separation and contrast. */
  --prog-known: #1f7fb8;
  --prog-learning: #d99a1e;
  --prog-hard: #d64545;
  --radius: 18px;
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #15132a;
    --surface: #221f3d;
    --text: #f1efff;
    --muted: #a7a3c7;
    --primary: #8b7dff;
    --primary-text: #120f26;
    --border: #3a3660;
    --correct: #4cd486;
    --correct-bg: #183627;
    --wrong: #ff7b7b;
    --wrong-bg: #3d1d24;
    --key-white: #e9e7f7;
    --key-black: #0c0a1a;
    --prog-known: #3a86c4;
    --prog-learning: #b8801f;
    --prog-hard: #d04f6a;
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  overscroll-behavior: none;
}

#app {
  max-width: 1100px;
  margin: 0 auto;
  min-height: 100%;
  padding: max(12px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
    max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
}

.screen { display: none; }
#screen-play.active {
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  gap: 14px;
  height: calc(100dvh - 28px);
}
#screen-settings.active { display: flex; flex-direction: column; gap: 16px; }
#screen-map.active { display: flex; flex-direction: column; gap: 14px; height: calc(100dvh - 28px); }
/* Only the game uses the full width of a tablet; reading screens stay narrow. */
#screen-settings, .picker { max-width: 720px; margin: 0 auto; }

[hidden] { display: none !important; }

/* Opening the app: its icon and name, until the players can show. */
#screen-start.active {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: calc(100dvh - 28px);
}
.start-icon { width: 112px; height: 112px; border-radius: 26px; animation: breathe 1.6s ease-in-out infinite; }
.start-name { margin: 0; font-size: 1.6rem; font-weight: 800; color: var(--primary); letter-spacing: 0.5px; }
.start-dots { display: flex; gap: 8px; margin: 0; }
.start-dots span { width: 10px; height: 10px; border-radius: 50%; background: var(--primary); opacity: 0.25; animation: start-dot 1.2s ease-in-out infinite; }
.start-dots span:nth-child(2) { animation-delay: 0.2s; }
.start-dots span:nth-child(3) { animation-delay: 0.4s; }
/* The first time: what downloads to work offline (offline.js), one row and bar per part. */
.prepare { width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px; padding: 0 8px; text-align: left; }
.prepare h2 { margin: 8px 0 0; font-size: 1.2rem; text-align: center; }
.prepare p { margin: 0; color: var(--muted); font-size: 0.95rem; text-align: center; }
.prepare-list { list-style: none; margin: 6px 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.prepare-list li { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; font-weight: 600; }
.prepare-size { color: var(--muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.prepare-bar { grid-column: 1 / -1; height: 8px; border-radius: 4px; background: color-mix(in srgb, var(--primary) 15%, transparent); overflow: hidden; }
.prepare-bar span { display: block; height: 100%; width: 0; border-radius: 4px; background: var(--primary); transition: width 0.3s; }
.prepare-list li.ready .prepare-size::after { content: ' ✓'; color: var(--correct); font-weight: 700; }
.prepare-list li.failed .prepare-bar span { background: var(--muted); }
.prepare-status { min-height: 1.4em; }
.prepare .btn-small { align-self: center; }
@keyframes start-dot { 40% { opacity: 1; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) {
  .start-icon, .start-dots span { animation: none; }
}
button { font: inherit; color: inherit; cursor: pointer; touch-action: manipulation; }

/* ---------- top bar ---------- */

.topbar { display: flex; align-items: center; gap: 4px; }
/* The stars take the room the buttons leave, each at most a tenth of it, so the bar fits a
   narrow phone whatever buttons show (container query units). */
.stars { flex: 1; min-width: 0; display: flex; justify-content: center; gap: 1px; font-size: clamp(0.85rem, 4.4vw, 2rem); container-type: inline-size; }
.star { color: var(--border); transition: color 0.2s ease, transform 0.3s ease; font-size: min(1em, 9cqi); }
.star.on { color: var(--star); transform: scale(1.15); }
.star.help { color: var(--help); }
.star.miss { color: var(--wrong); }

.parents-btn {
  flex: none;
  position: relative;
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  padding: 8px;
  opacity: 0.6;
}
.parents-btn svg { width: 100%; height: 100%; fill: var(--muted); }
.map-btn { flex: none; width: 40px; height: 44px; border: none; background: transparent; padding: 0; font-size: 1.7rem; }
.map-btn:active { transform: scale(0.92); }
/* 👀 in the card's top-left corner, faint like ⏸ opposite until a grown-up turns it on. */
.watch-btn {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 3;
  width: 48px;
  height: 48px;
  padding: 0;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--border) 70%, transparent);
  font-size: 1.5rem;
  line-height: 1;
}
.watch-btn:not(.on) { filter: grayscale(0.4); opacity: 0.7; }
.watch-btn.on { background: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 35%, transparent); }
.watch-btn:active { transform: scale(0.92); }
.lock-btn[hidden] { display: none; }
.watch-btn[hidden], .card.held .watch-btn, .card.celebrate .watch-btn { display: none; }

/* A grown-up watching: the refused answer waiting for their verdict, at the top of the card. */
.watch-strip {
  position: absolute;
  top: 8px;
  left: 8px;
  right: 8px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 12px;
  border-radius: 16px;
  background: var(--primary);
  color: var(--primary-text);
  font-weight: 700;
}
.watch-strip[hidden] { display: none; }
/* Meanwhile the card's corner buttons (💡, ⏸) make room: the answer waits for the grown-up. */
.card.held .pause-btn, .card.held .help-btn { display: none; }
.watch-question { flex: 1; min-width: 0; }
.watch-action { flex: none; width: 44px; height: 44px; border: none; border-radius: 12px; background: var(--surface); color: var(--text); font-size: 1.3rem; font-weight: 800; }
.watch-action:disabled { opacity: 0.4; }
.watch-yes { color: var(--correct); }
.watch-no { color: var(--wrong); }

/* ---------- note card ---------- */

.card {
  position: relative;
  min-height: 0;
  background: var(--surface);
  border: 4px solid var(--border);
  border-radius: 28px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.card.correct, .card.celebrate { background: var(--correct-bg); border-color: var(--correct); }
.card.celebrate { overflow: hidden; } /* the animal's parade starts off the card */
.card.shown { border-color: var(--primary); }
.card.wrong { background: var(--wrong-bg); border-color: var(--wrong); animation: shake 0.35s; }
.card.celebrate .feedback { color: var(--star); letter-spacing: 2px; animation: bounce 0.6s ease infinite alternate; }

@keyframes shake {
  25% { transform: translateX(-10px); }
  50% { transform: translateX(10px); }
  75% { transform: translateX(-5px); }
}
@keyframes bounce { to { transform: scale(1.25); } }
.card.celebrate .feedback { font-size: clamp(4rem, 22vw, 7rem); }
.card.celebrate .staff { opacity: 0.15; transition: opacity 0.4s ease; }

.confetti {
  position: fixed;
  top: -12px;
  width: 10px;
  height: 14px;
  border-radius: 2px;
  z-index: 10;
  pointer-events: none;
  animation: fall linear forwards;
}
.confetti.animal { width: 2.6rem; height: auto; border-radius: 0; }
@keyframes fall {
  to { transform: translate(var(--drift), 105vh) rotate(var(--spin)); }
}

/* End of round: the player's animal celebrates over the faded staff. The box moves it
   around the card (pop-in, parade); the animal moves itself (see app.js, CHEERS). */
.cel-animal {
  position: absolute;
  top: 6%;
  left: 50%;
  z-index: 2;
  width: clamp(5rem, 26vw, 9rem);
  transform: translateX(-50%);
  pointer-events: none;
}
.cel-animal { animation: pop-in 0.4s ease-out both; }
.cel-parade { animation: parade 3.6s ease-in-out both; }

@keyframes pop-in { from { transform: translateX(-50%) scale(0); } }
/* In from the left, across to the right, back to the middle, then a twirl. */
@keyframes parade {
  0% { left: -20%; }
  35% { left: 85%; }
  65% { left: 50%; transform: translateX(-50%) scale(1) rotate(0); }
  85% { transform: translateX(-50%) scale(1.35) rotate(360deg); }
  100% { left: 50%; transform: translateX(-50%) scale(1.2) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .cel-animal { animation: none !important; }
}

/* A player's animal in a button or a tile: as big as the emoji it replaced (font-size). */
.animal-pic { display: inline-block; width: 1.2em; vertical-align: middle; }
.animal-pic svg { display: block; }
.player-btn .animal-pic { width: 1.45em; }

.staff { width: 100%; max-width: 640px; flex: 1; min-height: 0; display: flex; align-items: center; }
.staff svg { display: block; width: 100%; height: 100%; max-height: 100%; }

/* VexFlow draws in black; invert it on dark backgrounds. */
@media (prefers-color-scheme: dark) {
  .staff svg { filter: invert(1) hue-rotate(180deg); }
}

/* The 💡 walk (staff.js, markWalk): the clef and the line it marks glow, then one note hops
   up or down the staff in the note's place, yellow at the start and blue for each step, with
   a little pop each time; the note itself turns yellow when reached. A wrong answer shows
   the same way, in red (markWrong). On dark backgrounds the whole drawing is inverted, as
   above, so the colors there are the ones that come out yellow, blue and red once inverted. */
.staff { --walk-yellow: var(--star); --walk-blue: var(--help); --walk-red: var(--wrong); }
@media (prefers-color-scheme: dark) {
  .staff { --walk-yellow: #8c4a00; --walk-blue: #177ddc; --walk-red: #e05050; }
}
.staff .walk-glow, .staff .walk-glow * { fill: var(--walk-yellow); stroke: var(--walk-yellow); }
.staff .vf-clef.walk-glow { filter: drop-shadow(0 0 3px var(--walk-yellow)); }
.staff .walk-line { fill: var(--walk-yellow); opacity: 0.4; }
.staff .walk-note { stroke: #1f1b3a; stroke-width: 2; }
.staff .walk-start { fill: var(--walk-yellow); }
.staff .walk-step { fill: var(--walk-blue); }
.staff .walk-ledger { stroke: #000; stroke-width: 1.2; }
.staff .wrong-note, .staff .wrong-note .walk-acc { fill: var(--walk-red); }
.staff .walk-acc { font-family: Bravura; font-size: 36px; }
.staff .walk-hop { transform-box: fill-box; transform-origin: center; animation: walk-hop 0.25s ease-out; }
@keyframes walk-hop { from { transform: scale(1.5); opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) {
  .staff .walk-hop { animation: none; }
}

.feedback { min-height: 3rem; font-size: clamp(2rem, 9vw, 3rem); font-weight: 800; text-align: center; line-height: 1.2; }
.card.correct .feedback { color: var(--correct); }
.card.wrong .feedback { color: var(--wrong); }
.card.shown .feedback { color: var(--primary); }

.start {
  display: none;
  position: absolute;
  inset: 0;
  margin: auto;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  border: none;
  background: var(--primary);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--primary) 45%, transparent);
  animation: breathe 1.6s ease-in-out infinite;
}
.start svg { width: 64px; height: 64px; fill: var(--primary-text); margin-left: 8px; }
.card.syncing .start { opacity: 0.4; } /* back in the app: waiting for what changed elsewhere */
.card.waiting .start { display: grid; place-items: center; }
/* Pause, in the card's corner while a note is being answered. */
.pause-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 3;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--border) 70%, transparent);
}
.pause-btn svg { width: 24px; height: 24px; fill: var(--muted); }
.card.waiting .pause-btn, .card.celebrate .pause-btn { display: none; }
/* In the card's bottom-left corner, near the child's hands (the coin jar has the other). */
.help-btn {
  position: absolute;
  bottom: 10px;
  left: 10px;
  z-index: 3;
  width: 52px;
  height: 52px;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--star) 25%, var(--surface));
  font-size: 1.7rem;
}
/* A note never seen before: a little nudge toward the help. */
.help-btn.nudge { animation: nudge 2.4s infinite ease-in-out; }
@media (prefers-reduced-motion: reduce) { .help-btn.nudge { animation: none; } }
.card.waiting .help-btn, .card.celebrate .help-btn { display: none; }
.card.waiting .staff, .card.waiting .feedback { opacity: 0.25; }
@keyframes breathe { 50% { transform: scale(1.08); } }

/* ---------- microphone ---------- */

.voice-answer { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 16px; }
.mic {
  position: relative;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: none;
  background: var(--border);
  display: grid;
  place-items: center;
}
.mic { touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.mic svg { grid-area: 1 / 1; width: 42px; height: 42px; fill: var(--muted); position: relative; }
.mic .icon-slash { display: none; }
.mic-level {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--primary);
  opacity: 0;
  transition: transform 0.1s linear;
}
/* Listening: names after a tap or while held, or singing. */
.mic[data-mode="on"] { background: var(--primary); }
/* Ready: waiting for the child's tap to say the note; a gentle nudge. */
.mic[data-mode="ready"] { background: var(--surface); box-shadow: inset 0 0 0 4px var(--primary); animation: nudge 2.4s infinite ease-in-out; }
.mic[data-mode="ready"] svg { fill: var(--primary); }
@keyframes nudge { 0%, 70%, 100% { transform: scale(1); } 80% { transform: scale(1.08); } 90% { transform: scale(0.98); } }
@media (prefers-reduced-motion: reduce) { .mic[data-mode="ready"] { animation: none; } }
.mic[data-mode="on"] svg { fill: var(--primary-text); }
.mic[data-mode="on"] .mic-level { opacity: 0.3; }
/* A quick tap on the microphone: a little shake, as it only listens while held. */
.mic.hold-me { animation: shake 0.35s; }
/* While 💡 teaches: gone (its place kept, so nothing moves), as dimmed wasn't clear enough
   for a child; it can't be pressed. */
.mic:disabled { visibility: hidden; }
/* Loading the voice: no microphone yet, but the player's animal turning, which can't be
   tapped, so it doesn't look ready to play. */
.mic-animal { display: none; grid-area: 1 / 1; font-size: 3rem; line-height: 1; }
.mic[data-mode="loading"] { background: transparent; box-shadow: none; pointer-events: none; }
.mic[data-mode="loading"] svg, .mic[data-mode="loading"] .mic-level { display: none; }
.mic[data-mode="loading"] .mic-animal { display: block; animation: turn 1.8s infinite linear; }
.mic .mic-animal svg { display: block; width: 100%; height: auto; position: static; }
@keyframes turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .mic[data-mode="loading"] .mic-animal { animation: pulse 1.2s infinite ease-in-out; } }
/* Off (before ▶ or while paused) looks plain; only a microphone that failed is crossed out. */
.mic[data-mode="error"] .icon-slash { display: block; }
.mic[data-mode="error"] svg { fill: var(--wrong); }
.mic-status { order: 3; flex-basis: 100%; margin: -6px 0 0; text-align: center; font-size: 0.9rem; color: var(--muted); }
.mic[data-mode="error"] ~ .mic-status { color: var(--wrong); }
/* ✓ / ✗ for a grown-up, or ⏭; the 🔒 for the grown-up's question. */
.judge { display: flex; gap: 16px; }
.judge button {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: 3px solid currentColor;
  background: var(--surface);
  font-size: 2.6rem;
  font-weight: 800;
}
.judge button:active { transform: scale(0.94); }
.judge-right { color: var(--correct); }
.judge-wrong { color: var(--wrong); }
.judge .judge-lock { width: 44px; height: 44px; font-size: 1.3rem; border-color: transparent; background: transparent; align-self: center; }
.dialog {
  max-width: min(440px, calc(100vw - 32px));
  border: none;
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  padding: 20px;
}
.dialog::backdrop { background: rgb(0 0 0 / 0.4); }
.dialog h3 { margin: 0 0 8px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* ---------- "How to use" (tutorial.js, guides.js) ---------- */

/* The big button under the players, for the grown-ups. */
.guides-btn {
  align-self: center;
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 8px; padding: 14px 22px;
  border: 2px solid var(--primary); border-radius: var(--radius);
  background: var(--surface); color: var(--primary);
  font: inherit; font-size: 1.05rem; font-weight: 700;
}
.guides-btn span { font-size: 1.4rem; line-height: 1; }
.tutorial-dialog { width: min(440px, calc(100vw - 32px)); max-height: calc(100dvh - 24px); touch-action: pan-y; }
.tut-head { display: flex; align-items: center; gap: 8px; margin: -6px -8px 4px 0; }
.tut-heading { flex: 1; margin: 0 !important; }
.tut-close { margin-left: auto; font-size: 1.1rem; }
.tut-list { display: flex; flex-direction: column; gap: 8px; }
.tut-item {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px; text-align: left;
  border: 2px solid var(--border); border-radius: 14px; background: var(--surface); color: var(--text);
  font: inherit; font-weight: 600;
}
.tut-item-icon { width: 1.6em; font-size: 1.3rem; text-align: center; line-height: 1; }
.tut-item-title { flex: 1; }
.tut-item-go { color: var(--primary); font-size: 0.85rem; }
.tut-guide[hidden], .tut-list[hidden], .tut-video[hidden] { display: none; }
/* The video: the app on a phone, as tall as fits with the words under it. */
.tut-video {
  display: block; margin: 0 auto; height: min(46dvh, 400px); aspect-ratio: 9 / 16; max-width: 100%;
  object-fit: contain; background: var(--bg); border: 3px solid var(--border); border-radius: 18px;
}
.tutorial-dialog .tut-title { margin: 12px 0 6px; }
.tutorial-dialog .tut-text { min-height: 4.4em; margin: 0; line-height: 1.45; }
.tut-dots { display: flex; justify-content: center; gap: 6px; margin: 10px 0; }
.tut-dots span { width: 8px; height: 8px; border-radius: 50%; background: var(--border); }
.tut-dots span.on { background: var(--primary); }
.tut-actions { align-items: center; }
.tut-gap { flex: 1; }

.level-dialog .select { width: 100%; margin: 8px 0 16px; }
/* A new player (setUpPlayer): what they learn as big choices, then where to start. */
.setup-choices { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 4px; }
.setup-choices button { padding: 14px 16px; border: 2px solid var(--border); border-radius: 14px; background: var(--surface); color: var(--text); font: inherit; font-weight: 700; text-align: left; }
.setup-choices button[aria-pressed="true"] { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, var(--surface)); color: var(--primary); }
.setup-start { margin: 16px 0 0; font-size: 1rem; }
.setup-name { display: flex; flex-direction: column; gap: 6px; margin: 14px 0 0; font-weight: 700; }
.setup-name input { font: inherit; font-weight: 400; padding: 10px 12px; border: 2px solid var(--border); border-radius: 12px; background: var(--surface); color: var(--text); }
.setup-name + .setup-start { margin-top: 14px; }
/* The child's turn: "Léo, choose your animal!", big, above the animals. */
.animals-title { margin: 12px 0 0; text-align: center; font-size: 1.5rem; color: var(--primary); text-wrap: balance; }
.grownup-dialog { text-align: center; }
.grownup-question { font-size: 1.6rem; margin: 8px 0 0; }
.grownup-answer { font-size: 2rem; min-height: 1.4em; margin: 0 0 8px; font-variant-numeric: tabular-nums; }
.grownup-keys { display: grid; grid-template-columns: repeat(3, 64px); gap: 8px; justify-content: center; margin-bottom: 16px; }
.grownup-keys button { height: 52px; font-size: 1.4rem; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); color: var(--text); }
.grownup-keys button.primary { background: var(--primary); color: var(--primary-text); border-color: var(--primary); }
.grownup-dialog.wrong { animation: shake 0.4s; }
@keyframes pulse { 50% { opacity: 0.4; } }

/* ---------- piano keyboard ---------- */

/* Keys are drawn by piano.js: as many as fit at --min-key wide or more. */
.keyboard {
  --min-key: 36px;
  position: relative;
  display: flex;
  height: clamp(110px, 20dvh, 180px);
  gap: 4px;
}
.key { border: none; padding: 0; transition: background 0.15s ease, transform 0.05s ease; }
.key.white {
  position: relative;
  flex: 1;
  min-width: 0;
  background: var(--key-white);
  border: 2px solid var(--border);
  border-radius: 0 0 12px 12px;
}
.key.black {
  position: absolute; /* width and left set by piano.js */
  top: 0;
  height: 60%;
  background: var(--key-black);
  border-radius: 0 0 8px 8px;
  z-index: 1;
}
/* Middle C, to find one's way as on a real piano. */
.key.middle-c::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 12%;
  width: 10px;
  height: 10px;
  margin-left: -5px;
  border-radius: 50%;
  background: var(--primary);
}
.key:active { transform: translateY(2px); }
.key.white:active { background: var(--border); }
.key.hint { background: var(--correct) !important; border-color: var(--correct); }

/* ---------- name buttons (letters / solfège) ---------- */

/* Laid out like the piano: 7 natural names, with sharps/flats between them above. Each name
   spans two of 14 columns, so the in-between ones straddle their neighbors. */
.names {
  display: grid;
  grid-template-columns: repeat(14, minmax(0, 1fr));
  grid-auto-rows: clamp(46px, 8dvh, 60px);
  gap: 6px 4px;
}
.names button {
  min-width: 0;
  padding: 0;
  border: 2px solid var(--border);
  border-radius: 14px;
  background: var(--key-white);
  color: var(--key-black);
  font-size: clamp(1rem, 4.6vw, 1.4rem);
  font-weight: 800;
  transition: background 0.15s ease, transform 0.05s ease;
}
.names button.acc {
  margin: 0 3px;
  background: var(--key-black);
  border-color: var(--key-black);
  color: var(--key-white);
  font-size: clamp(0.8rem, 3.5vw, 1.1rem);
}
.names button:active { transform: translateY(2px); }
.names button.hint { background: var(--correct); border-color: var(--correct); color: #fff; }

/* Landscape, the microphone at the right: the grown-up's buttons at the left of the strip,
   ✓ and ✗ furthest from it, so a child's quick tap near the microphone can't answer for them. */
@media (orientation: landscape) {
  .watch-strip { flex-direction: row-reverse; padding: 6px 12px 6px 6px; }
  .watch-question { text-align: right; }
}

/* Landscape: the microphone at the right of the card, under the thumb; a full-width
   keyboard (or name buttons) underneath. Short screens (phones on their side) below. */
@media (orientation: landscape) and (min-height: 561px) {
  #screen-play.active:has(#voice-answer:not([hidden])) { grid-template-columns: 1fr auto; grid-template-rows: auto 1fr auto; }
  #screen-play:has(#voice-answer:not([hidden])) > :not(.card, #voice-answer) { grid-column: 1 / -1; }
  #voice-answer { grid-area: 2 / 2; flex-direction: column; flex-wrap: nowrap; }
  #voice-answer .judge { flex-direction: column; align-items: center; }
  #voice-answer .mic-status { max-width: 9em; }
}

/* Short landscape screens (phones on their side): the top bar in one row with the card's
   buttons, the microphone at the right of the card, and a full-width keyboard (or name
   buttons) underneath. */
@media (orientation: landscape) and (max-height: 560px) {
  #screen-play.active {
    grid-template-columns: auto 1fr auto auto auto auto auto auto;
    grid-template-rows: auto 1fr auto;
    gap: 8px;
  }
  .topbar { display: contents; }
  #btn-player { grid-area: 1 / 1; }
  #stars { grid-area: 1 / 2; font-size: clamp(1rem, 3vw, 1.6rem); }
  #screen-play:has(#voice-answer:not([hidden])) .card { grid-area: 2 / 1 / 3 / 6; }
  #voice-answer { grid-area: 2 / 6 / 3 / -1; flex-direction: column; flex-wrap: nowrap; gap: 8px; }
  /* No room for the status line beside the card: it shows just below the top bar, over it. */
  .mic-status { position: fixed; top: 64px; left: 16px; right: 16px; margin: 0; z-index: 2; }
  #btn-activity { grid-area: 1 / 4; }
  #btn-mode { grid-area: 1 / 5; }
  #btn-practice { grid-area: 1 / 6; }
  #btn-lock { grid-area: 1 / 7; }
  #btn-parents { grid-area: 1 / 8; }
  .card { grid-area: 2 / 1 / 3 / -1; flex-direction: row; gap: 12px; }
  /* The card is wide and short: the name goes beside the staff, which gets the full height. */
  .card .staff { flex: 2; height: 100%; }
  .card .feedback { flex: 1; }
  .keyboard, .names { grid-area: 3 / 1 / 4 / -1; }
  .mic { width: 64px; height: 64px; }
  .judge button { width: 48px; height: 48px; }
  .mic svg { width: 32px; height: 32px; }
  .mic-animal { font-size: 2.2rem; }
  .judge { gap: 8px; }
  .judge button { font-size: 1.5rem; border-width: 2px; }
  .judge .judge-lock { width: 36px; height: 36px; font-size: 1.1rem; }
  .keyboard { height: clamp(80px, 27dvh, 130px); }
  .names { grid-auto-rows: clamp(34px, 11dvh, 48px); gap: 4px; }
  /* Rhythm: the pad beside the card, at the right, under the thumb. */
  #screen-play:has(#rhythm-answer:not([hidden])) .card { grid-area: 2 / 1 / 4 / 6; }
  .rhythm-answer { grid-area: 2 / 6 / 4 / -1; flex-direction: column; gap: 10px; }
  .pad { width: min(40dvh, 160px); height: min(40dvh, 160px); }
  /* The line takes the card's whole width: the tempo goes under it rather than beside it
     (the count-in is on the pad). */
  #screen-play:has(#rhythm-answer:not([hidden])) .card .feedback { position: absolute; top: 6px; left: 0; right: 0; min-height: 0; pointer-events: none; }
  #screen-play:has(#rhythm-answer:not([hidden])) .card .tempo { position: absolute; bottom: 8px; left: 0; right: 0; text-align: center; }
  #screen-play:has(#rhythm-answer:not([hidden])) .card .staff { flex: 1; }
}

/* Tablets: bigger controls for small fingers on a big screen. */
@media (min-width: 700px) and (min-height: 700px) {
  #screen-play.active { gap: 18px; }
  .player-btn { width: 56px; height: 56px; font-size: 2rem; }
  .map-btn { width: 52px; height: 56px; font-size: 2.2rem; }
  .parents-btn { width: 52px; height: 52px; }
  .stars { font-size: 2.2rem; }
  .mic, .judge button { width: 112px; height: 112px; }
  .mic svg { width: 56px; height: 56px; }
  .mic-animal { font-size: 4rem; }
  .judge button { font-size: 3.4rem; }
  .keyboard { --min-key: 40px; height: clamp(150px, 22dvh, 260px); gap: 6px; }
  .names { grid-auto-rows: clamp(60px, 7.5dvh, 88px); gap: 10px 8px; }
  .names button { font-size: 2rem; border-radius: 18px; }
  .names button.acc { font-size: 1.5rem; }
  .feedback { font-size: 3.6rem; min-height: 4.4rem; }
}

/* ---------- settings (parents) ---------- */

.settings-top { display: grid; grid-template-columns: 48px 1fr 48px; align-items: center; }
.settings-top h2 { margin: 0; text-align: center; }
.icon-btn { border: none; background: transparent; font-size: 1.5rem; width: 48px; height: 48px; border-radius: 50%; }
.icon-btn:active { background: var(--border); }
/* A new version, ready, on "Who's playing?" (update.js): its Restart button. */
.update-banner { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; padding: 8px 8px 8px 14px; border: 2px solid var(--primary); border-radius: 14px; background: color-mix(in srgb, var(--primary) 9%, var(--surface)); font-weight: 600; }
.update-banner[hidden] { display: none; }
.update-banner span { flex: 1; }
.update-banner button { min-height: 40px; padding: 0 14px; border: 0; border-radius: 10px; background: var(--primary); color: var(--primary-text); font-weight: 700; }
/* ⚙️ on "Who's playing?": the same gear as in the game's top bar (.parents-btn). */
.gear-btn { padding: 10px; opacity: 0.6; }
.gear-btn svg { width: 100%; height: 100%; fill: var(--muted); display: block; }

.settings { display: flex; flex-direction: column; gap: 14px; user-select: text; -webkit-user-select: text; }
.settings fieldset { border: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.settings legend { font-weight: 700; margin-bottom: 8px; padding: 0; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border: 2px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-weight: 600;
  cursor: pointer;
}
.chip:has(input:checked) { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 15%, var(--surface)); }
.chip:has(input:disabled) { opacity: 0.5; }
.select {
  font: inherit;
  font-weight: 600;
  color: var(--text);
  min-width: 200px;
  padding: 10px 40px 10px 14px;
  border: 2px solid var(--primary);
  border-radius: 14px;
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%235b4bdb' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") no-repeat right 10px center / 24px;
  appearance: none;
  -webkit-appearance: none;
}
.chip input { accent-color: var(--primary); width: 18px; height: 18px; margin: 0; }
.help { line-height: 1.45; }
.muted { color: var(--muted); }

/* ---------- recordings (parents) ---------- */

.settings fieldset.recordings-field { display: block; }
.backup-settings { margin-top: 24px; }
.backup-settings fieldset { display: block; }
.backup-settings .player-actions { margin-bottom: 8px; }
#backup-status:empty, #account-status:empty { display: none; }
.account-devices { list-style: none; padding: 0; margin: 0 0 12px; display: grid; gap: 8px; }
.account-devices li { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sign-in-buttons { display: contents; }
.cloud { margin-bottom: 12px; }
.cloud-label { font-weight: 700; margin: 16px 0 8px; }
.cloud-code-shown { font-size: 1.4em; font-weight: 700; padding: 10px 14px; border: 2px dashed var(--primary); border-radius: 14px; display: inline-block; user-select: all; -webkit-user-select: all; }
.cloud-join { display: grid; gap: 8px; margin-bottom: 12px; }
.cloud-code { font: inherit; color: var(--text); background: var(--surface); padding: 10px 14px; border: 2px solid var(--primary); border-radius: 14px; max-width: 22em; }

/* Saving online, in the family settings: a card with where it stands, the rest folded. */
.online { gap: 12px; }
.online-title { margin: 0; }
.online-card { display: flex; flex-direction: column; gap: 10px; padding: 14px; border: 2px solid var(--border); border-radius: 18px; background: var(--surface); }
.online-status { display: flex; align-items: center; gap: 12px; }
.online-icon { flex: none; width: 40px; height: 40px; padding: 9px; box-sizing: border-box; border-radius: 50%; background: var(--bg); fill: none; stroke: var(--muted); stroke-width: 2; stroke-linejoin: round; }
.online.on .online-icon { stroke: var(--correct); background: color-mix(in srgb, var(--correct) 14%, var(--surface)); }
.online-text { flex: 1; min-width: 0; }
.online-state { margin: 0; font-weight: 700; }
.online-text .small { margin: 2px 0 0; overflow-wrap: anywhere; }
.online-card > .player-actions:empty, #online-note:empty { display: none; }
.cloud-row { display: flex; gap: 8px; }
.online-card .cloud-join { grid-template-columns: minmax(0, 1fr); margin: 0; }
/* The backup and privacy folds follow on from saving online's, under one line. */
#account:not([hidden]) + .folds { border-top: none; }
.cloud-row .cloud-code { flex: 1; min-width: 0; max-width: none; }
/* Folds (sharing, the family code, the account, the backup, privacy): one line each, closed. */
.folds { gap: 0; border-top: 2px solid var(--border); }
.fold { border-bottom: 2px solid var(--border); }
.fold > summary { display: flex; align-items: center; gap: 10px; min-height: 52px; font-weight: 700; cursor: pointer; }
.fold > summary::before { content: '▸'; color: var(--muted); }
.fold[open] > summary::before { content: '▾'; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary { list-style: none; }
.fold-summary { margin-left: auto; font-weight: 400; color: var(--muted); font-size: 0.9rem; text-align: right; }
.fold[open] { padding-bottom: 14px; }
.fold[open] > * + * { margin-top: 8px; }
.fold .account-devices { margin: 0; }
.fold .cloud-label { margin: 6px 0 0; }
.fold .cloud-code-shown { margin-bottom: 0; }
.fold-end { margin-top: 8px; }
.small { font-size: 0.9rem; margin: 0 0 4px; }
.rec-status { min-height: 1.3em; margin: 6px 0 0; font-weight: 600; color: var(--primary); }
.progress-fold { border-top: 1px solid var(--border); padding: 6px 0; }
.progress-fold > summary { font-weight: 700; cursor: pointer; padding: 6px 0; }
.rec-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 8px; }
.rec-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 14px;
  border: 2px solid var(--border);
  border-radius: 16px;
  background: var(--surface);
}
.rec-row.has { border-color: var(--correct); }
.rec-row.recording { border-color: var(--wrong); }
.rec-name { flex: 1; font-weight: 800; font-size: 1.2rem; min-width: 0; }
.rec-action {
  height: 48px;
  flex: none;
  border: 2px solid var(--border);
  border-radius: 12px;
  background: var(--bg);
  font-size: 1rem;
  font-weight: 700;
  padding: 0 12px;
}
.rec-action:disabled { opacity: 0.3; }
.rec-action:not(:disabled):active { transform: scale(0.96); }
.rec-btn, .play-btn { width: 52px; padding: 0; font-size: 1.4rem; }
.rec-btn { color: var(--wrong); }
.play-btn { color: var(--correct); }
.del-btn { width: 48px; padding: 0; margin-left: 6px; }
.check-heard { display: block; font-weight: 600; font-size: 0.85rem; color: var(--muted); }
.yes-btn, .no-btn { width: 52px; padding: 0; font-size: 1.4rem; }
.yes-btn { color: var(--correct); }
.no-btn { color: var(--wrong); }
.tab-badge {
  display: inline-block;
  min-width: 1.4em;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--wrong);
  color: #fff;
  font-size: 0.8rem;
  line-height: 1.4em;
}
.tab-badge[hidden] { display: none; }
.rec-row.recording .rec-btn { background: var(--wrong); border-color: var(--wrong); color: #fff; animation: pulse 1s infinite; }

/* ---------- players ---------- */

.player-btn {
  flex: none;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 0 0 2px var(--border);
  font-size: 1.6rem;
  line-height: 1;
  padding: 0;
}

.picker.active { display: flex; flex-direction: column; gap: 16px; min-height: calc(100dvh - 28px); }
.picker-top { height: 48px; display: flex; justify-content: space-between; align-items: center; }
/* The den's way to the journey: big, for small fingers. */
.den-journey { width: 56px; height: 56px; font-size: 2rem; line-height: 1; border: 3px solid var(--border); border-radius: 18px; background: var(--surface); cursor: pointer; box-shadow: 0 3px 0 var(--border); }
.den-journey:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--border); }
.picker-actions { margin-left: auto; display: flex; align-items: center; gap: 2px; }
/* The language menus ("Who's playing?", the grown-up question): each language named in itself. */
.lang-menu { font: inherit; font-size: 0.95rem; color: var(--text); background: var(--surface); border: 2px solid var(--border); border-radius: 12px; padding: 6px 8px; max-width: 9.5em; }
.grownup-exit { width: 64px; height: 64px; margin: 0 auto 14px; }
.grownup-exit svg { width: 32px; height: 32px; }
.keep-dialog { max-width: min(440px, calc(100vw - 32px)); }
.keep-list { display: grid; gap: 8px; margin: 12px 0; }
.keep-list label { display: flex; align-items: center; gap: 10px; font-weight: 600; }
.keep-list .animal-pic { width: 44px; height: 44px; }
.animal-grid {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 16px;
  align-content: center;
  justify-items: center;
}
.animal-tile {
  width: 110px;
  height: 110px;
  border-radius: 28px;
  border: 4px solid var(--border);
  background: var(--surface);
  font-size: 4rem;
  line-height: 1;
  transition: transform 0.08s ease;
}
.animal-tile:active { transform: scale(0.94); }
.animal-tile.selected { border-color: var(--primary); }
.animal-tile.add { color: var(--primary); font-weight: 700; border-style: dashed; }
/* First launch: a single big ＋ in the middle. */
.animal-grid:has(> .animal-tile:only-child) { grid-template-columns: 1fr; }
.animal-tile.add:only-child { width: 160px; height: 160px; font-size: 5rem; }

/* A player's settings: the animal (a tap changes player) and the name beside it. */
.player-field { display: flex; align-items: center; gap: 16px; padding: 12px; border: 2px solid var(--border); border-radius: 20px; background: var(--surface); }
/* Saving online at a glance, above the player: a tap opens the family settings. */
.sync-status { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border: 2px solid var(--border); border-radius: 22px; background: var(--surface); color: var(--muted); font-weight: 700; text-align: left; }
.sync-status svg { flex: none; width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
.sync-status #sync-status-text { flex: 1; min-width: 0; }
.sync-status.on { color: var(--correct); border-color: color-mix(in srgb, var(--correct) 40%, var(--border)); }
.sync-status[hidden] { display: none; }
/* Deleting a player: at the end of the Settings tab, set apart. */
.danger-zone { margin-top: 28px; padding: 16px; gap: 8px; border: 2px solid color-mix(in srgb, var(--wrong) 35%, var(--border)); border-radius: 18px; }
.danger-zone h3 { margin: 0; color: var(--wrong); }
.danger-zone .btn-small { align-self: flex-start; }
/* The backup, folded under saving online. */
.backup-fold > summary, .help-fold > summary { font-weight: 700; cursor: pointer; padding: 6px 0; }
.settings-animal {
  flex: none;
  width: 88px;
  height: 88px;
  border-radius: 24px;
  border: 3px solid var(--primary);
  background: var(--surface);
  font-size: 3.2rem;
  line-height: 1;
}
.player-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.btn-small {
  border: 2px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  padding: 8px 12px;
  font-weight: 600;
}
.btn-small.danger { color: var(--wrong); }
.btn-small.primary { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }

/* Settings tabs */

.tabs { display: flex; gap: 4px; border-bottom: 2px solid var(--border); }
.tabs button {
  flex: 1;
  min-height: 48px;
  border: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  background: transparent;
  font-weight: 700;
  color: var(--muted);
}
.tabs button[aria-selected="true"] { color: var(--primary); border-bottom-color: var(--primary); }

/* Progress tab */

.progress-view { display: flex; flex-direction: column; gap: 16px; user-select: text; -webkit-user-select: text; }
.progress-clef h3 { margin: 0 0 2px; }
.swatch { width: 14px; height: 14px; border-radius: 3px; }
.swatch.known, .bar.known { background: var(--prog-known); }
.swatch.learning, .bar.learning { background: var(--prog-learning); }
.swatch.hard, .bar.hard {
  background: repeating-linear-gradient(135deg, var(--prog-hard) 0 4px,
    color-mix(in srgb, var(--prog-hard) 60%, var(--surface)) 4px 6px);
}
.swatch.unseen, .bar.unseen { background: transparent; border: 2px dashed var(--muted); }
.chart-box { --chart-h: 180px; --label-h: 44px; --axis-w: 34px; position: relative; margin-top: 8px; }
.chart-scroll { overflow-x: auto; margin-left: var(--axis-w); }
.chart {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 2px;
  height: var(--chart-h);
  min-width: min-content;
}
.chart::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--label-h);
  border-top: 1px solid var(--muted);
}
.gridline {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px dashed var(--border);
  pointer-events: none;
  z-index: 1;
}
.gridline-label { position: absolute; left: 0; top: -0.55em; font-size: 0.7rem; color: var(--muted); line-height: 1; }
.bar-col {
  position: relative;
  flex: 0 0 28px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  border: none;
  padding: 0;
  background: transparent;
  border-radius: 6px 6px 0 0;
}
.bar-col.ledger { background: color-mix(in srgb, var(--border) 45%, transparent); }
.bar-col:focus-visible { outline: 2px solid var(--primary); }
.bar { width: 20px; border-radius: 4px 4px 0 0; min-height: 2px; }
.bar.unseen { border-bottom: none; }
.bar-label {
  height: var(--label-h);
  display: grid;
  place-items: center;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
/* Under a note's name: 🎙 if its name is recorded, else as much room. */
.chart-box:not(.rhythm-chart) .bar-label { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 3px; padding-top: 4px; }
.rec-mark-none { height: 16px; }
.bar-info { min-height: 1.3em; margin-top: 6px; }
.chart-sub { margin: 10px 0 0; color: var(--muted); font-weight: 600; }
.pace-line { margin: 0; }
.progress-details { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.table-scroll { overflow-x: auto; max-width: 100%; }
.details-table { border-collapse: collapse; font-size: 0.85rem; }
.details-table th, .details-table td { padding: 4px 8px; border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap; }
.details-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.params {
  margin: 0;
  max-width: 100%;
  overflow-x: auto;
  font-size: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px;
}

/* ---------- the journey (journey.js): the animal's walk, in 3D ---------- */

.map {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  border: 4px solid var(--border);
  border-radius: 28px;
  background: color-mix(in srgb, var(--correct) 10%, var(--surface));
}
.map-exit {
  flex: none;
  width: 84px;
  height: 84px;
  margin: 0 auto;
  display: grid;
  place-items: center;
  border: 3px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
}
.map-exit svg { width: 40px; height: 40px; fill: var(--text); }
.map-exit:active { transform: scale(0.94); }

/* Progress tab: skill picker and path line */

/* The skill shown: joined buttons (.seg) on one line, icons only, the chosen one's name under. */
.skill-choice { display: flex; flex-direction: column; gap: 4px; }
.skill-picker span { padding: 0; font-size: 1.1rem; white-space: nowrap; }
.skill-name { margin: 0; text-align: center; font-size: 0.9rem; font-weight: 600; color: var(--muted); }
/* The current level, drawn: name and places reached, the path bar, then its characters'
   known / learning / hard as a thin stacked bar with their numbers. */
.level-card { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; border: 2px solid var(--border); border-radius: 16px; background: var(--surface); }
.level-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.level-card .level-name { font-weight: 800; color: var(--primary); }
.level-count { font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.path-bar { position: relative; height: 12px; border-radius: 6px; overflow: hidden; background: color-mix(in srgb, var(--primary) 14%, transparent); }
.path-bar b { position: absolute; inset: 0 auto 0 0; border-radius: 6px; background: var(--primary); }
.path-bar i { position: absolute; top: 0; bottom: 0; background: color-mix(in srgb, var(--primary) 40%, transparent); }
.mix { display: flex; flex-direction: column; gap: 6px; }
.mix-bar { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: color-mix(in srgb, var(--muted) 18%, transparent); }
.mix-bar .known, .mix-numbers .known::before { background: var(--prog-known); }
.mix-bar .learning, .mix-numbers .learning::before { background: var(--prog-learning); }
.mix-numbers .hard::before { background: var(--prog-hard); }
.mix-bar .hard { background: repeating-linear-gradient(135deg, var(--prog-hard) 0 4px, color-mix(in srgb, var(--prog-hard) 60%, var(--surface)) 4px 6px); }
.mix-numbers { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 0.85rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.mix-numbers span::before { content: ""; display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 5px; }
.path-line { margin: 0; font-weight: 600; }

.path-progress { margin: 4px 0 0; width: 100%; font-weight: 600; color: var(--primary); }
.level-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: 100%; margin: 4px 0 0; }
.level-line .path-progress { width: auto; margin: 0; }

/* Settings: one line per setting, more behind ⓘ (as in Zilu) */
.info-btn { border: none; background: transparent; padding: 0 4px; font-size: 1.1rem; color: var(--primary); }

/* A player's Settings tab: sections, each a heading and its controls. */
#settings-form { gap: 24px; }
.set-section { display: flex; flex-direction: column; gap: 10px; }
.set-section h3 { margin: 0; font-size: 1.05rem; display: flex; align-items: center; gap: 2px; }
.set-section > .small { margin: -6px 0 0; }
.set-section .select { width: 100%; }
.level-line { justify-content: space-between; padding: 8px 8px 8px 14px; border: 2px solid var(--border); border-radius: 14px; background: var(--surface); }
.set-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* What to practice: the label, the two choices across, then what the choice means. */
.practice-choice { display: flex; flex-direction: column; gap: 8px; }
.practice-summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0; color: var(--muted); font-size: 0.92rem; }
.practice-actions { flex-wrap: wrap; }
.practice-actions #btn-practice-path { margin-right: auto; }
.set-row.set-column { flex-direction: column; align-items: stretch; }
.set-label { flex: 1; font-weight: 600; }
/* Segmented buttons, from radio inputs: one choice of a few, on one line. */
.seg { display: inline-flex; border: 2px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surface); }
.seg label { position: relative; display: flex; }
.seg label + label { border-left: 2px solid var(--border); }
.seg input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.seg span { min-width: 44px; min-height: 38px; padding: 0 12px; display: grid; place-items: center; font-weight: 700; cursor: pointer; }
.seg input:checked + span { background: var(--primary); color: var(--primary-text); }
.seg input:focus-visible + span { outline: 3px solid var(--primary); outline-offset: -3px; }
.seg-wide { display: flex; }
.seg-wide label { flex: 1; }
.seg-wide span { flex: 1; }
/* Sliders: the value beside the name, and what each end means under. */
.slider { display: flex; flex-direction: column; gap: 6px; }
.slider-top { display: flex; align-items: center; gap: 2px; font-weight: 600; }
.slider-top output { margin-left: auto; font-weight: 700; color: var(--primary); }
.slider input[type=range] { width: 100%; margin: 0; accent-color: var(--primary); height: 28px; }
.slider-ends { display: flex; justify-content: space-between; gap: 12px; font-size: 0.8rem; color: var(--muted); }
.slider-ends span:last-child { text-align: right; }
/* Rewards: one line per way of answering, ×1 ×2 ×3. */
.reward-list { border: 2px solid var(--border); border-radius: 14px; background: var(--surface); }
.reward-row { display: flex; align-items: center; gap: 10px; padding: 6px 8px 6px 14px; }
.reward-row + .reward-row { border-top: 1px solid var(--border); }
.reward-row[hidden] { display: none; }
.reward-label { flex: 1; font-weight: 600; }
.reward-row .seg span { min-height: 34px; }
/* Activities: each way to play, its coins, and its own settings under it. */
.activity + .activity { border-top: 1px solid var(--border); }
.coins-label { font-size: 0.8rem; color: var(--muted); }
.activity-setting { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 2px 8px 8px 28px; font-size: 0.95rem; }
.activity-setting.activity-column { flex-direction: column; align-items: stretch; }
.activity-setting.switch-row { min-height: 0; }
#voice-advanced[hidden] { display: none; }
/* Each activity, folded: its name and what it is set to, then its settings when unfolded. */
.reward-list .fold.activity { border-bottom: none; }
.reward-list .fold.activity > summary { padding: 0 12px; min-height: 50px; }
.reward-list .fold.activity[open] { padding-bottom: 8px; }
.reward-list .fold.activity[open] > summary { color: var(--primary); }
.activity-summary { font-size: 0.85rem; }
/* Two or three short choices beside their name, on one line. */
.seg-compact span { min-width: 0; padding: 0 9px; min-height: 34px; font-size: 0.9rem; }
/* On/off options, as switches. */
.switch-row { display: flex; align-items: center; gap: 10px; min-height: 44px; font-weight: 600; }
.switch-row > span { flex: 1; }
.switch-row input { width: 22px; height: 22px; margin: 0; accent-color: var(--primary); }
.set-fold[open] > .set-row { margin-top: 4px; }
.set-fold { border-top: 2px solid var(--border); }
.sub-setting { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: 100%; margin-top: 6px; }
.sub-label { width: 100%; font-weight: 600; }
/* The microphone's finer settings, folded; all of them hidden while voice is off. */
.advanced-fold { width: 100%; margin-top: 10px; }
.advanced-fold > summary { font-weight: 600; cursor: pointer; color: var(--primary); }
.voice-field:not(:has([name=voice]:checked)) :is(.sub-setting, .advanced-fold) { display: none; }
#info-text p { margin: 0 0 8px; }

/* The app's version, at the bottom of the settings, for the grown-ups. */
.app-version { text-align: center; margin-top: 24px; }

/* "Who's playing?": the player's animal, with their name under it for the grown-ups. */
.player-pic { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.player-pic .animal-pic { width: 1.05em; }
.player-name { font-size: 0.85rem; font-weight: 700; line-height: 1.1; max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-name:empty { display: none; }
.player-name-field { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; font-weight: 600; }
.player-name-field input { flex: 1; min-width: 0; font: inherit; padding: 8px 12px; border: 2px solid var(--border); border-radius: 12px; background: var(--surface); color: var(--text); }

/* The den (den.js): the child's animal on a round rug, then a row of animals and one per kind
   of prop, each swiping sideways (its far end fades, to show there's more). Theirs come first,
   in color; the others are greyed, with a padlock that wiggles when tapped. While the jar is
   full, the next one of each row has a glowing gold border instead of the padlock.
   The den is one screen tall: the animal stays in sight while the shelves scroll under it
   (beside it, on a screen held sideways), so the child sees each thing put on. */
.den.active {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  gap: 14px;
  height: calc(100dvh - max(12px, env(safe-area-inset-top)) - max(16px, env(safe-area-inset-bottom)));
}
.den-stage {
  align-self: center;
  justify-self: center;
  width: min(250px, 58vw, 30dvh);
  padding: 24px 12px 6px;
  border-radius: 50% 50% 28px 28px / 40% 40% 28px 28px;
  background: radial-gradient(ellipse at 50% 85%, var(--correct-bg) 0 45%, transparent 70%);
  cursor: pointer;
}
.den-stage:focus-visible { outline: 3px solid var(--primary); outline-offset: 4px; }
.den-shelves {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  padding-bottom: 24px;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); /* more below */
  mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
}
.den-shelves::-webkit-scrollbar { display: none; }
/* Held sideways: the animal on the left, the shelves on the right. */
@media (orientation: landscape) {
  .den.active { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); grid-template-rows: auto minmax(0, 1fr); column-gap: 20px; }
  .den .picker-top { grid-column: 1 / -1; }
  .den-stage { width: min(320px, 36vw, 62dvh); }
}
.den-shelf { display: flex; align-items: center; gap: 8px; }
.den-shelf-icon { font-size: 1.6rem; width: 32px; text-align: center; flex: none; }
.den-tiles {
  flex: 1;
  min-width: 0;
  display: flex;
  gap: 10px;
  padding: 8px 8px 10px; /* room for the gold glow */
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  position: relative; /* the tiles' offsetLeft, to bring one into view */
  -webkit-mask-image: linear-gradient(to right, #000 82%, transparent);
  mask-image: linear-gradient(to right, #000 82%, transparent);
}
.den-tiles::-webkit-scrollbar { display: none; }
.den-tile {
  position: relative;
  flex: none;
  width: 84px;
  height: 84px;
  scroll-snap-align: start;
  padding: 6px;
  border-radius: 20px;
  border: 3px solid var(--border);
  background: var(--surface);
  transition: transform 0.08s ease;
}
.den-tile:active { transform: scale(0.94); }
.den-tile.selected { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary) inset; }
.den-pic { display: block; width: 100%; height: 100%; overflow: hidden; border-radius: 14px; }
.den-pic svg { display: block; width: 100%; height: 100%; }
.den-tile.locked .den-pic { filter: grayscale(1); opacity: 0.4; }
.den-tile.winnable .den-pic { filter: grayscale(1); opacity: 0.55; }
.den-tile.winnable { border: 3px dashed var(--star); animation: den-glow 1.6s ease-in-out infinite; }
@keyframes den-glow { 50% { box-shadow: 0 0 0 4px rgba(245, 193, 59, 0.35), 0 0 14px rgba(245, 193, 59, 0.6); } }
.den-tile.won { animation: den-won 0.6s cubic-bezier(.3, 1.6, .6, 1); }
@keyframes den-won { from { transform: scale(0.4); } }
.den-lock { position: absolute; right: 4px; bottom: 2px; font-size: 1.15rem; line-height: 1; }
.den-tile.nope { animation: shake 0.35s; }
.den-tile.nope .den-lock { animation: bounce 0.18s ease 2 alternate; }
@media (prefers-reduced-motion: reduce) { .den-tile.nope, .den-tile.nope .den-lock, .den-tile.winnable, .den-tile.won { animation: none; } }

/* ---------- the coin jar (jar.js; the same in Tunilo and Zilu) ---------- */
/* The jar sits in the card's bottom-right corner, out of the way of any way of answering. */
.jar-dock { position: absolute; right: 10px; bottom: 10px; width: 52px; transform-origin: 50% 100%; pointer-events: none; z-index: 1; }
.jar-dock.bump { animation: jar-bump 0.25s ease-out; }
@keyframes jar-bump { 50% { transform: scale(1.08, 0.94); } }
.jar-svg { display: block; width: 100%; height: auto; overflow: visible; }
.falling-coin { position: fixed; z-index: 11; pointer-events: none; line-height: 0; }
.falling-coin svg { display: block; overflow: visible; }

/* The gift: the full jar in the middle, opened with a tap (then the den opens). */
.gift { position: fixed; inset: 0; z-index: 9; background: rgba(31, 27, 58, 0.55); display: grid; place-items: center; animation: gift-in 0.3s; }
@keyframes gift-in { from { background: transparent; } }
.gift-jar { width: min(190px, 50vw); position: relative; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.gift-jar:focus-visible { outline: 3px solid var(--star); outline-offset: 6px; border-radius: 16px; }
.gift.ready .gift-jar { animation: gift-wiggle 1.1s ease-in-out infinite; }
@keyframes gift-wiggle {
  0%, 60%, 100% { transform: rotate(0); }
  70% { transform: rotate(-6deg) scale(1.04); }
  80% { transform: rotate(6deg) scale(1.04); }
  90% { transform: rotate(-3deg); }
}
.gift .jar-lid { transition: transform 0.6s ease-out, opacity 0.6s 0.2s; }
.gift.open .jar-lid { transform: translate(28px, -70px) rotate(35deg); opacity: 0; }
.gift.open .gift-jar { transform: scale(0.8); opacity: 0.35; transition: transform 0.6s, opacity 0.6s; }
@media (prefers-reduced-motion: reduce) {
  .gift, .gift.ready .gift-jar, .jar-dock.bump { animation: none !important; }
}

/* ---------- the answer mode and practice (top bar, map, 🎯 pane) ---------- */
/* The mode button shows the mode's badge, as the animal wears it on the map (C, Do, ♪, 🎹). */
.mode-btn { display: grid; place-items: center; }
.mode-badge {
  min-width: 34px;
  height: 34px;
  padding: 0 7px;
  display: grid;
  place-items: center;
  border-radius: 17px;
  background: var(--primary);
  color: var(--primary-text);
  font-size: 1.05rem;
  font-weight: 800;
  line-height: 1;
}
.map-actions { display: flex; align-items: center; justify-content: center; gap: 24px; }
.map-actions .map-exit { margin: 0; }
.map-side { width: 64px; }
.map-actions .map-side.map-exit { height: 64px; font-size: 1.8rem; }
.practice-dialog fieldset { border: none; padding: 0; margin: 0 0 14px; display: flex; flex-wrap: wrap; gap: 8px; }
.practice-dialog legend { font-weight: 700; margin-bottom: 8px; padding: 0; }
.icon-actions { justify-content: space-between; }
.icon-action {
  width: 56px;
  height: 56px;
  border: 2px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  font-size: 1.6rem;
  line-height: 1;
}
.icon-action.primary { background: var(--primary); border-color: var(--primary); color: var(--primary-text); font-weight: 800; }

/* ---------- rhythm (rhythmgame.js) ---------- */

/* The line fills the card's width; the notes are VexFlow's, the rest is drawn by staff.js
   (drawLine, markParts, cursorAt, markTaps). On dark backgrounds the whole drawing is inverted
   (as for notes), so the colors here are picked to come out right both ways. */
.staff.rhythm-staff { max-width: 900px; justify-content: center; }
.staff.rhythm-staff svg { height: auto; max-height: 100%; margin: 0 auto; }
.staff .bar-line { stroke: #000; stroke-width: 1.2; }
.staff .bar-end { fill: #000; }
.staff .meter { font: 700 22px Georgia, "Times New Roman", serif; fill: #000; stroke: none; }
.staff .say { font-family: system-ui, sans-serif; font-weight: 600; /* its size: staff.js */ fill: var(--walk-blue); stroke: none; opacity: 0; transition: opacity 0.3s ease; }
.card.say .staff .say { opacity: 1; }
.staff .say.now { fill: var(--walk-yellow); font-weight: 800; }
.staff .rhythm-note.now, .staff .rhythm-note.now * { fill: var(--walk-blue); stroke: var(--walk-blue); }
.staff .rhythm-note.wrong-part, .staff .rhythm-note.wrong-part * { fill: var(--walk-red); stroke: var(--walk-red); }
.staff .rhythm-cursor { stroke: var(--walk-blue); stroke-width: 3; stroke-linecap: round; opacity: 0.7; }
.staff .tap-note { fill: #b9b5cf; }
.staff .tap-press { fill: var(--walk-blue); }
.staff .tap-fault { fill: var(--walk-red); }
.staff .tap-rest.tap-fault { fill: none; stroke: var(--walk-red); stroke-width: 2; stroke-dasharray: 4 3; }

.tempo { margin: 0; font-size: 0.95rem; color: var(--muted); font-weight: 600; }

.rhythm-answer { display: flex; justify-content: center; align-items: center; gap: 16px; padding-bottom: 4px; }
/* The pad: big, round, for small hands; no text selection or callout on a long press. */
.pad {
  position: relative;
  width: min(42vw, 180px);
  height: min(42vw, 180px);
  border-radius: 50%;
  border: 6px solid var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  display: grid;
  place-items: center;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  transition: transform 0.05s ease, background 0.05s ease;
}
.pad > span { grid-area: 1 / 1; }
.pad-drum { font-size: min(18vw, 76px); line-height: 1; }
.pad-start { display: none; width: 44%; height: 44%; border-radius: 50%; background: var(--primary); place-items: center; }
.pad-start svg { width: 60%; height: 60%; fill: var(--primary-text); margin-left: 8%; }
/* Waiting for a tap to start the line: the ▶ breathes over the drum. */
.pad[data-mode="ready"] .pad-start { display: grid; animation: breathe 1.6s ease-in-out infinite; }
.pad[data-mode="ready"] .pad-drum { opacity: 0.3; }
.pad[data-mode="counting"], .pad[data-mode="playing"] { border-color: var(--correct); }
.pad[data-mode="waiting"] { opacity: 0.5; }
.pad-count { font-size: min(16vw, 72px); font-weight: 800; line-height: 1; color: var(--correct); }
.pad-count:empty { display: none; }
.pad:has(.pad-count:not(:empty)) .pad-drum { opacity: 0.2; }
.pad.down { transform: scale(0.93); background: color-mix(in srgb, var(--primary) 40%, var(--surface)); }
@media (prefers-reduced-motion: reduce) {
  .pad[data-mode="ready"] .pad-start { animation: none; }
}

/* Progress, rhythm: each bar's label is its little rhythm (staff.js, drawCell). */
.rhythm-chart { --label-h: 54px; }
.rhythm-chart .bar-col { flex-basis: 52px; }
.rhythm-chart .bar { width: 30px; }
.rhythm-chart .bar-label { width: 100%; padding: 2px 0; }
.cell-picture { display: block; width: 100%; height: 100%; }
.cell-picture .vf-stavenote, .cell-picture .vf-stavenote * { fill: var(--text); stroke: var(--text); }
.cell-picture path, .cell-picture rect { fill: var(--text); stroke: var(--text); }

/* The levels of the Progress tab: the child's open, the later ones folded. */
.level-fold { display: flex; flex-direction: column; gap: 10px; }
.level-summary { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; list-style: none; }
.level-summary::-webkit-details-marker { display: none; }
.level-summary::before { content: '▸'; color: var(--muted); }
.progress-fold[open] > .level-summary::before { content: '▾'; }
.level-summary .level-name { flex: 1 1 8em; min-width: 0; }
.debug-fold { border-top: 1px solid var(--border); border-bottom: none; }
/* ✓ known and 🎙 recorded, side by side; under the name when it doesn't leave them room. */
.count-badges { display: inline-flex; gap: 6px; margin-left: auto; flex: none; }
.count-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 10px; font-size: 0.78rem; font-weight: 700; white-space: nowrap; }
.known-badge { color: var(--prog-known); background: color-mix(in srgb, var(--prog-known) 14%, var(--surface)); }
.rec-badge { color: var(--muted); background: color-mix(in srgb, var(--primary) 10%, var(--surface)); }
.mic-icon { display: inline-flex; width: 11px; height: 11px; }
.mic-icon svg { width: 100%; height: 100%; }
.rec-mark { display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: 50%; background: var(--primary); color: var(--primary-text); flex: none; }
.rec-mark .mic-icon { width: 10px; height: 10px; }

/* A bar's sheet (sheet.js): slides up from the bottom of the screen. */
.sheet {
  margin: auto auto 0;
  width: min(560px, 100vw);
  max-width: 100vw;
  max-height: 92dvh;
  border: none;
  border-radius: 22px 22px 0 0;
  background: var(--bg);
  color: var(--text);
  padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
  box-shadow: 0 -6px 24px rgb(0 0 0 / 0.18);
  overflow-y: auto;
}
.sheet[open] { display: flex; flex-direction: column; gap: 14px; animation: sheet-up 0.22s ease-out; }
.sheet::backdrop { background: rgb(0 0 0 / 0.35); }
@keyframes sheet-up { from { transform: translateY(40px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .sheet[open] { animation: none; } }
.sheet-grip { align-self: center; width: 40px; height: 5px; border-radius: 3px; background: var(--border); }
.sheet-head { display: flex; align-items: center; gap: 12px; }
.sheet-heading { flex: 1; min-width: 0; }
.sheet-heading h3 { margin: 0; font-size: 1.15rem; }
.sheet-heading p { margin: 2px 0 0; }
.sheet-close { flex: none; width: 44px; height: 44px; border-radius: 50%; border: 2px solid var(--border); background: var(--surface); color: var(--text); font-size: 1.1rem; }
/* The note, colored by status as its bar. */
.sheet-tile { flex: none; width: 60px; height: 64px; border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; font-weight: 800; }
.sheet-tile.known { background: var(--prog-known); }
.sheet-tile.learning { background: var(--prog-learning); color: #1d1300; }
.sheet-tile.hard { background: var(--prog-hard); }
.sheet-tile.unseen { background: var(--surface); border: 2px dashed var(--muted); color: var(--text); }
.sheet-tile-name { font-size: 1.2rem; line-height: 1.1; }
.sheet-tile-octave { font-size: 0.8rem; opacity: 0.85; }
.sheet-bars { display: flex; flex-direction: column; gap: 10px; }
.sheet-bar-line { display: flex; align-items: baseline; gap: 8px; }
.sheet-bar-name { flex: 1; font-weight: 600; }
.sheet-track { height: 10px; margin-top: 4px; border-radius: 5px; background: color-mix(in srgb, var(--border) 70%, var(--surface)); overflow: hidden; }
.sheet-fill { display: block; height: 100%; border-radius: 5px; }
.sheet-fill.known { background: var(--prog-known); }
.sheet-fill.learning { background: var(--prog-learning); }
.sheet-fill.hard { background: var(--prog-hard); }
.sheet-fill.unseen { background: var(--muted); opacity: 0.35; }
.sheet-recs { display: flex; flex-direction: column; gap: 8px; padding-top: 12px; border-top: 2px solid var(--border); }
.sheet-recs-head { display: flex; align-items: baseline; gap: 8px; }
.sheet-recs-head h4 { flex: 1; margin: 0; font-size: 1rem; }
.sheet-recs .rec-status { margin: 0; }
.sheet-rec { display: flex; align-items: center; gap: 8px; padding: 6px 6px 6px 14px; border: 2px solid var(--border); border-radius: 14px; background: var(--surface); }
.sheet-rec.has { border-color: var(--correct); }
.sheet-rec.recording { border-color: var(--wrong); }
.sheet-rec.recording .rec-btn { background: var(--wrong); border-color: var(--wrong); color: #fff; animation: pulse 1s infinite; }
.sheet-rec-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.sheet-rec-name { font-size: 1.05rem; font-weight: 800; }
.sheet-rec .rec-action { height: 44px; }
.sheet-rec .del-btn { margin-left: 0; }
.sheet-nav { display: flex; align-items: center; gap: 8px; }
.sheet-nav > span { flex: 1; text-align: center; }
.sheet-nav .btn-small { min-height: 44px; }
.sheet-nav .btn-small:disabled { opacity: 0.4; }

/* The child's voice (voiceview.js), at the bottom of the Voice tab. */
/* A card, as the Progress tab's levels; the open one outlined in the app's color. */
.voice-fold { border: 2px solid var(--border); border-radius: 16px; background: var(--surface); padding: 0 12px; }
.voice-fold[open] { border-color: var(--primary); padding-bottom: 12px; }
.voice-fold > summary::before, .voice-fold[open] > summary::before { content: none; }
.voice-fold[open] > summary .fold-title { color: var(--primary); }
.voice-fold > summary .fold-title { flex: 1; }
.voice-view { display: flex; flex-direction: column; gap: 10px; user-select: text; -webkit-user-select: text; }
.voice-view > * { margin: 0; }
/* A long list scrolls inside the card, the page around it as before; a shadow at the top or
   bottom shows there is more. */
.voice-rows {
  display: flex;
  flex-direction: column;
  max-height: 55dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-bottom: 1px solid var(--border);
  background:
    linear-gradient(var(--surface) 30%, transparent) top / 100% 28px no-repeat local,
    linear-gradient(transparent, var(--surface) 70%) bottom / 100% 28px no-repeat local,
    radial-gradient(farthest-side at 50% 0, rgb(0 0 0 / 0.14), transparent) top / 100% 10px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgb(0 0 0 / 0.14), transparent) bottom / 100% 10px no-repeat scroll,
    var(--surface);
}
.voice-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid var(--border); }
.voice-row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.voice-row-name { font-size: 1.1rem; font-weight: 500; }
.voice-row.in-play .voice-row-name { font-weight: 800; }
.voice-row-facts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.voice-row-facts .small { margin: 0; }
.voice-row .rec-action { height: 42px; width: 46px; }
.voice-row .del-btn { margin-left: 0; }
.voice-row .rec-btn.recording { background: var(--wrong); border-color: var(--wrong); color: #fff; animation: pulse 1s infinite; }
.example-dots { display: inline-flex; align-items: center; gap: 4px; }
.example-dots .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border); }
.example-dots .dot.on { background: var(--primary); }
.example-dots .small { margin: 0 0 0 2px; }
.helps-tag { font-size: 0.78rem; font-weight: 700; color: var(--correct); background: var(--correct-bg); border-radius: 10px; padding: 2px 8px; white-space: nowrap; }
.check-tag { font-size: 0.78rem; font-weight: 700; color: var(--primary-text); background: var(--primary); border-radius: 9px; padding: 1px 7px; white-space: nowrap; }
.voice-view .btn-small { align-self: flex-start; }
.voice-view .table-scroll { max-width: 100%; }

/* The Progress tab's levels as cards, as in the design: the open one outlined in the app's color. */
.progress-view .progress-fold { border: 2px solid var(--border); border-radius: 16px; background: var(--surface); padding: 0 12px; }
.progress-view .progress-fold[open] { border-color: var(--primary); padding-bottom: 12px; }
.progress-view .progress-fold > .level-summary { min-height: 56px; padding: 8px 0; }
.progress-view .progress-fold > .level-summary::before { content: none; }
.progress-view .progress-fold[open] > .level-summary .level-name { color: var(--primary); }
