/* Bundled fonts (fonts/, SIL OFL) so the app works offline and inside the
   Android shell. Silkscreen is the chrome — every label, button and heading, on
   an 8px pixel grid. Bricolage is what you actually read: hints, status lines,
   anything more than a few words, where pixel type stops being legible on a
   phone. */
@font-face {
  font-family: 'Silkscreen';
  src: url('fonts/Silkscreen-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: block;
}
@font-face {
  font-family: 'Silkscreen';
  src: url('fonts/Silkscreen-Bold.ttf') format('truetype');
  font-weight: 700;
  font-display: block;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  src: url('fonts/BricolageGrotesque-Variable.ttf') format('truetype');
  font-weight: 200 800;
  font-stretch: 75% 100%;
  font-display: swap;
}
:root {
  --panel: #D8D4C8;
  --panel-deep: #C9C4B6;
  --panel-raise: #E6E2D7;
  --ink: #1C1B18;
  --ink-soft: #5E5B52;
  --line: #A8A294;
  --rec: #D6341F;
  --rec-deep: #A8230F;
  --note: #1F4FD1;
  --trace: #8A6D2B;
  --fur: #8A8272;          /* Lupa's light fur */
  --lens: #0E1022;         /* her sunglasses */
  --night: #0b1026;
  /* 8-bit: nothing is rounded and nothing is blurred. A raised thing is the
     ink shifted two pixels down and right; pressing it moves the thing onto
     its own shadow. */
  --radius: 0;
  --drop: 2px 2px 0 var(--ink);
  --font: 'Bricolage Grotesque', system-ui, sans-serif;
  --pixel: 'Silkscreen', 'Courier New', monospace;
}
* { box-sizing: border-box; }
html { color-scheme: light; }
body {
  margin: 0;
  height: 100dvh;
  display: grid;
  /* minmax(0, 1fr), not the default auto: an auto column takes the width of the
     widest row's content, and the header's row of buttons does not shrink, so
     the whole app would end up wider than the phone it is on. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  overflow: hidden;
  background: var(--panel);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  /* the dither that makes the paper read as a screen rather than a page */
  background-image: repeating-linear-gradient(0deg, rgba(28,27,24,0.035) 0 1px, transparent 1px 3px);
}
h1, h2, h3, p { margin: 0; }
button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 2px solid var(--note); outline-offset: 2px; }
[hidden] { display: none !important; }
input[type=range], input[type=checkbox] { accent-color: var(--ink); }
svg { display: block; }
/* Pixel type for everything that labels the machine. */
.wordmark, .block-h, .btn, .chip, .tab, .seg button, .settings h2, .fold summary,
.tchip, .part-chip, .track-badge, .vibes-label, .tool-label, .menu-list button,
.menu-list .file, .field > span:first-child, .rec-status, .song-section, .sketch-list .pick b {
  font-family: var(--pixel);
  letter-spacing: 0;
  text-transform: none;
}
/* Kill every soft edge the browser would otherwise give us. */
.btn, .chip, .tab, .seg, .tchip, dialog, input, select, .menu-list, .analysis,
.fold, .meter, .roll-box, .part-chip, .toast, .mixer-row { border-radius: 0; }

/* ---------- header ---------- */
.top {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px;
  border-bottom: 2px solid var(--ink);
  background: var(--panel-raise);
  min-width: 0;
}
.brand { display: flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; flex: none; }
.logo { flex: none; image-rendering: pixelated; }
.wordmark {
  display: flex; flex-direction: column;
  font-weight: 700; font-size: 15px; line-height: 1;
}
.wordmark span:last-child { color: var(--rec); }
.transport { display: flex; align-items: center; gap: 6px; margin-left: auto; min-width: 0; }
/* Nothing in the header shrinks, so when it runs out of room the row itself
   scrolls rather than pushing the app sideways. */
.top { overflow-x: auto; scrollbar-width: none; }
.top::-webkit-scrollbar { display: none; }
.mix { display: flex; align-items: center; gap: 5px; font-size: 10px; font-family: var(--pixel); color: var(--ink-soft); }
.mix input { width: 70px; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 11px;
  border: 2px solid var(--ink);
  background: var(--panel-raise);
  font-size: 11px; font-weight: 400; line-height: 1;
  box-shadow: var(--drop);
  white-space: nowrap;
}
.btn:hover { background: #fff; }
.btn:active { transform: translate(2px, 2px); box-shadow: none; }
.btn:disabled { opacity: 0.35; cursor: default; box-shadow: none; transform: none; }
.btn:disabled:hover { background: var(--panel-raise); }
.btn-primary { background: var(--ink); color: var(--panel-raise); }
.btn-primary:hover { background: #000; }
.btn-quiet { box-shadow: none; border-color: var(--line); background: transparent; }
.btn-quiet:hover { background: var(--panel-raise); border-color: var(--ink); }
.btn-small { padding: 7px 8px; font-size: 10px; }
.icon-btn { padding: 8px 9px; }
.btn-play.playing { background: var(--ink); color: var(--panel-raise); }
.btn-magic { background: var(--ink); color: var(--panel-raise); }
.btn-magic:hover { background: #000; }
.btn-magic svg { color: #F2C14E; }
.btn-song { background: var(--night); color: #F2E9C9; border-color: var(--ink); }
.btn-song:hover { background: #141a3d; }
.btn-song svg { color: #F2E9C9; }
.file { position: relative; }
.file input { position: absolute; inset: 0; opacity: 0; width: 100%; cursor: pointer; }
.wide-only { display: none; }

/* ---------- the Sketch dropdown ---------- */
.menu { position: relative; }
.menu summary { list-style: none; user-select: none; }
.menu summary::-webkit-details-marker { display: none; }
.caret { display: inline-block; width: 0; height: 0; border: 4px solid transparent; border-top: 5px solid currentColor; border-bottom: 0; }
.menu[open] summary { background: var(--ink); color: var(--panel-raise); }
/* Fixed and placed by placeMenu() in js/main.js, not absolute: the header is a
   sideways scroller, and a declared overflow-x turns overflow-y from visible
   into auto, so the header clips in both axes. A dropdown hanging below it was
   cut off and took no clicks at all — the tabs underneath got them. */
.menu-list {
  position: fixed; z-index: 30;
  min-width: 210px; max-width: calc(100vw - 16px); padding: 5px;
  max-height: min(70vh, 460px); overflow-y: auto;
  background: var(--panel-raise); border: 2px solid var(--ink);
  box-shadow: 4px 4px 0 rgba(28,27,24,0.45);
  display: flex; flex-direction: column;
}
.menu-list button, .menu-list .file {
  display: block; width: 100%; text-align: left;
  padding: 9px 8px; border: 0; background: transparent; font-size: 11px;
}
.menu-list button:hover, .menu-list .file:hover { background: var(--ink); color: var(--panel-raise); }
.menu-list .file { cursor: pointer; }
.menu-now {
  padding: 4px 8px 7px; font-size: 12px; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu-list hr { border: 0; border-top: 2px solid var(--line); margin: 5px 2px; }

/* ---------- tabs ---------- */
.tabs { display: flex; border-bottom: 2px solid var(--ink); background: var(--panel-deep); }
.tab {
  flex: 1; padding: 9px 8px; border: 0; background: transparent;
  font-size: 12px; color: var(--ink-soft);
  border-bottom: 4px solid transparent;
}
.tab.on { color: var(--ink); background: var(--panel); border-bottom-color: var(--rec); }

/* ---------- the two panes ---------- */
.panes {
  display: flex; min-height: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.panes::-webkit-scrollbar { display: none; }
.pane {
  flex: 0 0 100%; min-width: 0; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 10px;
  padding: 10px 12px;
  overflow-y: auto; overscroll-behavior-y: contain;
}
#pane-edit { overflow-y: hidden; padding-bottom: 8px; }
.block { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.block-h {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 700; color: var(--ink-soft);
}
.block-h::before { content: ''; width: 6px; height: 6px; background: var(--rec); flex: none; }

/* A row of chips or tools that scrolls sideways instead of wrapping: the whole
   point of the two panes is that nothing on them scrolls up and down. */
.scroller {
  display: flex; align-items: center; gap: 5px;
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; padding-bottom: 2px;
}
.scroller::-webkit-scrollbar { display: none; }
.scroller > * { flex: none; }

.chip {
  padding: 6px 9px;
  border: 2px solid var(--line);
  background: var(--panel-raise);
  font-size: 11px; line-height: 1; white-space: nowrap;
}
.chip:hover { border-color: var(--ink); }
.chip.on { background: var(--ink); color: var(--panel-raise); border-color: var(--ink); box-shadow: var(--drop); }
.chip small { color: inherit; opacity: 0.6; margin-left: 5px; font-family: var(--pixel); }
.chips.small .chip { font-size: 10px; padding: 5px 8px; }
.vibe-chips .chip.on { background: var(--rec); border-color: var(--ink); color: #fff; }

.row { display: flex; gap: 14px; flex-wrap: wrap; align-items: end; }
.row.end { justify-content: flex-end; }
.field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--ink-soft); }
.field > span:first-child { font-size: 10px; }
.field input[type=range] { width: 100%; max-width: 200px; }
.field.check { flex-direction: row; align-items: center; gap: 8px; color: var(--ink); }
.field.check span { font-family: var(--font); font-size: 13px; }
.field.col small { color: var(--ink-soft); font-size: 12px; }
.field.swing { min-width: 110px; }
.tempo { display: flex; align-items: baseline; gap: 5px; color: var(--ink); }
.tempo em { font-style: normal; color: var(--ink-soft); font-size: 12px; }
input[type=number], input[type=text], input[type=password], select {
  padding: 7px 8px;
  border: 2px solid var(--line);
  background: var(--panel-raise);
  color: var(--ink);
}
input:focus, select:focus { border-color: var(--ink); }
input[type=number] { width: 70px; font-variant-numeric: tabular-nums; }
.seg { display: inline-flex; border: 2px solid var(--line); }
.seg button { padding: 6px 10px; border: 0; background: transparent; color: var(--ink); font-size: 11px; }
.seg button + button { border-left: 2px solid var(--line); }
.seg button.on { background: var(--ink); color: var(--panel-raise); }
.hint { color: var(--ink-soft); font-size: 12.5px; line-height: 1.35; }
/* Instrument, both, or the singer's own take: one row, never taller. */
.hear-row { display: flex; align-items: center; gap: 10px; margin-top: 8px; min-width: 0; }
.hear-row .seg { flex: none; }
.hear-row .seg button:disabled { opacity: 0.35; }
.hear-row .hint { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.block-vibe .hint { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A collapsed options row: the title on the left, what it is set to on the
   right, so it can stay shut most of the time. */
.fold { border: 2px solid var(--line); background: var(--panel-raise); }
.fold summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 9px; cursor: pointer; font-size: 10px; list-style: none;
}
.fold summary::-webkit-details-marker { display: none; }
.fold summary::after { content: '▸'; color: var(--ink-soft); }
.fold[open] summary::after { content: '▾'; }
.fold[open] summary { border-bottom: 2px solid var(--line); }
.fold-now { color: var(--ink-soft); margin-left: auto; }
.fold-body { padding: 9px; }

 /* Everything that acts on the selected track is marked the same way: an 8px
   block of that track's colour, the same block the dock chip and the mixer row
   already carry. Colour is what ties a control to the track it will change, so
   these three must stay in step. --track is the selected track's colour, set on
   the root by selectTrack(). */
.track-badge {
  display: inline-flex; align-items: center; gap: 5px;
  margin-left: auto; font-size: 9px;
  color: var(--ink); background: var(--panel-raise);
  padding: 3px 6px; border: 2px solid var(--ink);
  border-left: 8px solid var(--track, var(--note));
}
.track-badge i { display: none; }
/* One flash when you change track, so it is clear these blocks followed you. */
@keyframes track-say {
  0%, 100% { background: var(--panel-raise); color: var(--ink); }
  50% { background: var(--track, var(--note)); color: var(--panel-raise); }
}
.track-badge.say { animation: track-say 0.36s steps(2) 2; }
@media (prefers-reduced-motion: reduce) { .track-badge.say { animation: none; } }

/* ---------- record ---------- */
.rec-area { display: flex; gap: 12px; align-items: center; }
.rec {
  flex: none;
  width: 64px; height: 64px;
  border: 2px solid var(--ink);
  background: var(--rec);
  display: grid; place-items: center;
  box-shadow: 4px 4px 0 var(--ink);
}
.rec:active, .rec.armed { transform: translate(4px, 4px); box-shadow: none; }
.rec-dot { width: 20px; height: 20px; background: var(--panel-raise); }
/* The count is shown as well as played — 4, 3, 2, 1 on the button, one per
   click — because a device that swallows the first clicks (see OUTPUT_WARMUP
   in js/audio.js) must not leave the singer with nothing to come in on. The
   number and the dot share the one grid cell, so the button does not change
   size when the count starts. */
.rec-dot, .rec-count { grid-area: 1 / 1; }
.rec-count { display: none; font: 30px/1 var(--pixel); color: var(--panel-raise); }
.rec.counting .rec-dot { display: none; }
.rec.counting .rec-count { display: block; }
/* One pop per click, restarted by js/main.js, so the number is seen to land
   rather than merely being different from the last one. */
.rec-count.tick { animation: count-pop 0.2s steps(2); }
@keyframes count-pop { 50% { transform: scale(1.3); } }
.rec.recording { background: var(--rec-deep); }
.rec.recording .rec-dot { width: 18px; height: 18px; animation: none; }
.rec:disabled { filter: grayscale(0.6); opacity: 0.5; transform: none; }
.rec-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rec-status { font-size: 12px; }
.rec-status b { font-weight: 700; color: var(--rec-deep); }
.meter { height: 8px; background: var(--panel-deep); border: 2px solid var(--line); margin-top: 3px; overflow: hidden; }
.meter span {
  display: block; height: 100%; width: 0; background: var(--ink);
  /* stepped, like a level meter made of blocks */
  background-image: repeating-linear-gradient(90deg, var(--ink) 0 4px, var(--panel-deep) 4px 6px);
  transition: width 0.06s steps(8);
}
.analysis {
  font-size: 12px; padding: 6px 8px;
  background: var(--panel-raise); border: 2px solid var(--line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- sound ---------- */
.patch-result { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; min-width: 0; }
.patch-result b { font-family: var(--pixel); font-size: 12px; flex: none; color: var(--track, var(--note)); }
.patch-result span { color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An instrument on loan from another vibe, behind "More sounds". */
.chip.borrowed { border-style: dashed; }
/* The toggle is not an instrument, so it does not get the selected-chip fill. */
.chip.more { border-color: var(--ink); border-style: dashed; color: var(--ink-soft); }
.chip.more.open { border-style: solid; color: var(--ink); background: var(--panel-deep); }

/* ---------- piano roll ---------- */
.roll-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.legend { font-size: 11px; color: var(--ink-soft); display: flex; gap: 9px; align-items: center; }
.sw { display: inline-block; width: 10px; height: 8px; margin-right: 4px; vertical-align: middle; }
.sw-trace { background: var(--trace); height: 3px; }
.sw-note { background: var(--note); }
.sw-ghost { background: var(--ink); opacity: 0.25; }
.roll-tools { gap: 6px; }
.tool-group { display: inline-flex; gap: 4px; }
.tool-group.zoom .btn { min-width: 36px; }
.tool-label { font-size: 10px; color: var(--ink-soft); }
/* The roll scrolls inside its box, so the "nothing here yet" line cannot live
   inside it — it would scroll away. It sits over the box instead. */
.roll-stage { position: relative; flex: 1; min-height: 120px; display: flex; }
.roll-box {
  position: relative;
  flex: 1; min-width: 0;
  overflow: auto; overscroll-behavior: contain;
  background: var(--panel-deep);
  border: 2px solid var(--ink);
}
#roll { display: block; background: var(--panel-deep); cursor: crosshair; }
/* The playhead is its own element, not a rect in the canvas: it moves every
   frame while the loop plays, and redrawing the whole roll (every track's
   notes, the pitch trace, the gutters) sixty times a second was main-thread
   work the scheduler wanted back — that is what the loop breaking up was.
   Absolutely positioned inside the scrolling box, so it scrolls with the
   canvas, and moved with a transform so it never costs a layout. */
.playhead {
  position: absolute; left: 0; top: 0; width: 2px;
  background: var(--rec); pointer-events: none; display: none;
  will-change: transform;
}
.roll-empty {
  position: absolute; left: 16px; right: 16px; top: 42%; margin: 0;
  text-align: center; color: var(--ink-soft); font-size: 13px; pointer-events: none;
}
.roll-tools.nudge { padding: 6px; background: var(--panel-raise); border: 2px solid var(--line); }

/* Lupa, filling whatever is left of the Sing pane. She is a watermark until
   there is something to listen to, and then she is not. */
.lupa-idle {
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: flex; align-items: flex-end; justify-content: center;
}
.lupa-idle svg {
  width: min(48%, 170px); max-height: 100%; height: auto;
  image-rendering: pixelated; opacity: 0.22;
  transition: opacity 0.2s steps(4);
}
body.lupa-on .lupa-idle svg { opacity: 1; animation: lupa-bob 0.5s steps(2) infinite; }
@keyframes lupa-bob { 50% { transform: translateY(-4px); } }

/* ---------- the dock: tracks and the one big button ---------- */
/* Two rows. The tracks had to share one row with three buttons, which left
   them about 170px on a phone: with three tracks you saw one, and the selected
   one could be scrolled off altogether. They get the full width now. */
.dock {
  display: grid; grid-template-rows: auto auto; gap: 7px;
  padding: 7px 12px calc(7px + env(safe-area-inset-bottom));
  border-top: 2px solid var(--ink);
  background: var(--panel-deep);
}
.dock-tracks { min-width: 0; gap: 5px; }
.dock-acts { display: flex; align-items: center; gap: 6px; }
.dock-acts .last { margin-left: auto; }
.tchip {
  --track: var(--note);
  display: flex; align-items: center; gap: 6px;
  padding: 5px 5px 5px 0;
  border: 2px solid var(--line); background: var(--panel-raise);
  font-size: 10px; line-height: 1; max-width: 190px;
}
/* Inked is what "on" looks like everywhere else here (a pressed chip, an open
   menu), so the selected track is inked too — and it is the one holding a
   fader, because the level belongs with the track it changes. */
.tchip.on {
  border-color: var(--ink); box-shadow: var(--drop);
  background: var(--ink); color: var(--panel-raise);
  max-width: 260px;
}
.tchip.on .pick b { color: var(--panel-raise); }
.tchip .lvl { display: none; }
.tchip.on .lvl {
  display: block; width: 40px; flex: none; margin: 0 1px;
  accent-color: var(--track);
}
.tchip.on .m { border-color: var(--panel-raise); color: var(--panel-raise); }
/* On a phone there is not room for four names at once, and the name that
   matters is the one you are working on. The others keep their colour and
   their number, which is what tells them apart; the name comes back as soon
   as the strip is wide enough to hold it. */
.tchip:not(.on) .pick span { display: none; }
@media (min-width: 560px) { .tchip:not(.on) .pick span { display: inline; } }
.tchip .bar { width: 8px; align-self: stretch; background: var(--track); flex: none; }
.tchip .pick {
  display: flex; align-items: center; gap: 5px; min-width: 0;
  border: 0; background: transparent; padding: 3px 0 3px 4px; font: inherit; color: inherit;
}
.tchip .pick b { color: var(--track); font-weight: 700; flex: none; }
.tchip .pick span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tchip.muted .pick { opacity: 0.45; }
.tchip .m {
  border: 2px solid var(--line); background: transparent; color: var(--ink-soft);
  font-family: var(--pixel); font-size: 9px; line-height: 1; padding: 3px 4px; flex: none;
}
.tchip .m[aria-pressed=true] { background: var(--rec); border-color: var(--ink); color: #fff; }

/* the mixer, where levels and deleting a track live */
.mixer-list { display: flex; flex-direction: column; gap: 8px; }
.mixer-row {
  --track: var(--note);
  display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 10px;
  padding: 8px; border: 2px solid var(--line); border-left: 8px solid var(--track);
  background: var(--panel-raise);
}
.mixer-row .who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mixer-row .who b { font-family: var(--pixel); font-size: 11px; }
.mixer-row .who small { color: var(--ink-soft); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mixer-row input[type=range] { width: 100%; accent-color: var(--track); grid-column: 1 / -1; }
.mixer-row .m, .mixer-row .x {
  border: 2px solid var(--line); background: transparent; font-family: var(--pixel);
  font-size: 10px; padding: 6px 8px; line-height: 1;
}
.mixer-row .m[aria-pressed=true] { background: var(--ink); color: var(--panel-raise); border-color: var(--ink); }
.mixer-row .x { color: var(--ink-soft); }
.mixer-row .x:hover { color: #fff; background: var(--rec); border-color: var(--ink); }
.mixer-row .x:disabled { opacity: 0.3; }

/* ---------- dialogs ---------- */
dialog {
  border: 2px solid var(--ink); background: var(--panel); color: var(--ink);
  padding: 0; width: min(520px, calc(100vw - 24px));
  box-shadow: 6px 6px 0 rgba(28,27,24,0.5);
}
dialog::backdrop { background: rgba(28, 27, 24, 0.6); }
.settings { padding: 18px; display: flex; flex-direction: column; gap: 14px; }
.settings h2 { font-size: 14px; }
.settings .field.col input, .settings .field.col select { width: 100%; }
.latency .row { align-items: center; gap: 10px; }
.sketch-list { list-style: none; margin: 0; padding: 0; max-height: min(52vh, 420px); overflow-y: auto; }
.sketch-list li { display: flex; align-items: center; gap: 8px; border-top: 2px solid var(--line); }
.sketch-list li:last-child { border-bottom: 2px solid var(--line); }
.sketch-list .pick { flex: 1; min-width: 0; text-align: left; background: transparent; border: 0; padding: 10px 8px; }
.sketch-list .pick:hover { background: var(--panel-raise); }
.sketch-list .pick b { display: block; font-size: 11px; color: var(--ink); }
.sketch-list .pick small { color: var(--ink-soft); font-size: 12px; }
.sketch-list li.on .pick b::after { content: ' · open'; color: var(--rec); }
.sketch-list .drop { flex: none; padding: 8px 10px; background: transparent; border: 0; color: var(--ink-soft); font-size: 16px; line-height: 1; }
.sketch-list .drop:hover { color: var(--rec); }

/* ---------- the song sky ---------- */
.song {
  position: fixed; inset: 0; z-index: 40;
  display: flex; flex-direction: column; justify-content: space-between;
  background: var(--night); color: #E8E4D8;
  overscroll-behavior: contain; touch-action: none;
}
.song canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }
.song canvas:active { cursor: grabbing; }
.song-top, .song-foot { position: relative; z-index: 1; padding: 10px 14px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; pointer-events: none; }
.song-top > *, .song-foot > * { pointer-events: auto; }
.song-foot { padding-bottom: max(12px, env(safe-area-inset-bottom)); justify-content: space-between; }
.song-top { padding-top: max(10px, env(safe-area-inset-top)); }
.song-title { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.song-title b { font-family: var(--pixel); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.song-section { font-size: 10px; color: rgba(242, 233, 201, 0.8); min-height: 1.2em; }
.song-section.pop { animation: song-pop 0.5s steps(4); }
@keyframes song-pop { from { opacity: 0; } to { opacity: 1; } }
.song-hint { font-size: 12.5px; color: rgba(232, 228, 216, 0.8); max-width: 520px; flex: 1 1 240px; }
.song-controls { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* One chip per part Lupa made up. Its colour is the colour of its own
   constellation in the sky, so the chip and the stars it rolls are the same
   thing seen twice. */
.song-parts { display: flex; gap: 6px; flex-wrap: wrap; }
.part-chip {
  --part: #E8E4D8;
  display: inline-flex; align-items: center; gap: 2px;
  border: 2px solid var(--part);
  color: var(--part); font-size: 10px; padding-left: 8px;
  background: rgba(11, 16, 38, 0.6);
}
.part-chip b { display: inline-flex; align-items: center; gap: 5px; margin-right: 4px; font-weight: 700; }
.part-chip b::before { content: ''; width: 6px; height: 6px; background: var(--part); flex: none; }
.part-chip button {
  appearance: none; background: transparent; border: 0; color: inherit; cursor: pointer;
  font: inherit; font-size: 13px; line-height: 1; min-width: 32px; height: 32px;
}
.part-chip button:hover { background: rgba(232, 228, 216, 0.15); }
.part-chip .keep[aria-pressed="true"] { color: #fff; }
.part-chip.kept { background: var(--part); color: var(--night); }
.part-chip.kept b::before { background: var(--night); }
.part-chip.kept button:hover { background: rgba(11, 16, 38, 0.2); }
.song .btn { background: rgba(11, 16, 38, 0.6); color: #E8E4D8; border-color: rgba(232, 228, 216, 0.6); box-shadow: none; }
.song .btn:hover { background: rgba(232, 228, 216, 0.16); }
.song .btn-play { background: #F2E9C9; color: var(--night); border-color: #F2E9C9; min-width: 88px; }
.song .btn-play:hover { background: #fff; }
.song .btn:disabled { opacity: 0.4; }
body.song-open { overflow: hidden; }
/* The song's dialogs sit over the night but are drawn like every other dialog. */
.song dialog { color: var(--ink); }
.song dialog .btn { background: var(--panel-raise); color: var(--ink); border-color: var(--ink); box-shadow: var(--drop); }
.song dialog .btn:hover { background: var(--panel); }
.song dialog .btn-quiet { background: transparent; border-color: var(--line); box-shadow: none; }
.song dialog .btn-primary { background: var(--ink); color: var(--panel-raise); }
.song dialog .btn:disabled { opacity: 0.4; }
/* the three ways to keep a song: a heading and a sentence each */
.save-ways { display: flex; flex-direction: column; gap: 8px; }
.save-ways .btn { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; text-align: left; padding: 10px 12px; }
.save-ways .btn b { font-size: 12px; }
.save-ways .btn small { font-family: var(--font); font-size: 12.5px; color: var(--ink-soft); font-weight: 400; }
/* the Shape dialog: one row per section, in order */
.shape-forms { display: flex; gap: 6px; flex-wrap: wrap; }
.shape-list { list-style: none; margin: 0; padding: 0; max-height: min(44vh, 380px); overflow-y: auto; counter-reset: sec; }
.shape-list li {
  display: flex; align-items: center; gap: 6px; padding: 6px 4px 6px 8px;
  border-top: 2px solid var(--line); border-left: 8px solid var(--sec, var(--line));
  background: var(--panel-raise); counter-increment: sec;
}
.shape-list li:last-child { border-bottom: 2px solid var(--line); }
.shape-list li b { flex: 1; min-width: 0; font-family: var(--pixel); font-size: 11px; }
.shape-list li b::before { content: counter(sec) '. '; color: var(--ink-soft); }
.shape-list li[data-kind=intro], .shape-list li[data-kind=outro] { --sec: #8A8272; }
.shape-list li[data-kind=verse] { --sec: #1F4FD1; }
.shape-list li[data-kind=chorus] { --sec: var(--rec); }
.shape-list li[data-kind=breath] { --sec: #1E7F4E; }
.shape-list li[data-kind=bridge] { --sec: #7A3BBF; }
.shape-list .seg button { padding: 5px 7px; font-size: 10px; }
.shape-act {
  flex: none; min-width: 30px; height: 30px; border: 2px solid var(--line); background: transparent;
  font-family: var(--pixel); font-size: 11px; color: var(--ink); line-height: 1;
}
.shape-act:disabled { opacity: 0.25; }
.shape-act[data-act=drop]:hover:not(:disabled) { background: var(--rec); color: #fff; border-color: var(--ink); }
.shape-add { display: flex; gap: 6px; align-items: center; }
.shape-add .chip { font-size: 10px; }

.toast {
  position: fixed; left: 50%; bottom: 84px; transform: translateX(-50%);
  background: var(--ink); color: var(--panel-raise);
  border: 2px solid var(--panel-raise);
  padding: 9px 13px; font-size: 13px; opacity: 0; pointer-events: none;
  transition: opacity 0.15s steps(3);
  max-width: calc(100vw - 24px); z-index: 60;
}
.toast.show { opacity: 1; }

/* ---------- wide: both panes at once, no tabs ---------- */
@media (min-width: 900px) {
  .tabs { display: none; }
  .tool-group.zoom { margin-left: auto; }
  .panes { overflow-x: hidden; }
  .pane { flex: 1 1 0; scroll-snap-align: none; }
  #pane-edit { border-left: 2px solid var(--ink); }
  .wide-only { display: inline; }
  .wordmark { font-size: 18px; }
  .rec { width: 76px; height: 76px; }
  .rec-dot { width: 24px; height: 24px; }
  .rec-count { font-size: 36px; }
}
@media (max-width: 480px) {
  /* Play and Stop are the icons they already are; the words are the first
     thing to go when the header is tight. */
  #play span, #stop span { display: none; }
  #play, #stop { padding: 8px 10px; }
  .wordmark { font-size: 13px; }
  .logo { width: 32px; height: 32px; }
}
@media (max-width: 420px) {
  .mix.beat-mix { display: none; }
  .btn { padding: 7px 9px; }
  .dock { gap: 6px; padding-left: 8px; padding-right: 8px; }
  .pane { padding: 8px 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .toast, .meter span { transition: none; }
  .song-section.pop { animation: none; }
  .rec-count.tick, .patch-result.busy b::after { animation: none; }
}
