[hidden] { display: none !important; }
* { box-sizing: border-box; }
:root {
  color-scheme: dark;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  color: #25313a;
  background: #282e32;
  font-synthesis: none;
  --bb-cyan: #b7d4e5;
  --bb-amber: #ffd183;
  --bb-yellow: #ffe16b;
  --bb-readout: 'SFMono-Regular', Menlo, Consolas, monospace;
  --bb-frame: 12px;
  --bb-metal: url('./alloy-grain.svg'), linear-gradient(112deg, #dddedd, #bcbdbd 43%, #cfd0d0 74%, #aeb1b2);
}
/* A tap is only ever a tap. touch-action: manipulation keeps panning and pinch-zoom but takes away double-tap-to-zoom,
   which was scaling and shoving the whole enclosure whenever two taps landed near each other during play — and playing
   this game is rapid tapping. overscroll-behavior: none takes away the rubber-band bounce that a tap with a few pixels
   of finger drift used to start, and with it pull-to-refresh, which could reload the page mid-run. Deliberate scrolling
   and swiping are untouched (Mike, 2026-09-17). Set on the root so it governs every element, not just the buttons. */
/* The machine is a fixed-size object, so it is scaled to the viewport rather than scrolled off the bottom of it: the
   page itself never scrolls and the enclosure sits centred at whatever size fits (--bb-fit, set by fitDevice in
   app.mjs). Panels with their own content to scroll — the trays, Rules, Career, the dialogs, the campaign map — still
   scroll inside themselves (Mike, 2026-09-17: the page scrolled like a madman on a phone). */
html { height: 100%; touch-action: manipulation; overscroll-behavior: none; }
body { display: grid; place-items: center; height: 100%; overflow: hidden; margin: 0; padding: var(--bb-frame) 20px; background: radial-gradient(ellipse at 42% 22%, #424344, #252728 75%); }
button { font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }
button:focus-visible, a:focus-visible { outline: none; }
button:disabled { cursor: default; }
/* The machine is 400px wide, or the room it has, and nothing else. body's place-items:center leaves this a
   fit-content grid item, so max-width let the enclosure size itself to its longest unbreakable run of text: the LCD
   reading "Campaign cleared" made it 356px where a drum name made it 343, a 3.8% difference that the fit then
   faithfully scaled — which is the whole of "Jungle is a smaller device than Motown" and every size jump after it
   (Mike, 2026-09-19). The pads are aspect-ratio:1, so width is height here. */
#bb-pairs { width: min(400px, 100%); min-width: 0; margin: auto; }
.bb-device { position: relative; perspective: 1800px; transform: perspective(1800px) translateY(var(--bb-lift, 0px)) rotateX(12deg) scale(var(--bb-fit, 1)); }
.bb-machine, .bb-rules-panel, .bb-career-panel {
  border: 1px solid #dfe1e1; border-radius: 9px; background: var(--bb-metal);
  box-shadow: inset 0 1px 0 #ffffffd9, inset -1px 0 0 #757879, 0 18px 0 #555859, 0 22px 0 #17191a, 0 24px 7px #0008, 0 43px 29px #0007;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  transition: transform 420ms cubic-bezier(.2, .75, .2, 1);
}
.bb-machine { position: relative; isolation: isolate; padding: 16px 16px 14px; transform: rotateY(0deg); }
.bb-machine::after { content: ''; position: absolute; z-index: -1; left: -1px; right: -1px; bottom: -19px; height: 20px; border: 1px solid #696b6c; border-top: 1px solid #f2f3f3; border-radius: 0 0 16px 16px; background: url('./alloy-grain.svg'), linear-gradient(#b2b4b4 0%, #919494 25%, #656869 77%, #4b4e4f); box-shadow: inset 0 1px 0 #ffffff4d, 0 4px 0 #17191a; pointer-events: none; }
.bb-rules-panel, .bb-career-panel { position: absolute; inset: 0; overflow: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #71818b #c8d0d5; transform: rotateY(-180deg); }
.bb-device[data-face="rules"] .bb-machine, .bb-device[data-face="career"] .bb-machine { transform: rotateY(180deg); }
.bb-device[data-face="rules"] .bb-rules-panel, .bb-device[data-face="career"] .bb-career-panel { transform: rotateY(0deg); }
.bb-nameplate { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: -3px 0 11px; }
.bb-wordmark { display: flex; align-items: center; color: #25313a; font-size: 27px; line-height: 1; font-weight: 700; letter-spacing: -1.2px; text-shadow: 0 1px 0 #ffffff70; }
.bb-brand-mark { display: inline-flex; align-items: end; gap: 3px; height: 20px; margin-right: 8px; }
.bb-brand-mark i { width: 4px; height: 12px; background: currentColor; }
.bb-brand-mark i:nth-child(2) { height: 20px; }
.bb-brand-mark i:nth-child(3) { height: 15px; }
.bb-screen { --bb-side: 14px; position: relative; display: flex; flex-direction: column; padding: 16px var(--bb-side) 15px; border: 1px solid #090c0f; border-radius: 4px; background: linear-gradient(120deg, #1c242b, #11181e 72%, #1b2228); box-shadow: 0 1px 0 #ffffffbd, 0 -1px 0 #62676b, inset 0 2px 4px #000c, inset 1px 0 2px #0008; color: #e8eff3; }
/* The welcome: the LCD talking to a brand-new player, typed a character at a time, until Play starts the campaign. */
.bb-screen:has(.bb-welcome:not([hidden])) > *:not(.bb-welcome) { visibility: hidden; }
.bb-welcome { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; padding: 16px 18px; }
.bb-welcome-text { margin: 0; color: #d7e6ec; font: 600 15px/1.65 var(--bb-readout); white-space: pre-wrap; text-shadow: 0 0 10px #8fe8ff40; }
.bb-welcome-text b { display: block; margin-bottom: 6px; color: var(--bb-amber); font-size: 17px; letter-spacing: .06em; }
.bb-welcome-text i { font-style: normal; color: #3fe0e0; }
.bb-welcome-text .bb-caret { display: inline-block; width: .55em; color: #3fe0e0; animation: bb-caret 1s steps(1, end) infinite; }
@keyframes bb-caret { 50% { opacity: 0; } }
.bb-groove { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: end; gap: 10px; padding-bottom: 17px; }
/* One line each, always: a wrapped readout is a taller machine. */
.bb-genre { overflow: hidden; white-space: nowrap; font-size: 24px; line-height: 1.15; font-weight: 700; letter-spacing: -.7px; }
.bb-mode { display: grid; gap: 6px; }
.bb-score { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; min-width: 0; }
/* On the machine the head of the LCD is one grid of four rows that both columns share (subgrid), so the two sides read
   as lines of one table rather than two stacks pushed apart: the name and the number open the screen on one cap line,
   the tempo sits beside the number's label, and the Set strip closes the left on the unlock note's line. Each row is
   sized by what is always there, so no state moves it (Mike, 2026-09-26: "too much headroom above the title - title
   not aligned with score"). The number's column is only as wide as the number, which gives a long name the room. */
.bb-machine .bb-groove { grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto 1fr auto; align-items: end; column-gap: 14px; row-gap: 0; padding-bottom: 2px; }
.bb-machine .bb-mode, .bb-machine .bb-score { grid-row: 1 / -1; display: grid; grid-template-rows: subgrid; gap: 0; align-items: end; }
.bb-machine .bb-score { justify-items: end; }
/* Trimmed to the cap line, the name's box and the number's box start where their letters do, so aligning the boxes
   aligns the type. */
.bb-machine .bb-genre, .bb-machine .bb-score-value { grid-row: 1; align-self: start; line-height: 1; text-box: trim-start cap alphabetic; }
.bb-machine .bb-genre { padding-bottom: 2px; }
/* Figures have no descenders, so the number is trimmed at its baseline too and the tempo tucks up under the name. */
.bb-machine .bb-score-value { overflow: visible; text-box: trim-both cap alphabetic; }
.bb-machine .bb-tempo { grid-row: 2; margin-top: 8px; }
/* The label is a readout like the tempo and the Set: mono, and on the tempo's line. */
.bb-machine .bb-score-label { grid-row: 2; margin-top: 8px; min-height: 13px; font: 11px/1.2 var(--bb-readout); padding-bottom: 2px; }
.bb-machine .bb-run-length { grid-row: 3; margin-top: 5px; }
.bb-machine .bb-note-slot { grid-row: 4; margin-top: 4px; }
.bb-machine .bb-stage-strip { grid-row: 3 / 5; align-self: end; margin: 0 0 3px; }
/* The label holds its line whether or not it has a word in it. The LCD's height is what sets the machine's, and the fit
   that scales the enclosure is a function of the viewport alone — so this one line collapsing while you chase the
   target and coming back when you meet it resized the whole machine mid-run (Mike, 2026-09-20: "when you finish the
   round it's like the device shrinks"). */
.bb-score-label { display: block; color: #bac8d1; font-size: 10px; line-height: 12px; min-height: 12px; }
/* The audition's Solo and Mute keys, bottom right of the screen: outlined until they are on, then filled. */
/* The all-pads keys with the editing tag under them, at the LCD's bottom right. */
/* The jam keys and the EDITING lamp hang off the status row rather than sit in it: the lamp reads above the keys,
   in the blank the LCD has there, and the row's height — which is the machine's height — never moves for it
   (Mike, 2026-09-20: "put it above [s][m] where we have room and inside of an outline box"). */
.bb-status { position: relative; }
.bb-jam-all { position: absolute; right: 0; bottom: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.bb-jam-keys { display: flex; gap: 6px; }
/* EDITING reads under the High Score, in the line the unlock note keeps while a run is on (Mike, 2026-09-20). The
   two never show at once — one belongs to a run, the other to a jam with the tray out — so they share one slot of
   fixed height and the LCD cannot change size between them. */
.bb-note-slot { display: grid; justify-items: end; min-height: 15px; }
.bb-note-slot > * { grid-area: 1 / 1; }
.bb-editing { padding: 2px 5px; border: 1px solid currentColor; border-radius: 3px; color: #3fe0e0; font: 700 9px/1 var(--bb-readout); letter-spacing: 1.4px; text-transform: uppercase; }
.bb-jam-keys button { --key: #ffd183; width: 24px; height: 22px; padding: 0; border: 1px solid var(--key); border-radius: 3px; background: transparent; color: var(--key); font: 700 12px/1 var(--bb-readout); }
.bb-jam-keys .bb-key-mute { --key: #ff7bc8; }
.bb-jam-keys button[aria-pressed="true"] { color: #10161a; background: var(--key); }
.bb-intro-display .bb-intro-target { font-size: clamp(24px, 6vw, 36px); white-space: nowrap; }
/* The level screen's LCD reads targets, not a score: the points and the pad count in amber. */
.bb-intro-display .bb-intro-target, .bb-intro-display .bb-intro-target-label { color: #ffc46a; }
.bb-intro-display[data-cleared="true"] .bb-intro-target, .bb-intro-display[data-cleared="true"] .bb-intro-target-label { color: #5ee06a; }
/* A replay with drums the kept beat still lacks: the pads left to unlock, in amber under your best. */
.bb-intro-display .bb-intro-locked { color: #ffc46a; }
/* The drum bar: one square per drum the bar asks for, lit teal as drums lock. Squares, never words. */
.bb-drum-bar { display: inline-flex; gap: 3px; vertical-align: -1px; margin-left: 2px; }
.bb-drum-bar i { width: 7px; height: 7px; border-radius: 1.5px; background: #2c3a41; box-shadow: inset 0 0 0 1px #1f2a30; }
.bb-drum-bar i.bb-lit { background: #9ce7c9; box-shadow: 0 0 4px #9ce7c980; }
.bb-finish-readout [data-met="false"] .bb-drum-bar i { background: #5a4a2a; box-shadow: inset 0 0 0 1px #3d3220; }
.bb-finish-readout [data-met="false"] .bb-drum-bar i.bb-lit { background: #ffc46a; box-shadow: 0 0 4px #ffc46a80; }
/* The score over the target: amber chasing, green made; the target half smaller. */
.bb-score[data-state="chasing"] .bb-score-value { color: #ffc46a; }
.bb-score-of { font-size: .5em; letter-spacing: 0; opacity: .85; }
.bb-score-of[data-met] { font-size: .34em; opacity: .7; }
.bb-score[data-state="met"] .bb-score-value { color: #5ee06a; }
.bb-score-value { overflow: hidden; white-space: nowrap; color: #e8eff3; font-family: inherit; font-size: 36px; line-height: 1; font-weight: 500; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.bb-tempo { white-space: nowrap; color: #b8c9d3; font-family: var(--bb-readout); font-size: 17px; line-height: 1.2; font-variant-numeric: tabular-nums; }
/* No rule under the step row: the spacing separates it from the stage strip, and the line was one divider too many on
   a screen that already reads in bands (Mike, 2026-09-20: "remove the HR below the sequence"). */
/* Tight to the header: the LCD had about 60px with nothing in it between the Set strip and the steps and under them
   (Mike, 2026-09-25: "tighten up"). A shorter machine scales up on the phone. The steps keep 10px off the status row
   under them, so the bars never sit on the drum's name (Mike, 2026-09-25: "too tight against the row below"). */
.bb-patterns { margin: 12px 0 10px; padding-bottom: 0; }
.bb-pattern-row { display: flex; align-items: center; min-height: 42px; }
.bb-steps { display: grid; grid-template-columns: repeat(16, minmax(0, 1fr)); align-items: end; flex: 1; gap: 4px; height: 42px; }
.bb-step { min-width: 0; height: 4px; border-radius: 1px; background: #435562; }
.bb-step.bb-hit { background: #a9c2d2; box-shadow: inset 0 1px 0 #e0edf470; }
.bb-pattern-row.bb-engaged .bb-step.bb-hit { background: #3fe0e0; box-shadow: inset 0 1px 0 #c8f7f770; }
.bb-step.bb-now { background: #3fe0e0; box-shadow: 0 0 6px #3fe0e099; }
/* Last in the column and hard against the bottom (Mike, 2026-09-20: "Perc line should be flush with bottom"). The
   error line lays over it when there is one. */
/* Instrument left, the Studio readout dead centre, the lock dots right: the two outer tracks share the width equally
   so the centre is the line's centre, whatever the drum's name takes. */
.bb-status { order: 2; display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 10px; min-height: 22px; }
.bb-status .bb-progress-slot { justify-self: end; }
.bb-instrument { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #e1eaf0; font-size: 18px; line-height: 1.2; font-weight: 600; letter-spacing: -.35px; }
.bb-progress-slot { display: flex; align-items: center; gap: 10px; height: 13px; }
.bb-run-length { margin-top: 3px; color: #b8c9d3; font-family: var(--bb-readout); font-size: 10px; line-height: 13px; white-space: nowrap; }
.bb-unlock-note { color: #bac8d1; font: 10px/1.2 var(--bb-readout); white-space: nowrap; }
/* The board rules' readout, centred on the bottom line: Hot Wax's heat, One Take's drops. */
.bb-stage-readout { justify-self: center; transform-origin: center; position: relative; z-index: 1; color: #bac8d1; font: 11px/1 var(--bb-readout); white-space: nowrap; }
/* The drum-bar line and its note keep their place when a finished run takes them away. The LCD's height is what sets
   the machine's, and the fit that scales the enclosure is a function of the viewport alone — so a row that comes and
   goes resizes the whole machine on the spot (Mike, 2026-09-20: "when you finish the round it's like the device
   shrinks"). Same trick as .bb-playback[hidden]: laid out, not painted. */
.bb-run-length[hidden], .bb-unlock-note[hidden] { display: block !important; visibility: hidden; }
/* Lock progress: four LEDs, the lit ones glowing. */
.bb-lock-progress { display: flex; align-items: center; gap: 6px; height: 13px; }
.bb-lock-progress span { width: 6px; height: 6px; border-radius: 50%; background: #41525f; }
.bb-lock-progress .bb-filled { background: #b7d4e5; box-shadow: 0 0 4px #b7d4e5aa; }
.bb-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* The error line lays over the status row rather than holding a row of its own, so it can never change the machine's
   height and costs the LCD no space while there is nothing wrong (Mike, 2026-09-25: "tighten up"). It turns up
   mid-session when a kit fails to load. */
.bb-error { position: absolute; left: 14px; right: 14px; bottom: 15px; z-index: 1; height: 22px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; background: #141b21; color: #ffc0c8; font-size: 12px; line-height: 22px; }
.bb-error[hidden] { display: block !important; visibility: hidden; }

.bb-preview { display: grid; grid-template-columns: minmax(0, 1fr); }
.bb-preview > * { grid-area: 1 / 1; }
/* The token lane and the front row share one slot, and the slot is always as tall as the taller of them: a hidden
   transport used to leave the layout, so the machine shrank on the way to the levels and grew again on the way back
   (Mike, 2026-09-19). Hidden here means unseen and untouchable, not gone. */
.bb-playback[hidden] { display: block !important; visibility: hidden; }
/* Transport: play, pause, forward (next level) and eject (back to the level screen) as four rubber keys, cassette-deck style. */
.bb-playback { padding: 6px 8px 10px; }
/* Five keys across the front: Play, Beat, Mix, Library, Career (Mike, 2026-09-18). The Next key makes six when a level
   is cleared, and the row divides again rather than wrapping — nothing on this machine is ever wider than the device. */
/* Five keys, always: Next takes Career's place rather than joining the row, so the row never has to fit six
   (Mike, 2026-09-18). The six-column rule outlived that change by an hour and left five keys squeezed into six
   columns with the sixth standing empty. */
.bb-transport { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; }
.bb-career-keys.bb-transport { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 7px; }
/* More drops a second row of five under the front row: Tracks, Library, Profile, Scores, Challenge (Mike, 2026-10-08:
   "maybe we need a more... button that expands"). The tap hint and the column lights under the pads give up their
   room while it is open, so the machine does not grow (Mike, 2026-10-08: "why not cut the lights at the bottom"). */
.bb-transport-icon { width: 19px; height: 19px; fill: currentColor; }
.bb-transport-icon.bb-stroke-mark { fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* Legended keys, MPC style: a drawn mark over an engraved word. Play's legend swaps with its mark. */
.bb-key-legend { font-size: 9px; line-height: 1; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; color: #3f5566; text-shadow: 0 1px 0 #ffffffb0; white-space: nowrap; }
.bb-play[aria-pressed="true"] .bb-legend-play, .bb-play[aria-pressed="false"] .bb-legend-pause, .bb-jam-key[aria-pressed="true"] .bb-legend-play, .bb-jam-key[aria-pressed="false"] .bb-legend-pause { display: none; }
/* A finished level (its beat kept) reads Replay under a circling arrow on Play, and Play under a triangle on Jam, which
   is what that key does there (Mike, 2026-09-30). Pausing either still shows the bars. */
.bb-replay-mark { display: none; }
.bb-play[data-replay="true"][aria-pressed="false"] .bb-play-mark { display: none; }
.bb-play[data-replay="true"][aria-pressed="false"] .bb-replay-mark { display: inline; }
/* One key plays and pauses: the triangle (Jam's pads, always: Mike, 2026-10-02) shows while stopped, the bars while the beat runs (aria-pressed). */
.bb-play[aria-pressed="true"] .bb-play-mark, .bb-play[aria-pressed="false"] .bb-pause-mark, .bb-jam-key[aria-pressed="true"] .bb-play-mark, .bb-jam-key[aria-pressed="false"] .bb-pause-mark { display: none; }
.bb-transport-key:disabled, .bb-transport-key[data-locked] { color: #98a5ad; }
.bb-transport-key:disabled .bb-key-legend, .bb-transport-key[data-locked] .bb-key-legend { color: #98a5ad; }
.bb-transport-key[data-locked] { cursor: default; }
/* On the result card only, a dead key is unpowered, not just greyer lettering: the face goes dull and dark and the
   legend loses its engraving light, so a locked Jam reads as off against the black card (Mike, 2026-10-13: "it's not
   dark enough"; 2026-10-14: only there, the front row's keys stay silver). */
.bb-finish-keys .bb-transport-key:disabled, .bb-finish-keys .bb-transport-key[data-locked] { color: #7b878f; background: linear-gradient(#aeb8be, #97a3aa 60%, #86929a); box-shadow: 0 5px 0 #66767f, 0 6px 0 1px #3f4c54, 0 9px 8px #0007, inset 0 1px 0 #c5ced3, inset 0 -1px 0 #ffffff20; }
.bb-finish-keys .bb-transport-key:disabled .bb-key-legend, .bb-finish-keys .bb-transport-key[data-locked] .bb-key-legend { color: #6f7b83; text-shadow: none; }
.bb-machine[data-finished="true"] .bb-queue, .bb-machine[data-idle="true"] .bb-queue, .bb-machine[data-paused="true"] .bb-queue,
.bb-machine[data-finished="true"] .bb-columns { visibility: hidden; }
.bb-machine[data-finished="true"][data-alarm="true"] .bb-queue { visibility: visible; }
/* Held: the Pause key sits down (aria-pressed) and the board goes quiet behind it, so a paused run reads as paused
   without a panel over it (Mike, 2026-09-19). */
.bb-machine[data-paused="true"] .bb-playfield { opacity: .6; }
.bb-machine[data-alarm="true"] .bb-socket .bb-token { --coin-rgb: 255, 79, 163; }
/* The loaded slot (design/transport-queue-2026-09-10.md, queue option 1): the current token seated in a recessed well on the
   left, the next two smaller and dimmer behind chevrons that point into the well, the lane entrance labelled at the right. */
.bb-queue { display: flex; align-items: center; gap: 8px; overflow: hidden; padding: 12px 8px 13px; }
/* Synth-panel lane marks: dots that grow toward the well in each gap, chasing from gap 2 to gap 1 like step LEDs so the lane
   shows its own direction. The hold slot sits at the lane's entrance, where the Pause key was (2026-09-24). */
.bb-flow { flex-shrink: 0; width: 20px; height: 10px; fill: #aeb9bf; color: #aeb9bf; }
.bb-rail-line { fill: none; stroke: currentColor; stroke-width: .8; stroke-linecap: round; opacity: .38; }
.bb-flow circle { animation: bb-dot-chase 2.4s linear infinite; opacity: .45; }

.bb-flow-2 circle:nth-of-type(1) { animation-delay: .75s; } .bb-flow-2 circle:nth-of-type(2) { animation-delay: .9s; } .bb-flow-2 circle:nth-of-type(3) { animation-delay: 1.05s; }
.bb-flow-1 circle:nth-of-type(1) { animation-delay: 1.35s; } .bb-flow-1 circle:nth-of-type(2) { animation-delay: 1.5s; } .bb-flow-1 circle:nth-of-type(3) { animation-delay: 1.65s; }
@keyframes bb-dot-chase { 0%, 100% { opacity: .45; } 6% { opacity: 1; } 22% { opacity: .45; } }
.bb-queue-item { position: relative; }
.bb-queue-item[data-ahead="2"] { opacity: .6; }
.bb-queue-item[data-ahead="1"] { opacity: .8; }
.bb-queue-item[data-ahead="1"] .bb-token { transform: scale(.84); }
.bb-queue-item[data-ahead="2"] .bb-token { transform: scale(.7); }
/* Request Line: the two tiles behind the well are keys, full strength and full size, so the lane reads as three to choose from. */
.bb-queue-item.bb-pickable { opacity: 1; cursor: pointer; }
.bb-queue-item.bb-pickable .bb-token { transform: scale(.92); }
.bb-queue-item.bb-pickable[data-new="true"] .bb-token { animation: bb-pick-new 1s ease-in-out infinite; }
@keyframes bb-pick-new { 50% { border-color: #ffc46a; box-shadow: 0 0 14px #ffb04acc, 0 3px 0 #0c0d0e; filter: brightness(1.3); } }
@media (prefers-reduced-motion: reduce) { .bb-queue-item.bb-pickable[data-new="true"] .bb-token { animation: none; border-color: #ffc46a; box-shadow: 0 0 10px #ffb04a99; } }
/* The lane is one cut-out panel holding all three tokens: a bordered frame, slightly transparent, so the alloy reads as
   running underneath it. The current token is seated at its left edge. */
.bb-lane { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 60px; padding: 5px 5px 5px 8px; border: 1px solid #56626a; border-top-color: #3c4548; border-bottom-color: #7b878e; border-radius: 6px; background: linear-gradient(rgba(9, 12, 14, .46), rgba(16, 19, 21, .36)); box-shadow: 0 1px 0 #ffffffc4, inset 0 2px 3px #000a, inset 0 0 0 1px #0a0c0d40; }
.bb-socket { display: grid; place-items: center; }
.bb-socket .bb-token { box-shadow: inset 0 1px 1px #ffffff2e, inset 1px 0 1px #ffffff0d, inset -1px -2px 1px #000b, 0 3px 0 #0c0d0e, 0 4px 0 rgba(var(--coin-rgb), .7), 0 5px 0 1px #08090a; }
/* The badge only exists through the last measure, so it sits up over the panel edge, centred on the token. */
.bb-countdown { position: absolute; z-index: 2; top: -9px; left: 50%; transform: translateX(-50%); display: grid; place-items: center; min-width: 18px; height: 18px; padding: 0 3px; border: 1px solid #c9770f; border-radius: 2px; background: #15191c; color: #ffc46a; font-family: var(--bb-readout); font-size: 11px; line-height: 1; font-weight: 600; font-variant-numeric: tabular-nums; text-shadow: 0 0 4px #ffc46a80; box-shadow: 0 0 0 1px #0009, 0 0 6px #ffc46a33; pointer-events: none; }
/* The final beat inverts the readout window to solid amber (app.mjs pulseCount sets data-urgent). */
.bb-countdown[data-urgent="true"] { border-color: #ffe16b; background: #ffc46a; color: #2b1d07; text-shadow: none; box-shadow: 0 0 0 1px #0009, 0 0 9px #ffc46ab0; }
.bb-countdown:empty { visibility: hidden; }
/* The count-in digits use the LCD's score face (the page's Helvetica stack), not the monospace readout: Menlo's flat-topped 3 at this size reads as terminal text. */
.bb-count-in { position: absolute; z-index: 3; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #ffc46a; font-family: inherit; font-size: 104px; font-weight: 500; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -3px; text-shadow: 0 0 8px #ffd18355; pointer-events: none; }
.bb-count-in:empty { display: none; }
/* During the count-in only the big digits show; the readouts fade in over one beat once the digits clear (app.mjs lightScreen). */
.bb-screen > :not(.bb-count-in) { transition: opacity calc(var(--bb-beat, .5s) / 2) ease-out; }
.bb-screen[data-counting="true"] > :not(.bb-count-in) { opacity: 0; transition: none; }
.bb-token {
  --coin-rgb: 116, 206, 255; --pop-rgb: 116, 206, 255;
  position: relative; isolation: isolate; display: grid; place-items: center; width: 48px; height: 46px;
  border: 1px solid rgba(var(--coin-rgb), .88); border-radius: 5px;
  background: radial-gradient(ellipse at 43% 30%, #303132, #202123 65%, #18191b);
  box-shadow: inset 0 1px 1px #ffffff2e, inset 1px 0 1px #ffffff0d, inset -1px -2px 1px #000b, 0 3px 0 #0c0d0e, 0 4px 0 rgba(var(--coin-rgb), .7), 0 5px 0 1px #08090a, 0 6px 1px 1px #ffffff70;
}
.bb-token::before { content: ''; position: absolute; z-index: -1; inset: 2px 2px 3px; border-radius: 3px; background: url('./rubber-grain.svg') repeat; background-size: 112px 112px; opacity: .32; mix-blend-mode: soft-light; pointer-events: none; }
.bb-token::after { content: ''; position: absolute; left: 3px; right: 3px; bottom: -3px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, rgba(var(--coin-rgb), .12), rgb(var(--coin-rgb)) 28%, rgb(var(--coin-rgb)) 72%, rgba(var(--coin-rgb), .12)); box-shadow: 0 1px 4px rgba(var(--coin-rgb), .3); pointer-events: none; }
.bb-token[data-kind="double"] { --coin-rgb: 198, 168, 255; --pop-rgb: 198, 168, 255; }
.bb-token[data-kind="fx"] { --coin-rgb: 116, 206, 255; --pop-rgb: 116, 206, 255; }
.bb-token[data-kind="add"] { --coin-rgb: 255, 209, 131; --pop-rgb: 255, 209, 131; }
.bb-token[data-kind="subtract"] { --coin-rgb: 124, 242, 192; --pop-rgb: 124, 242, 192; }
.bb-token[data-kind="special"] { --coin-rgb: 255, 142, 211; --pop-rgb: 255, 142, 211; }
.bb-token[data-kind="fill"] { --coin-rgb: 255, 150, 60; --pop-rgb: 255, 150, 60; }
.bb-token[data-kind="wild"] { --coin-rgb: 236, 240, 243; --pop-rgb: 236, 240, 243; }
/* The Gold Wild is a Wild first: white coin like every special tile, and only the star is gold. A gold coin read as
   one more color to match, and next to the Fill's yellow it read as a Fill (Mike, 2026-10-15: "two blues just cleared
   with a yellow"). The pop stays gold: that is the ten-times moment. */
.bb-token[data-kind="gold"] { --coin-rgb: 236, 240, 243; --symbol-rgb: 255, 196, 60; --pop-rgb: 255, 196, 60; }
.bb-mult { color: #ffc43c; font-weight: 600; }
/* Color means a token you match; white means a special tile (Mike, 2026-09-24: "all special tiles should be white"),
   and red the two that destroy, the Bomb and the Drill ("bomb and drill need to be red"). */
.bb-token[data-kind="lock"], .bb-token[data-kind="dollar"], .bb-token[data-kind="hold"], .bb-token[data-kind="clean"] { --coin-rgb: 236, 240, 243; --pop-rgb: 236, 240, 243; }
/* The Brake is a grey tile: no color, nothing it matches. On the board its pad goes dark like hiss. */
.bb-token[data-kind="brake"], .bb-token[data-kind="x"], .bb-token[data-kind="mute"], .bb-token[data-kind="static"] { --coin-rgb: 150, 158, 166; --pop-rgb: 150, 158, 166; }
/* A tile that wears a number (the Timer, the Fuse; Mike, 2026-10-26): the drops it has left, in a badge on the tile's
   top right corner, lane and board alike. */
.bb-token .bb-life { position: absolute; top: -5px; right: -5px; z-index: 2; min-width: 19px; height: 19px; padding: 0 4px; box-sizing: border-box; border-radius: 10px; background: #0b0d0f; border: 1.5px solid #f2f5f7; color: #fff; font: 700 12px/16px var(--bb-readout); text-align: center; box-shadow: 0 1px 2px #000c; }
.bb-pad .bb-token .bb-life { top: 1px; right: 1px; }
.bb-token[data-kind="virus"] { --coin-rgb: 150, 230, 120; --pop-rgb: 150, 230, 120; }
/* An infected tile: a green ring and its fuse in the corner. */
.bb-pad.bb-virus { box-shadow: inset 0 0 0 2px rgba(150, 230, 120, .85); }
.bb-pad.bb-virus::after { content: attr(data-fuse); position: absolute; top: 3px; right: 5px; font: 700 11px/1 ui-monospace, Menlo, monospace; color: #9ef08a; }
/* A tile the Mute put to sleep keeps its face, drained of color, until it wakes. */
.bb-pad.bb-asleep .bb-token { opacity: .45; filter: saturate(0); }
.bb-pad.bb-braked { background: #202325; border-color: #4a4f53; }
/* Blind (16.4): the lane's two back slots are empty sockets, the tile's footprint with nothing in it. */
/* A hidden tile is the light-grey outline of a tile with a question mark in it (Mike, 2026-10-25). */
.bb-queue-item.bb-blind .bb-token-blind { background: none; border: 2px solid #c9d2d7; box-shadow: none; }
.bb-queue-item.bb-blind .bb-token-blind::before { display: none; }
.bb-queue-item.bb-blind .bb-token-blind::after { content: '?'; position: static; width: auto; height: auto; border-radius: 0; background: none; color: #c9d2d7; font: 700 24px/1 var(--bb-readout); }
.bb-token[data-kind="bomb"], .bb-token[data-kind="drill"] { --coin-rgb: 255, 72, 72; --pop-rgb: 255, 72, 72; }
.bb-symbol { position: relative; display: block; width: 43px; height: 41px; overflow: visible; }
.bb-legend-core { stop-color: color-mix(in srgb, white var(--legend-light, 70%), rgb(var(--symbol-rgb, var(--coin-rgb)))); }
.bb-legend-color { stop-color: rgb(var(--symbol-rgb, var(--coin-rgb))); }
.bb-legend-edge { stop-color: color-mix(in srgb, rgb(var(--symbol-rgb, var(--coin-rgb))) 78%, #06080a); }
.bb-legend-spill { fill: rgb(var(--symbol-rgb, var(--coin-rgb))); opacity: var(--legend-bloom, .85); }
.bb-legend-rim { fill: none; stroke: #d9dadd40; stroke-width: 1.4; stroke-linejoin: round; }
.bb-legend-socket { fill: #050607; stroke: #060708; stroke-width: 1.2; stroke-linejoin: round; }
.bb-legend-face { stroke: rgba(var(--coin-rgb), .3); stroke-width: .25; stroke-linejoin: round; }
.bb-queue .bb-symbol { width: 31px; height: 29px; }

.bb-playfield { position: relative; --bb-gap: 8px; --bb-bank-padding: 3px; --bb-tap-height: 48px; }
/* A mashup's record change (app.mjs flipWait): the board and the lane take no taps until the new groove drops, and
   they go a little darker to say so (Mike, 2026-10-26: "so you don't tap at it and think, what the hell"; "a little
   darker, not lighter": at 40% opacity the silver faded into the chrome). */
.bb-board[data-flipping] .bb-pad, .bb-queue[data-flipping] .bb-lane { filter: brightness(.72); transition: filter .3s ease; }
/* Rust's next X, the drop before it appears (core.mjs rustNext): a faint X on the grey pad it will take. */
.bb-pad.bb-rust-next .bb-token { opacity: .3; }
.bb-board { position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--bb-gap); overflow: hidden; padding: var(--bb-bank-padding); border: 1px solid transparent; border-radius: 5px; background: transparent; pointer-events: none; }
.bb-pad { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 0; aspect-ratio: 1; border: 1px solid #838788; border-top-color: #e5e6e6; border-left-color: #d8dada; border-radius: 5px; background: linear-gradient(132deg, #b9bcbd, #a2a6a8 74%, #929799); box-shadow: inset 1px 1px 1px #ffffff7a, inset -1px -3px 2px #51585a80, 0 3px 0 #363a3c, 0 4px 0 1px #171a1b, 0 5px 1px 1px #ffffff80; }
.bb-pad::before { content: ''; position: absolute; inset: 2px 2px 4px; border-radius: 3px; background: url('./rubber-grain.svg') repeat; background-size: 112px 112px; opacity: .18; mix-blend-mode: soft-light; }
/* The goal flash: a white glow under the rubber, lit by app.mjs goalFlash animating this layer's opacity. */
.bb-pad::after { content: ''; position: absolute; z-index: 3; inset: 2px 2px 4px; border-radius: 3px; background: radial-gradient(ellipse 62% 58% at 50% 46%, #ffffff 0%, #ffffffb8 34%, #ffffff33 68%, transparent 100%); opacity: 0; mix-blend-mode: screen; pointer-events: none; }
.bb-number { position: absolute; z-index: 2; top: 5px; left: 5px; color: #555d62; text-shadow: 0 1px 0 #e9eded80; font-family: var(--bb-readout); font-size: 8px; line-height: 1; font-weight: 600; letter-spacing: .8px; }
.bb-pad:has(.bb-token), .bb-pad.bb-locked, .bb-pad.bb-blocked, .bb-falling { background: #151719; border-color: #111315; border-top-color: #6c6f70; border-left-color: #747879; box-shadow: inset 0 1px 0 #060708, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 5px 1px 1px #ffffff80; }
.bb-pad:has(.bb-token)::before { content: none; }
.bb-pad .bb-token, .bb-falling .bb-token { position: absolute; inset: 0 0 3px; width: 100%; height: auto; border: 1px solid #ffffff14; border-top-color: #ffffff30; border-radius: 4px; box-shadow: inset 0 1px 1px #ffffff12, inset -1px -2px 1px #000b, 0 3px 0 #08090a; }
/* Star Power: the star pad (core.mjs placeStar) is printed with one big star in the middle of the plate, the centre
   square of a Scrabble board (Mike, 2026-10-10), a faint dark grey printed on the silver. A tile covers it the way a Scrabble tile does,
   so over a tile it shrinks to an amber star in the top right corner. */
.bb-pad:has(.bb-token) .bb-number, .bb-locked .bb-number, .bb-blocked .bb-number { color: #a3adb3; text-shadow: 0 1px 0 #08090a; }
.bb-landing { border-bottom-color: #6b8290; }
.bb-ghost { position: relative; display: flex; flex-direction: column; align-items: center; gap: 1px; color: #526673; font-size: 10px; font-weight: 500; }
/* The landing ghost: the move's pay on top, what it does in words under it (app.mjs ghostMark). */
.bb-ghost-pay { font: 600 12px/1 var(--bb-readout); letter-spacing: .02em; }
.bb-ghost-word { font-size: 10px; font-weight: 700; line-height: 1.1; white-space: nowrap; }
.bb-mark-lock { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 1.4; }
.bb-top-entry .bb-mark-lock { width: 12px; height: 12px; vertical-align: -2px; }
.bb-ghost-new { animation: bb-ghost-new 1.4s ease-in-out 1 both; }
@keyframes bb-ghost-new { 0%, 50%, 100% { transform: scale(1); color: #526673; } 25%, 75% { transform: scale(1.22); color: #8a5a00; } }
.bb-match { outline: 1px solid #657f8e; outline-offset: -3px; }
/* A tile lights amber the moment its pop begins (app.mjs resolveMove); the pop itself is a compositor-only animation. */
/* A clearing token inverts to its own color: the face fills with the kind's --pop-rgb and the legend goes dark. --pop-rgb is
   declared per kind beside --coin-rgb because the dark --coin-rgb below would otherwise be what a var() reference resolves to. */
.bb-pad.bb-popping .bb-token { background: rgb(var(--pop-rgb)); --coin-rgb: 36, 28, 48; --legend-light: 0%; --legend-bloom: 0; }
.bb-pad.bb-final { outline: 2px dashed #b17a29; outline-offset: -3px; animation: bb-lock-light 1.5s ease-in-out infinite; }
.bb-final .bb-ghost { color: #735321; font-weight: 600; }
.bb-pad.bb-locked { background: radial-gradient(ellipse at 43% 30%, #303335, #202426 70%, #171b1d); box-shadow: inset 1px 1px 1px #ffffff26, inset -1px -3px 1px #000b, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 5px 1px 1px #ffffff80; }
/* Saved drum: a step button, a hairline frame whose square LED lights while the drum sounds (design/glyphs-2026-09-10.md,
   option C). An empty frame at rest, lit and glowing on a hit, the whole mark dimmed when muted; the name beneath in the
   readout face. */
.bb-lock-icon { display: block; width: 12px; height: 12px; margin-bottom: 5px; fill: none; stroke: #b7d4e5; stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; }
.bb-lock-icon .bb-lock-led { fill: none; stroke: none; }
/* A drum not landed yet: the grey plate, the lock outline and the name dim on it. A tap opens the level screen. */
.bb-pad.bb-ghost-pad { cursor: pointer; }
.bb-ghost-pad .bb-lock-icon { stroke: #646b6e; stroke-dasharray: 2 1.5; }
.bb-ghost-pad .bb-layer { color: #646b6e; text-shadow: 0 1px 0 #e9eded80; }
.bb-pad.bb-ghost-pad:focus-visible { outline: 2px solid #ffd183; outline-offset: -2px; }
/* A given Break, Fill or effect pad on a jam whose board is not closed waits, grey as a ghosted drum, until the close
   (Mike, 2026-10-24: "close the board in order to enable whatever that is"). */
.bb-pad.bb-extra-wait { filter: grayscale(1); opacity: .5; cursor: pointer; }
.bb-pad.bb-extra-wait .bb-layer, .bb-pad.bb-extra-wait .bb-jam-mark { color: #646b6e; }
.bb-locked.bb-sounding .bb-lock-icon { stroke: #e2f5ff; filter: drop-shadow(0 0 3px #b7d4e5); }
.bb-locked.bb-sounding .bb-lock-icon .bb-lock-led { fill: #e2f5ff; }
.bb-layer { position: relative; color: #dce3e7; font: 600 10px/1 var(--bb-readout); letter-spacing: 1px; text-transform: uppercase; }
.bb-pad.bb-locked.bb-sounding { background: radial-gradient(ellipse at 50% 75%, #3b4850, #242a2f 75%); border-color: #90a3af; box-shadow: inset 0 1px 1px #d8e4ed40, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 5px 1px 1px #ffffff80; }
.bb-pad.bb-blocked, .bb-falling.bb-blocked { background: repeating-linear-gradient(135deg, transparent 0 5px, #ffffff05 5px 6px), #25282b; border-color: #64696c; }
/* Tape Hiss (stage 19 on): a tile gone to hiss is a blank grey coin, the token's shape with no kind, on a plain dark pad
   so it reads as dead rather than struck out like an X. Tiles dim over their last six drops before they turn, counted
   as drops left (core.mjs HISS_DROPS) so the fade follows the clock: keyed on age, it stopped when the clock doubled. */
.bb-pad.bb-hissed { background: #202325; border-color: #4a4f53; }
.bb-hiss { width: 70%; height: 70%; border-radius: 5px; background: repeating-linear-gradient(135deg, #3a3e42 0 2px, #2b2f33 2px 4px); border: 1px solid #555a5f; box-shadow: inset 0 1px 1px #ffffff14, 0 2px 0 #0c0d0e; }
.bb-pad.bb-popping .bb-hiss { background: #9aa3aa; }
.bb-pad:is([data-left="6"],[data-left="5"]) .bb-token { opacity: .78; filter: saturate(.75); }
.bb-pad:is([data-left="4"],[data-left="3"]) .bb-token { opacity: .58; filter: saturate(.45); }
.bb-pad:is([data-left="2"],[data-left="1"]) .bb-token { opacity: .4; filter: saturate(.15); }
/* The falling piece is the pad tile it becomes (dark pad, flat token, striped X), so nothing changes at touchdown. */
.bb-falling { position: absolute; z-index: 3; display: grid; place-items: center; border: 1px solid; border-radius: 5px; pointer-events: none; }
.bb-points { position: absolute; z-index: 5; top: 12%; bottom: 12%; left: 0; right: 0; overflow: hidden; text-align: center; pointer-events: none; color: #fff0d6; font-family: var(--bb-readout); font-weight: 750; letter-spacing: -1.5px; text-shadow: 0 3px 0 #16212b, 0 0 8px #16212b, 0 0 18px #16212b, 0 0 30px #f6a44570; }
.bb-reward { position: absolute; top: 50%; left: 0; right: 0; font-size: clamp(36px, 10vw, 44px); line-height: 1; }
/* Loop Pedal: the board's two side edges are drawn as a cut, the same dashed line on both, so the first and last columns
   read as joined round the back. */
.bb-machine[data-loop="true"] .bb-board { border-left: 1px dashed #6b7c86; border-right: 1px dashed #6b7c86; }
.bb-columns { position: absolute; z-index: 4; inset: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); padding: 0 calc(var(--bb-bank-padding) - var(--bb-gap) / 2 + 1px); }
.bb-column { position: relative; display: flex; align-items: flex-end; justify-content: center; min-width: 0; padding: 0 0 20px; border: 0; border-radius: 4px; background: transparent; }
/* The ↓1 marker rides the top edge of a full column, half in the bank padding above the first row, and adds no height:
   one match from above locks a closed column's parked token, and clears any other top tile. It lights amber when the tile in hand can take it. */
.bb-top-entry { position: absolute; top: -6px; left: 50%; transform: translateX(-50%); width: 30px; height: 16px; border: 1px solid #717575; border-radius: 3px; background: #a5aaaa; box-shadow: inset 0 1px #e9eded99; color: #344550; font: 600 11px/14px var(--bb-readout); text-align: center; }
.bb-top-entry[data-ready="true"] { border-color: #9a742f; background: #e4c384; color: #4a391c; box-shadow: 0 0 5px #ffd18380, inset 0 1px #fff6; }
/* Through stage 1 and on a last chance, a match from above flashes: the marker and the top pad it lands on (app.mjs teachTop). */
.bb-top-entry[data-flash="true"] { animation: bb-top-flash .6s ease-in-out infinite; }
@keyframes bb-top-flash { 50% { background: #ffb04a; border-color: #fff1d6; box-shadow: 0 0 12px #ffb04a; } }
.bb-pad.bb-top-flash { animation: bb-top-pad-flash .6s ease-in-out infinite; }
@keyframes bb-top-pad-flash { 50% { box-shadow: inset 0 0 0 2px #ffc46a, inset -1px -3px 2px #51585a80, 0 3px 0 #363a3c, 0 4px 0 1px #171a1b, 0 0 14px 2px #ffb04acc; } }
@media (prefers-reduced-motion: reduce) { .bb-top-entry[data-flash="true"], .bb-pad.bb-top-flash { animation: none; } .bb-pad.bb-top-flash { box-shadow: inset 0 0 0 2px #ffc46a, 0 0 10px #ffb04a99; } }
.bb-tap-indicator { position: relative; display: block; width: 31px; height: 8px; border-radius: 5px; background: #666f74; box-shadow: inset 0 1px 2px #20282d80, 0 1px 0 #f2f4f4; }
.bb-tap-indicator::after { content: ''; position: absolute; inset: 3px 10px; border-radius: 1px; background: #3d494f; }
.bb-selected .bb-tap-indicator { background: #60849a; }
.bb-selected .bb-tap-indicator::after { background: #e1f2fa; box-shadow: 0 0 4px #d6eaff; }
.bb-column:disabled .bb-tap-indicator { opacity: .4; }
/* The column button is an invisible strip: it never draws a box. The tap indicator below the pad carries hover, focus and press. */
.bb-column:focus-visible .bb-tap-indicator, .bb-column:not(:disabled):active .bb-tap-indicator { background: #60849a; }
.bb-column:focus-visible .bb-tap-indicator::after, .bb-column:not(:disabled):active .bb-tap-indicator::after { background: #e1f2fa; box-shadow: 0 0 4px #d6eaff; }
.bb-controls { position: relative; height: var(--bb-tap-height); }
.bb-end { position: absolute; inset: 0 8px; display: grid; grid-template-columns: minmax(0, 1fr); padding-top: 3px; }
/* Share Beat sits alone in the 48px tap lane, so it is shorter than a dialog key. */
.bb-end .bb-share { min-height: 40px; align-self: start; padding-block: 8px; }
/* The hint line sits between the token lane and the pads, so the lane under the board belongs to Share alone. */
/* One line, always. A hint that wrapped to two made the machine 16px taller, and the fit only ever scales down within
   a viewport, so the device stayed shrunk for the rest of the session (Mike, 2026-09-19: "Jungle is a smaller device
   than Motown"). The line is the machine's own, not a paragraph. */
.bb-tap-hint { min-height: 18px; margin: -4px 0 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center; color: #5f7682; font-size: 13px; line-height: 18px; }
/* A hidden hint keeps its line: it blanks while the board clears and returns in Free Play, and collapsing it made the
   machine shrink and grow again at the end of a run (Mike, 2026-09-13). */
.bb-tap-hint[hidden] { display: block !important; visibility: hidden; }
/* A long press on a phone selected the row it landed on — the name, the BPM, the key beside it — and left the blue
   highlight sitting on the machine (Mike, 2026-09-19). The machine is an object, not a document: nothing in it selects
   except the text that is there to be copied, and the fields you type into. */
#bb-pairs { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
#bb-pairs input, #bb-pairs textarea, .bb-career-share-text, .bb-career-rules { -webkit-user-select: text; user-select: text; }
.bb-pad.bb-toggle { cursor: pointer; pointer-events: auto; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
/* A held pad solos its drum: yellow, like the Add token, for as long as the finger stays down. */
.bb-pad.bb-locked.bb-solo { background: radial-gradient(ellipse at 50% 75%, #6a5322, #3a2f14 75%); border-color: #ffd183; box-shadow: inset 0 0 0 2px #ffd183, inset 0 1px 1px #ffe7b840, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 5px 1px 1px #ffffff80; }
.bb-pad.bb-solo .bb-layer, .bb-pad.bb-solo .bb-number { color: #ffd183; }
/* Soloing turns the lock icon yellow, outline and the LED that follows the hits alike. */
.bb-pad.bb-locked.bb-solo .bb-lock-icon { stroke: #ffd183; filter: none; }
.bb-pad.bb-locked.bb-solo.bb-sounding .bb-lock-icon .bb-lock-led { fill: #ffd183; }
.bb-pad.bb-toggle:focus-visible { outline: none; }
/* Free Play jam pads: the board's empty pads become the two fills (green, two bars small and three big), a dark pad with the
   color on its rim, mark and legend. */
/* A token pad in Free Play wears the token as a run's pad would; its tap mutates the beat instead of dropping. */
/* A Fill and a Break landed on the board wear the Free Play fill pad's coin, in their own color: green for a fill,
   pink for a break (Mike, 2026-09-20). They are locked pads -- they count toward a full house and stay with the beat
   -- but they are not drums, so they carry the bar marks rather than the padlock. */
.bb-pad.bb-extra { --coin-rgb: 94, 224, 106; cursor: pointer; pointer-events: auto; -webkit-user-select: none; user-select: none; background: radial-gradient(ellipse at 43% 30%, #262a2c, #1a1e20 70%, #131617); border-color: rgba(var(--coin-rgb), .75); border-top-color: rgba(var(--coin-rgb), .95); border-left-color: rgba(var(--coin-rgb), .9); box-shadow: inset 0 0 0 1px rgba(var(--coin-rgb), .35), inset 1px 1px 1px #ffffff1a, inset -1px -3px 1px #000b, 0 3px 0 #08090a, 0 4px 0 1px rgba(var(--coin-rgb), .55), 0 5px 1px 1px #ffffff70; }
.bb-pad.bb-extra-break { --coin-rgb: 255, 123, 200; }
/* Effect pads (Reverb, Delay, Drive, Sweep) wear the Engineer's blue, and dim when tapped off. */
.bb-pad.bb-extra-fx { --coin-rgb: 110, 170, 255; }
.bb-pad.bb-extra-fx.bb-fx-off { opacity: .45; }
.bb-pad.bb-extra .bb-number { color: rgba(var(--coin-rgb), .8); text-shadow: 0 1px 0 #08090a; }
.bb-pad.bb-extra .bb-layer { color: rgb(var(--coin-rgb)); padding-left: 1px; }
.bb-jam-mark { display: flex; gap: 3px; margin-bottom: 6px; }
.bb-break-mark { width: 16px; height: 14px; fill: none; stroke: rgb(var(--coin-rgb)); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 0 1px rgba(var(--coin-rgb), .4)); }
.bb-jam-mark i { display: block; width: 3px; height: 12px; border-radius: 1px; background: rgb(var(--coin-rgb)); box-shadow: 0 0 2px rgba(var(--coin-rgb), .3); }
.bb-pad.bb-extra:active { transform: translateY(2px); box-shadow: inset 0 0 0 1px rgba(var(--coin-rgb), .6), inset 0 0 12px rgba(var(--coin-rgb), .35), 0 1px 0 #08090a, 0 2px 0 1px rgba(var(--coin-rgb), .55); }
.bb-pad.bb-jam:focus-visible { outline: 2px solid rgb(var(--coin-rgb)); outline-offset: -3px; }
.bb-intro-career:focus, .bb-intro-rules:focus { outline: none; }
.bb-pad.bb-locked.bb-muted { background: #121416; border-top-color: #3a3e40; border-left-color: #3a3e40; box-shadow: inset 0 2px 5px #0009; }
.bb-pad.bb-muted .bb-layer, .bb-pad.bb-muted .bb-lock-icon { opacity: .35; }
/* The bench's engaged drum, the one the beat tray edits: a stripe along the pad's top edge and its number, a shape apart from a solo's filled face. */
/* The bench's selected drum, the one the beat tray edits: green. A solo stays amber, a fill pink. */
.bb-pad.bb-locked.bb-engaged { border-color: #3fe0e0; box-shadow: inset 0 0 0 3px #3fe0e0, inset 0 0 12px #3fe0e055, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 0 10px #3fe0e066; }
.bb-pad.bb-engaged .bb-number, .bb-pad.bb-engaged .bb-layer { color: #3fe0e0; }
.bb-pad.bb-locked.bb-engaged .bb-lock-icon { stroke: #3fe0e0; filter: none; }
.bb-pad.bb-locked.bb-engaged.bb-sounding .bb-lock-icon .bb-lock-led { fill: #3fe0e0; }
/* Selected and soloed at once stays green: the lit S key and the dimmed pads say solo. */
.bb-pad.bb-locked.bb-engaged.bb-solo { background: radial-gradient(ellipse at 43% 30%, #303335, #202426 70%, #171b1d); border-color: #3fe0e0; box-shadow: inset 0 0 0 3px #3fe0e0, inset 0 0 12px #3fe0e055, 0 3px 0 #08090a, 0 4px 0 1px #0c0e0f, 0 0 10px #3fe0e066; }
.bb-pad.bb-engaged.bb-solo .bb-layer, .bb-pad.bb-engaged.bb-solo .bb-number { color: #3fe0e0; }
.bb-pad.bb-locked.bb-engaged.bb-solo .bb-lock-icon { stroke: #3fe0e0; }
.bb-pad.bb-locked.bb-engaged.bb-solo.bb-sounding .bb-lock-icon .bb-lock-led { fill: #3fe0e0; }
.bb-dialog-action { position: relative; isolation: isolate; display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; min-width: 0; padding: 10px 12px; border: 1px solid #5b6063; border-top-color: #8a8e90; border-bottom-color: #111416; border-radius: 4px; background: linear-gradient(145deg, #36393b, #202325 78%); color: #e0e5e8; box-shadow: 0 3px 0 #111416, 0 4px 0 #ffffff60, inset 0 1px 1px #ffffff26, inset 0 -2px 1px #0008; font-size: 14px; font-weight: 600; }
.bb-intro-start, .bb-stage-start, .bb-new-next, .bb-finish-primary { border-top-color: #afb9be; color: #eff5f8; }
.bb-dialog-action::after { content: ''; position: absolute; z-index: -1; inset: 2px; border-radius: 2px; background: url('./rubber-grain.svg') repeat; background-size: 112px 112px; opacity: .25; mix-blend-mode: soft-light; pointer-events: none; }

.bb-dialog { width: min(400px, calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 40px); padding: 0; overflow: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #71818b #c8d0d5; border: 1px solid #edf2f4; border-radius: 8px; background: var(--bb-metal); color: #25313a; box-shadow: inset 0 1px 0 #fff, 0 5px 0 #7d8d98, 0 8px 0 #253139, 0 25px 70px #081015b0; }
.bb-dialog::backdrop { background: #141e27b0; backdrop-filter: blur(3px); }
.bb-dialog[open] { animation: bb-dialog-enter 380ms cubic-bezier(.3, .7, .4, 1); }
.bb-dialog[data-enter="side"][open] { animation: bb-dialog-enter-side 380ms cubic-bezier(.3, .7, .4, 1); }
.bb-dialog-heading { position: sticky; z-index: 1; top: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 20px; border-bottom: 1px solid #7d909e70; background: #c8d1d7; }
.bb-dialog-heading h1 { margin: 0; font-size: 23px; line-height: 1.2; font-weight: 650; letter-spacing: -.5px; }
.bb-dialog-close, .bb-rules-close, .bb-share, .bb-transport-key { flex-shrink: 0; min-height: 36px; padding: 6px 10px; border: 1px solid #9eadb6; border-bottom-color: #718591; border-radius: 4px; background: linear-gradient(#d7e0e5, #b6c3cc); color: #2e4555; font-size: 12px; font-weight: 600; box-shadow: 0 2px 0 #8395a1, inset 0 1px 0 #f5f9fc; }
.bb-dialog-body { padding: 18px 20px 20px; }
/* The intro LCD is the main display's rows: genre and tempo on the left, the target number where the score goes with its label right under it, then the divider and the kit name where the moves go. */
.bb-intro-display .bb-groove, .bb-finish-readout .bb-groove { grid-template-columns: minmax(0, 1fr) auto; }
.bb-intro-display .bb-groove { align-items: start; }
/* Where a level asks drums, they sit left of the points as the goal's other half (app.mjs openIntro). */
.bb-intro-display .bb-groove:has(.bb-intro-pads:not([hidden])):has(.bb-score:not(.bb-intro-pads):not([hidden])) { grid-template-columns: minmax(0, 1fr) auto auto; column-gap: 26px; }
.bb-intro-display .bb-intro-pads .bb-score-value { font-size: clamp(24px, 6vw, 36px); white-space: nowrap; }
.bb-intro-display .bb-intro-pads, .bb-intro-display .bb-intro-pads .bb-score-value, .bb-intro-display .bb-intro-pads .bb-lcd-note { color: #ffc46a; }
/* The level screen and the result card have room for the whole name: it wraps rather than being cut (Mike,
   2026-09-26: "title gets cut off", then "STILL cut off on after panel"). */
.bb-intro-display .bb-genre, .bb-finish-readout .bb-genre { white-space: normal; text-wrap: balance; }
/* Level, target and the finish verdict are engraved into the heading plate like the Drum Kit label: dark ink, a light lower edge, a faint dark upper edge. */
.bb-intro-title, .bb-finish-title { color: #3a4244; text-shadow: 0 1px 0 #ffffffb8, 0 -.5px 0 #30343565; }
/* The level screen's heading is a label, not a title: the stage's name and the level, small (Mike, 2026-09-18). */
.bb-dialog-heading h1.bb-intro-title, .bb-dialog-heading h1.bb-finish-title { font-size: 15px; font-weight: 600; letter-spacing: .06em; }
/* The result card wears the level screen's header, Set 2 · Track 3, with MISSED on the right in the header's own ink
   (Mike, 2026-09-26: "missed shouldn't be in red"); a pass shows no verdict, only the Share key. */
.bb-dialog-heading h1.bb-finish-title { display: flex; flex: 1; align-items: baseline; justify-content: space-between; gap: 12px; min-width: 0; }
.bb-finish-verdict { text-transform: uppercase; letter-spacing: .1em; }
/* Embossed rule between the readouts and the pads on the intro and results screens, with air on both sides. */
/* Both bodies are a column that sizes to what it holds. The intro used to be pinned to 348px so it matched the results
 * panel when it still carried the rule text and four keys; with one key under the display that left a hand's width of
 * empty silver under it (Mike, 2026-09-18: "it's a fucking mess"). */
.bb-intro-dialog .bb-dialog-body, .bb-finish-dialog .bb-dialog-body { display: flex; flex-direction: column; box-sizing: border-box; }
.bb-intro-start, .bb-stage-start, .bb-new-next, .bb-finish-primary, .bb-finish-second { position: relative; min-height: 48px; }
.bb-intro-start, .bb-stage-start, .bb-new-next { width: 100%; margin-top: 22px; }
.bb-intro-start:focus-visible, .bb-finish-primary:focus-visible { outline: none; }
.bb-intro-start:focus-visible::after, .bb-finish-primary:focus-visible::after { content: ''; position: absolute; bottom: 5px; left: calc(50% - 10px); width: 20px; height: 2px; border-radius: 1px; background: #d5edf7; box-shadow: 0 0 4px #d5edf766; }
.bb-intro-error, .bb-finish-error { margin: 16px 0 0; color: #8d3343; font-size: 13px; line-height: 1.5; }
.bb-rules-heading { position: sticky; z-index: 1; top: 0; display: grid; gap: 14px; padding: 16px 18px; border-bottom: 1px solid #8297a570; background: #c8d1d7; box-shadow: 0 1px 0 #eef4f870; }
.bb-rules-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bb-rules-heading h1 { margin: 0; color: #3e5564; font-size: 20px; line-height: 1.2; font-weight: 650; letter-spacing: -.5px; text-shadow: 0 1px 0 #eef5f8; }
.bb-rules-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px; border: 1px solid #8295a2; border-radius: 5px; background: #9eadb7; box-shadow: inset 0 1px 3px #4d637066; }
.bb-rule-tab { position: relative; min-height: 40px; padding: 8px; border: 1px solid transparent; border-radius: 3px; background: transparent; color: #3a5262; font-size: 13px; font-weight: 600; }
.bb-rule-tab[aria-selected="true"] { border-color: #657d8c; background: linear-gradient(#3a4e5c, #2b3e4b); color: #e5f0f8; box-shadow: 0 1px 2px #18293366; }
.bb-rule-tab[aria-selected="true"]::after { content: ''; position: absolute; bottom: 3px; left: calc(50% - 7px); width: 14px; height: 2px; border-radius: 1px; background: var(--bb-amber); }
.bb-rules-body { padding: 18px 18px 24px; }
.bb-rule-pane:focus-visible { outline: none; }
.bb-how-to { margin: 0; padding-left: 21px; color: #405767; font-size: 13px; line-height: 1.6; }
.bb-how-to li { padding-left: 3px; margin-bottom: 10px; }
.bb-how-to li::marker { color: #4f6675; font-weight: 700; }
.bb-how-to strong { color: #243e50; font-weight: 600; }
.bb-rules-note { margin: 12px 0 0; color: #465e6e; font-size: 12px; line-height: 1.6; }
.bb-scoring-table { width: 100%; margin: 0; border-collapse: collapse; font-size: 13px; line-height: 1.4; }
.bb-scoring-table caption, .bb-rule-section { padding: 0; margin: 0 0 10px; text-align: left; color: #2e4555; font-size: 18px; line-height: 1.3; font-weight: 650; letter-spacing: -.2px; }
.bb-scoring-table thead { color: #526b7a; font-size: 11px; }
.bb-scoring-table th, .bb-scoring-table td { padding: 9px 0; border-bottom: 1px solid #8297a560; }
.bb-scoring-table th { text-align: left; font-weight: 500; }
.bb-scoring-table thead th { padding-top: 0; padding-bottom: 8px; font-weight: 600; }
.bb-scoring-table th:last-child, .bb-scoring-table td { text-align: right; }
.bb-scoring-table td { padding-left: 16px; color: #725320; font-family: var(--bb-readout); font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bb-scoring-table th span { display: block; margin-top: 2px; color: #526b7a; font-size: 11px; }
.bb-scoring-table .bb-score-bonus th, .bb-scoring-table .bb-score-bonus td { border-top: 1px solid #8297a5; }
.bb-chain-example { display: flex; flex-wrap: wrap; align-items: baseline; gap: 7px 12px; margin: 16px 0 0; font-family: var(--bb-readout); font-size: 15px; font-weight: 600; color: #465e6e; }
.bb-chain-example strong { color: #725320; }
.bb-rule-section { margin-top: 24px; }
.bb-token-guide { margin: 0; }
.bb-token-guide > div { display: grid; grid-template-columns: 140px minmax(0, 1fr); align-items: center; gap: 10px; padding: 9px 0; }
.bb-token-guide dt { display: flex; align-items: center; gap: 10px; color: #2e4555; font-size: 12px; font-weight: 600; }
.bb-token-guide dd { margin: 0; color: #465e6e; font-size: 12px; line-height: 1.5; }
.bb-token.bb-rule-symbol { flex: 0 0 34px; width: 34px; height: 34px; border-radius: 4px; }
/* The Locked row sits in the symbol column without a token face: the padlock alone, the Drums tab's own icon. */
.bb-rule-lock { flex: 0 0 34px; display: grid; place-items: center; width: 34px; height: 34px; color: #2e4555; }
.bb-rule-lock svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.bb-rule-lock svg [fill="currentColor"] { stroke: none; }
.bb-rule-symbol .bb-symbol { width: 26px; height: 24px; }
/* The results LCD is a .bb-screen with the main display's own rows; only the LED strip is replaced by its divider. */
/* No Final label and no Level here (the heading has it): the score rises to the top of the row and the verdict sits right under it, in the main display's target face. */
.bb-lcd-note { color: #bac8d1; font: 11px/1.4 var(--bb-readout); text-align: right; }
/* The campaign on the screen: a strip of this stage's seven levels under the objective, and the full map in its place
   when the strip is tapped (Mike, 2026-09-17). Chip states: kept (the beat is yours), cleared, open, locked. */
/* Empty: the row holds its place so the LCD is the same height before and after a run (app.mjs, renderMap).
   visibility also takes the key out of the tab order and the accessibility tree. */
.bb-stage-strip.bb-empty { visibility: hidden; }
/* Under the rule that closes the step row, not above it: the strip is about where you are in the campaign, which
   belongs with the readouts at the foot of the screen rather than between the tempo and the beat (Mike, 2026-09-20:
   "put the stage 5 line just below the hr"). */
/* The stage sits under the tempo, left, and the drum bar under the score, right (Mike, 2026-09-20): the strip's own
   row is gone, and with it the Levels key it carried -- the front key says Levels now. The strip still opens the tree. */
.bb-stage-strip { display: flex; align-items: center; gap: 8px; width: max-content; margin: 2px 0 0; padding: 0; border: 0; background: none; color: inherit; font: inherit; }
.bb-stage-chips { display: flex; width: 72px; gap: 4px; }
.bb-stage-chips .bb-map-chip { height: 6px; }
.bb-stage-strip-label { flex: none; color: #bac8d1; font: 11px/1 var(--bb-readout); white-space: nowrap; }
.bb-stage-strip:not(:disabled):active .bb-map-chip { filter: brightness(1.35); }
.bb-map-chip { flex: 1; min-width: 0; height: 8px; border: 0; border-radius: 2px; padding: 0; background: #2a3439; box-shadow: inset 0 0 0 1px #0007; }
.bb-map-chip[data-state="kept"] { background: #3fe0e0; box-shadow: 0 0 5px #3fe0e088; }
.bb-map-chip[data-state="cleared"] { background: #2f6b70; }
.bb-map-chip[data-state="open"] { background: #3d4a51; }
.bb-map-chip[data-current="true"] { background: #ffc46a; box-shadow: 0 0 6px #ffc46aaa; }
/* The map itself: four stages, seven keys each, the level you are on marked. */
.bb-map { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 0; min-height: 0; }
/* The list's box runs out to the screen's edges, with its own gutters inside, so a run's band can reach past the tiles
   without being clipped (the rows clip what overflows). */
.bb-map-rows { display: grid; align-content: start; gap: 0; min-height: 0; margin: 0 calc(0px - var(--bb-side)); padding: 6px var(--bb-side) 14px; scrollbar-width: thin; scrollbar-color: #3c4b54 transparent; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; -webkit-mask-image: linear-gradient(transparent, #000 8px, #000 calc(100% - 5px), transparent); mask-image: linear-gradient(transparent, #000 8px, #000 calc(100% - 5px), transparent); }
/* The Levels tree: a stage is a header line and a 2x2 of tiles, one a level, the shape of the pad board. A tile is the
   groove's name over its pad LEDs. Cyan is unlocked, as in the Library; white is a level whose pads are still needed;
   amber is Up next; an amber edge is the board on the machine. */
/* Up next is a section of its own at the head of the list, not a row: the groove's name large, the set and target
   under it, and a Play bar the width of the screen (Mike, 2026-10-30: "the little play button wasn't obvious enough").
   The Next key on the transport opens it. */
.bb-map-next { display: grid; font-family: inherit; gap: 6px; padding: 12px 12px 10px; margin: 0 0 6px; border: 1px solid #5a4218; border-radius: 3px; background: #241d14; color: #ffc46a; text-align: left; }
.bb-map-next-label { color: #c79a52; font: 700 9px/1.2 var(--bb-readout); letter-spacing: 1px; text-transform: uppercase; }
.bb-map-next-name { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow-wrap: anywhere; color: #ffe0ad; font-weight: 700; font-size: 26px; line-height: 1.1; letter-spacing: -.4px; }
.bb-map-next-detail { color: #e2b876; font: 600 11px/1.35 var(--bb-readout); letter-spacing: .3px; }
.bb-map-next-go { display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; margin-top: 4px; border-radius: 3px; background: #ffc46a; color: #2a1a05; font: 700 13px/1 var(--bb-readout); letter-spacing: 1.6px; text-transform: uppercase; }
.bb-map-next-go svg { width: 16px; height: 16px; }
.bb-map-rows::-webkit-scrollbar { width: 4px; }
.bb-map-rows::-webkit-scrollbar-thumb { border-radius: 2px; background: #3c4b54; }
/* A run's row is a band: a heavier rule above, room above it, the name in white (Mike, 2026-10-27: "a little more space
   between sets and clearer stage separation"). The first run on the list sits flush under Up next. */
.bb-map-run { display: flex; align-items: baseline; justify-content: space-between; margin-top: 22px; padding: 12px 0 10px; border-top: 2px solid #3a4852; color: #7d8990; font: 700 10px/1.2 var(--bb-readout); letter-spacing: 1.2px; text-transform: uppercase; }
.bb-map-run b { color: #eef3f5; font-size: 17px; letter-spacing: 1.6px; font-weight: 700; }
.bb-map-run[data-ahead] { color: #4f5a60; }
.bb-map-run[data-ahead] b { color: #6b7980; }
/* A run's block wears its own color, a wash that starts under the run's row and is gone by its second set (Mike,
   2026-10-27: "a subtle gradient down from the top, different colors for each stage"). The colors are picked here, not
   offered: Collector blue-grey, Creator amber, Arranger green, Producer violet. */
/* The wash and its rule reach out to 4px short of the screen's edge, past the list's own gutter. */
.bb-map-run-block { --run-rgb: 126, 170, 196; margin: 22px calc(4px - var(--bb-side)) 0; padding: 0 calc(var(--bb-side) - 4px) 6px; border-radius: 0 0 3px 3px; background: linear-gradient(rgba(var(--run-rgb), .16), rgba(var(--run-rgb), .06) 45%, transparent); }
.bb-map-run-block .bb-map-run { margin-top: 0; }
.bb-map-run-block:first-child { margin-top: 2px; }
.bb-map-next + .bb-map-run-block { margin-top: 10px; }
.bb-map-run-block[data-run="2"] { --run-rgb: 255, 196, 106; }
.bb-map-run-block[data-run="3"] { --run-rgb: 118, 214, 166; }
.bb-map-run-block[data-run="4"] { --run-rgb: 196, 150, 255; }
.bb-map-run-block .bb-map-run { border-top-color: rgba(var(--run-rgb), .45); }
.bb-map-run-block .bb-map-run b { color: rgb(var(--run-rgb)); }
/* Each set's head stands off the four tiles above it by a hairline and a gap; under its run's row it sits close. */
.bb-map-stage { display: flex; align-items: baseline; margin-top: 12px; padding: 10px 0 5px; border-top: 1px solid #1f272c; color: #bac8d1; font: 11px/1.4 var(--bb-readout); letter-spacing: 1.2px; text-transform: uppercase; }
.bb-map-run + .bb-map-stage { margin-top: 0; padding-top: 4px; border-top: 0; }
.bb-map-stage em { flex: none; min-width: 14px; margin-right: 6px; color: #7d8990; font-style: normal; }
.bb-map-four { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 4px; }
.bb-map-level { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 6px; min-height: 46px; padding: 6px 7px; border: 0; border-radius: 3px; background: #182027; box-shadow: inset 0 0 0 1px #0007; color: #e8eff3; font-family: inherit; text-align: left; }
.bb-map-level:not(:disabled):active { filter: brightness(1.3); }
.bb-map-level-name { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow-wrap: anywhere; font-weight: 600; font-size: 13px; line-height: 1.15; letter-spacing: -.2px; } /* A style's whole name, on two lines when a pad is narrow: Blue Tuesday, not Blue Tu... (Mike, 2026-09-23). */
.bb-tile-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 12px; }
.bb-tile-leds { display: inline-flex; gap: 2px; }
/* An instrument not yet unlocked is an open box, so what is left to fill reads at a glance (Mike, 2026-10-10: "open boxes
   where it's not filled"). */
.bb-tile-leds i { width: 5px; height: 5px; border-radius: 1px; background: transparent; box-shadow: inset 0 0 0 1px #7d8990; }
.bb-tile-leds i.bb-lit { background: #9ce7c9; box-shadow: 0 0 3px #9ce7c980; }
.bb-tile-icon { flex: none; width: 12px; height: 12px; }
.bb-tile-word { color: #7d8990; font: 9px/1 var(--bb-readout); letter-spacing: .4px; text-transform: uppercase; }
.bb-map-level[data-state="kept"] { background: #182a2f; box-shadow: inset 0 0 0 1px #3a5560; color: #ffffff; }
.bb-map-level[data-state="kept"][data-full="true"] { background: #0f2a2c; box-shadow: inset 0 0 0 1px #1f5558; color: #3fe0e0; }
.bb-map-level[data-state="kept"]:not([data-full]) .bb-tile-leds i.bb-lit { background: #ffffff; box-shadow: 0 0 3px #ffffff80; }
.bb-map-level[data-state="cleared"] { box-shadow: inset 0 0 0 1px #8fa3b0; color: #ffffff; }
.bb-map-level[data-state="cleared"] .bb-tile-icon { color: #ffffff; }
.bb-map-level[data-state="cleared"] .bb-tile-leds i.bb-lit { background: #ffffff; box-shadow: 0 0 3px #ffffff80; }
.bb-map-level[data-state="locked"] { color: #7d8990; }
/* The level on the machine wears the amber tile too, not just a spine down its edge (Mike, 2026-09-20): a level
   replayed from the Library sits far from Up next, and a 2px mark was not enough to find yourself by. The spine
   stays, so here and Up next still read apart when they are two different tiles. */
/* Up next is the amber tile; the board on the machine is an amber edge and an amber Play on its own color, so the
   two never read as two selections (Mike, 2026-09-22: "why both are selected"). */
.bb-map-level[data-next="true"] { background: #241d14; box-shadow: inset 0 0 0 1px #6b4e1a, 0 0 8px #ffc46a30; color: #ffc46a; }
.bb-map-level[data-here="true"] .bb-tile-icon { color: #ffc46a; }
.bb-map-level[data-here="true"]::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 2px; border-radius: 1px; background: #ffc46a; }
/* The Campaign view in the Library: the whole campaign as a grid of chips, four to a stage. */
.bb-map-grid { display: grid; gap: 4px; }
.bb-map-row { display: flex; align-items: center; gap: 8px; }
.bb-map-stage-no { flex: none; width: 10px; color: #7d8990; font: 700 11px/1 var(--bb-readout); }
.bb-map-chips { display: flex; flex: 1; gap: 4px; }
.bb-map-name { flex: none; width: 88px; color: #bac8d1; font: 11px/1.2 var(--bb-readout); }
.bb-map-row .bb-map-chip { height: 24px; border-radius: 3px; color: #8fa3b0; font: 700 11px/1 var(--bb-readout); }
.bb-map-row .bb-map-chip[data-state="kept"], .bb-map-row .bb-map-chip[data-current="true"] { color: #0b1a1c; }
.bb-map-row .bb-map-chip[data-state="cleared"] { color: #cfeff0; }
/* Free Play's High Score: shut to the label alone until it is tapped. */
/* Shut keeps the number's box and paints nothing in it. display:none took 12px out of the LCD the moment a run
   was won — and the LCD's height is the machine's, scaled by a fit that only answers to the viewport, so the
   whole enclosure shrank on the win and grew back when the readout was tapped open (Mike, 2026-09-20, twice).
   Reserving it also means tapping it open moves nothing: the number simply appears where it always sits. */
.bb-score[data-shut="true"] .bb-score-value { visibility: hidden; }
.bb-score[data-shut="true"] .bb-score-label::after { content: " \25be"; }
.bb-score[role="button"] { cursor: pointer; }
/* The rules, read on the LCD rather than on a face of their own: the black screen is where the game does its talking,
   and it was the one face with no key to reach it (Mike, 2026-09-19). */
/* The rules scroll inside the LCD, the way the Library list does: the black screen keeps its size and the words move
   inside it (Mike, 2026-09-19). */
.bb-career-rules { line-height: 1.5; flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #3a4348 #101214; }
.bb-career-rules h2 { position: static; margin: 18px 0 6px; padding: 0; background: none; color: #ffc46a; font: 700 11px/1 var(--bb-readout); letter-spacing: 1.5px; text-transform: uppercase; }
.bb-career-rules h2:first-child { margin-top: 0; }
.bb-career-rules h3 { margin: 14px 0 4px; color: #e8eff3; font-size: 13px; }
.bb-career-rules p, .bb-career-rules li { color: #b6c3cb; font-size: 13px; }
/* The rules are written for the light panel on the back of the machine, and reused on the LCD. Their lead-ins are set
   in that panel's dark ink, which on a black screen is a smudge (Mike, 2026-09-20: "make those more cyan - not
   readable too dark"). On the LCD they take the LCD's own cyan. */
.bb-career-rules b, .bb-career-rules strong, .bb-career-rules dt { color: #3fe0e0; font-weight: 700; }
.bb-career-rules em { color: #9fe9e9; font-style: normal; }
.bb-career-rules ol { margin: 6px 0; padding-left: 20px; color: #b6c3cb; }
.bb-career-rules ol li::marker { color: #6f7f88; }
.bb-career-rules ul { margin: 6px 0; padding-left: 18px; }
.bb-career-rules table { width: 100%; border-collapse: collapse; font-size: 12px; }
.bb-career-rules td, .bb-career-rules th { padding: 5px 0; border-bottom: 1px solid #1e262a; text-align: left; color: #b6c3cb; }
/* A beat you own, beside the groove's name on the LCD: a crate, in the Library's teal. Sized to the name rather than
   set as a superscript — it reads as a mark on the level, not a footnote (Mike, 2026-09-19). */
.bb-genre[data-owned="true"] { color: #6ff0f0; }
.bb-tempo[data-owned="true"] { display: inline-flex; align-items: center; gap: 8px; }
.bb-owned-mark { flex: none; width: 1.15em; height: 1.15em; fill: none; stroke: #3fe0e0; stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.bb-map-row .bb-map-chip:disabled { color: #4a5155; }
.bb-map-row .bb-map-chip[data-current="true"] { box-shadow: 0 0 0 2px #ffc46a, 0 0 8px #ffc46a80; }
.bb-map-row .bb-map-chip:not(:disabled):active { filter: brightness(1.3); }
.bb-map-hint { margin: 6px 0 8px; color: #7d8990; font: 10px/1.3 var(--bb-readout); }
/* Tracks grows the LCD over the pads (Mike, 2026-10-11, "one"): the screen takes the height it had plus the board's
   (--bb-screen-hold and --bb-map-grow, both measured on open), the pads and the tap hint go, and the machine keeps its
   height, so the front row stays right under the screen as the way out. The tree wears no header and no close box
   (Mike, 2026-09-20) — the Tracks key it came from closes it, and so does Play. */
.bb-screen[data-map="true"] { display: grid; grid-template-rows: 1fr; height: calc(var(--bb-screen-hold, 200px) + var(--bb-map-grow, 0px)); padding-bottom: 0; overflow: hidden; }
.bb-screen[data-map="true"] > :not(.bb-map) { display: none !important; }
.bb-machine:has(.bb-screen[data-map="true"]) .bb-playfield, .bb-machine:has(.bb-screen[data-map="true"]) .bb-tap-hint { display: none !important; }
/* One engraved line under the readings, stamped into the metal like the key legends. */
/* The closing screen mirrors the level screen's two readings, each led by a small check when its target was met or
   a cross when not, centred on the number's height. The marks carry the result alone; the readings keep their color. */
/* The finish LCD's score and pad count read green when met, amber when missed. */
.bb-finish-readout [data-met="true"] { color: #5ee06a; }
.bb-finish-readout [data-met="false"] { color: #ffc46a; }
/* The finish line is plain grey prose; the color lives on the LCD. */
/* A clear says what comes next at a readable size, and its two keys flash in turn: Free Play, then Next. */
.bb-finish-divider { margin: 4px 0 13px; padding-bottom: 14px; border-bottom: 1px solid #54677570; }
/* Share and the transport keys under the board are chrome, the same finish as the rear-face keys, at pad height. */
.bb-share, .bb-transport-key { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; min-width: 0; padding: 10px 12px; font-size: 14px; }
/* The transport keys stand proud of the panel: taller, with a deep chrome edge and a drop shadow, and they sink on press. */
.bb-transport-key { flex-direction: column; gap: 3px; min-height: 54px; padding: 7px 2px 5px; border-bottom-color: #5c6f7b; background: linear-gradient(#e3eaee, #c2ced5 60%, #aebbc3); box-shadow: 0 5px 0 #7d8f9b, 0 6px 0 1px #3f4c54, 0 9px 8px #0007, inset 0 1px 0 #f7fafc, inset 0 -1px 0 #ffffff40; }
.bb-transport-key:not(:disabled):not([data-locked]):active { box-shadow: 0 1px 0 #7d8f9b, 0 2px 0 1px #3f4c54, 0 3px 4px #0006, inset 0 1px 0 #f7fafc; }
/* Engaged is one look everywhere, machine and Career alike (Mike, 2026-09-17): the key stays down in its well with the
   legend engraved amber. Idle keys keep the plain black engraving of the front panel. */
.bb-transport-key[aria-selected="true"], .bb-transport-key[aria-pressed="true"] { transform: translateY(4px); border-color: #3f4c54; border-bottom-color: #3f4c54; background: linear-gradient(#b9c5cc, #aab7bf); color: #1f1b14; box-shadow: 0 1px 0 #7d8f9b, 0 2px 0 1px #3f4c54, inset 0 2px 5px #0006, inset 0 0 0 1px #3f4c5480; }
.bb-transport-key[aria-selected="true"] .bb-key-legend, .bb-transport-key[aria-pressed="true"] .bb-key-legend { color: #1f1b14; text-shadow: none; }
.bb-transport-key[aria-selected="true"] .bb-transport-icon, .bb-transport-key[aria-pressed="true"] .bb-transport-icon { filter: none; }
@media (prefers-reduced-motion: reduce) { .bb-transport-key[aria-selected="true"], .bb-transport-key[aria-pressed="true"] { transform: none; } }
.bb-transport-icon { filter: drop-shadow(0 1px 0 #ffffffa0); }
/* Share: three dots joined by two hairlines, a node graph. */
.bb-share-icon { flex-shrink: 0; width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* Share sits in the results heading as the same silver key as Back to game on the rear face. */
/* Two keys on the level screen, three once Levels joins them (and three again when paused, Retry level in Career's
   place): one row either way. */
/* One main key, then two under it side by side: three places that never move, whatever they happen to read. */
.bb-intro-keys { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.bb-intro-keys button { min-height: 44px; min-width: 0; }
.bb-dialog-action:disabled { opacity: .5; }
/* The one pad that moves the game on (Play Dropbeat, Next level, Try again) is built like a backlit token pad
 * (.bb-token, amber): a faint amber rim, an amber line under the rubber base, and a lit strip beneath the bottom
 * edge whose light pools under the key. Nothing spills over the face or the sides. */
.bb-intro-start, .bb-stage-start, .bb-new-next, .bb-finish-primary { --coin-rgb: 255, 209, 131; border-color: rgba(var(--coin-rgb), .5); border-top-color: rgba(var(--coin-rgb), .7); border-bottom-color: #111416; box-shadow: 0 3px 0 #0c0d0e, 0 4px 0 rgba(var(--coin-rgb), .7), 0 5px 0 1px #08090a, 0 6px 1px 1px #ffffff70, inset 0 1px 1px #ffffff2e, inset 0 -2px 1px #000b; }
.bb-intro-start::before, .bb-stage-start::before, .bb-new-next::before, .bb-finish-primary.bb-dialog-action::before { content: ''; position: absolute; left: 3px; right: 3px; bottom: -3px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, rgba(var(--coin-rgb), .12), rgba(var(--coin-rgb), .82) 30%, rgba(var(--coin-rgb), .82) 70%, rgba(var(--coin-rgb), .12)); box-shadow: 0 5px 6px rgba(var(--coin-rgb), .3), 0 10px 10px rgba(var(--coin-rgb), .12); pointer-events: none; }
/* The label is a backlit legend: orange letters with a soft amber halo, like the token symbols. */
.bb-intro-start, .bb-finish-primary { color: #ffc46a; text-shadow: 0 0 5px rgba(var(--coin-rgb), .35), 0 0 12px rgba(var(--coin-rgb), .18); }
.bb-intro-start:disabled, .bb-finish-primary:disabled { color: #e0e5e8; text-shadow: none; border-color: #5b6063; border-top-color: #8a8e90; box-shadow: 0 3px 0 #111416, 0 4px 0 #ffffff60, inset 0 1px 1px #ffffff26, inset 0 -2px 1px #0008; }
.bb-intro-start:disabled::before, .bb-finish-primary:disabled::before { display: none; }
/* History is one LCD (.bb-screen) holding the high-score table, highest score first. Each row borrows the main
 * display's faces: level and style in the instrument face, moves and drums in the readout face under it, the score in
 * the yellow score face on the right with Cleared (green, as Target reached) or Missed under it. Rows are split by the
 * same rule the LED strip sits on. */
@media (hover: hover) {
  .bb-column:not(:disabled):hover .bb-tap-indicator { background: #60849a; }
  .bb-column:not(:disabled):hover .bb-tap-indicator::after { background: #e1f2fa; box-shadow: 0 0 4px #d6eaff; }
  .bb-rules-close:not(:disabled):hover, .bb-dialog-close:not(:disabled):hover, .bb-share:not(:disabled):hover, .bb-transport-key:not(:disabled):hover, .bb-trim-save:not(:disabled):hover, .bb-trim-reset:not(:disabled):hover, .bb-beat-save:not(:disabled):hover, .bb-beat-reset:not(:disabled):hover { filter: brightness(1.1); }
  .bb-dialog-action:not(:disabled):hover { filter: brightness(1.15); }
}
.bb-transport-key:not(:disabled):active, .bb-share:not(:disabled):active, .bb-rules-close:not(:disabled):active, .bb-dialog-action:not(:disabled):active { transform: translateY(2px); }
.bb-transport-key:not(:disabled):not([data-locked]):active { transform: translateY(4px); }
@keyframes bb-lock-light { 0%, 100% { outline-color: #b17a29; } 50% { outline-color: #e1a849; } }
/* Slot machine: a panel falls in from above the frame, overshoots its slot and settles; it leaves off the bottom (app.mjs closeDialog). */
@keyframes bb-dialog-enter { from { transform: translateY(calc(-50vh - 50%)); } 62% { transform: translateY(10px); } 82% { transform: translateY(-4px); } to { transform: none; } }
/* Retry of the same level: the results swipe out to the right and the intro swipes in from the left. */
@keyframes bb-dialog-enter-side { from { transform: translateX(calc(-50vw - 50%)); } 62% { transform: translateX(10px); } 82% { transform: translateX(-4px); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .bb-machine, .bb-rules-panel, .bb-career-panel { transition: none; }
  .bb-dialog[open], .bb-final { animation: none; }
  .bb-flow circle { animation: none; opacity: .7; }
  .bb-pad.bb-locked.bb-sounding { border-color: #9dbaca; background: linear-gradient(145deg, #3c5667, #2c4454); box-shadow: 0 3px 0 #10171d; }
  .bb-locked.bb-sounding .bb-lock-icon { filter: none; stroke: #c8e3f1; }
  .bb-transport-key:not(:disabled):active, .bb-share:not(:disabled):active, .bb-rules-close:not(:disabled):active, .bb-dialog-action:not(:disabled):active { transform: none; }
}
/* 371-389px (iPhone SE, mini): the wide layout's lane is up to 13px short of three tokens, two flow marks, five 8px gaps and
   the key, so the lane's gaps and the key's width tighten while the tokens and marks keep their sizes. */
@media (max-width: 389px) {
  .bb-lane { gap: 6px; }
}
/* On a short screen the idle hint's band is the cheapest 18px on the machine, and losing it means the enclosure starts
   shrinking later. The +1 floaters are absolutely positioned over the board and are not in this band (Mike,
   2026-09-17). Situational lines — Last chance, the swap instructions — go with it on these screens. */
@media (max-height: 760px) {
  .bb-tap-hint, .bb-tap-hint[hidden] { display: none !important; }
}
@media (max-width: 370px) {
  body { padding: var(--bb-frame) 12px; }
  .bb-machine { padding: 12px 10px 12px; }
  .bb-wordmark { font-size: 25px; }
  .bb-screen { --bb-side: 10px; padding: 13px var(--bb-side) 13px; }
  .bb-genre { font-size: 22px; }
  .bb-score-value { font-size: 30px; }
  .bb-tempo { font-size: 16px; }
  .bb-instrument { font-size: 16px; }
  .bb-lock-progress { gap: 5px; }
  .bb-playfield { --bb-gap: 6px; --bb-bank-padding: 3px; }
  .bb-number { top: 4px; left: 4px; font-size: 7px; }
  .bb-symbol { width: 35px; height: 33px; }
  .bb-queue { padding: 10px 6px 11px; gap: 6px; }
  .bb-lane { gap: 6px; min-height: 54px; padding: 4px 4px 4px 6px; }
  .bb-queue .bb-token { width: 41px; height: 41px; }
  .bb-queue .bb-symbol { width: 28px; height: 27px; }
  .bb-end { inset-inline: 6px; }
  .bb-layer { font-size: 9px; letter-spacing: .8px; }
  .bb-steps { gap: 3px; }
  .bb-dialog-heading, .bb-dialog-body { padding-inline: 16px; }
  .bb-rules-heading { padding: 14px 12px; }
  .bb-rules-top { gap: 7px; }
  .bb-rules-body { padding: 16px 12px; }
  .bb-token-guide > div { grid-template-columns: 122px minmax(0, 1fr); gap: 8px; }
  .bb-token-guide dt { gap: 8px; }
}
/* Under the LCD, two mirrored readings: Drums Filled over the pad count on the left, Target Score over the points on
   the right. A score-only level shows the score alone, centred. The Play key follows with no rule between. */
/* The goal line reads like the tap hint under the LCD (plain weight, mixed case, the same muted grey), a size up. */
/* The stage's own words, under the LCD on every level of it: prose, so it is set to read as writing. */
.bb-intro-brief { margin: 22px 2px auto; color: #5d7480; font-size: 14px; font-weight: 400; line-height: 1.5; text-align: center; text-wrap: pretty; }
.bb-intro-score { text-align: right; }

/* The dropped swap tile blinks only after the player taps it. */
.bb-token[data-kind="swap"] { --coin-rgb: 255, 255, 255; --pop-rgb: 255, 255, 255; }
.bb-pad.bb-swap-choice { z-index: 5; pointer-events: auto; cursor: pointer; }
.bb-pad.bb-swap-choice:focus-visible { outline: 2px solid #fff; outline-offset: -3px; }
.bb-pad.bb-swap-target { outline: 1px solid #c6d9df; outline-offset: -3px; }
/* A tapped Swap tile flashes slowly and brightly until the second tap, so the swap in progress is never missed (Mike,
   2026-09-30: "make it more obvious once you hit the recycle tile ... flash slowly", meaning after the tap). */
.bb-pad.bb-swap-source { outline: 2px solid #fff; outline-offset: -3px; animation: bb-swap-pulse 1.6s ease-in-out infinite; }
/* The mix tray: one slider per kit slot, S and M per row, Update mix. */
/* The tuning bench trays (?mix=true). On a phone a tab bar at the foot of the screen (Machine, Mix, Beat) shows one tray in the machine's place; from 960px they slide out from under it on their handles, the mix tray to the left and the beat tray to the right. */
.bb-tray { position: relative; border-radius: 4px; background: #101214; color: #cfd6da; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
.bb-tray-body { padding: 14px 16px 16px; }
.bb-tray-handle { display: none; }
@media (max-width: 959px) {
  /* On the phone a tray slides in over the machine from its own side, the Beat tray from the right and the Mix tray from
     the left, at the machine's full height, and closes from its own key or the key that opened it. No tab bar, no handles. */
  .bb-tray { position: absolute; top: 0; bottom: 0; z-index: 4; width: 100%; overflow: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #3a4348 #101214; border: 1px solid #2a3034; border-top: 4px solid #a2a6a8; box-shadow: 0 0 40px #000c; visibility: hidden; transition: transform 320ms cubic-bezier(.2, .75, .2, 1), visibility 0s 320ms; }
  .bb-tray[hidden] { display: none; }
  .bb-trim { left: 0; transform: translateX(-104%); }
  .bb-beat { right: 0; transform: translateX(104%); }
  .bb-tray.bb-open { transform: none; visibility: visible; transition: transform 320ms cubic-bezier(.2, .75, .2, 1), visibility 0s; }
  /* The way out is the same X in the same corner as the campaign map and the Career face, and the help key moves to the
     other corner: a question mark beside a Close read as a pair of choices (Mike, 2026-09-17). */
  .bb-tray-close { position: absolute; top: 8px; right: 12px; z-index: 2; display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid #3a4348; border-radius: 4px; background: #1c2124; color: #cfd6da; }
  .bb-tray-close svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
  /* Six tabs ride the faders tray when Mix is opened from the Beat tray; at the full 18px and 1px tracking Kit ran under
     the X (Mike, 2026-09-27). */
  .bb-tray .bb-beat-views { gap: 12px; }
  .bb-tray .bb-beat-views button { letter-spacing: .5px; }
  .bb-beat .bb-beat-legend { left: 12px; }
}
@media (min-width: 960px) {
  .bb-tray-close { display: none; }
  .bb-tray { position: absolute; top: 22px; z-index: -1; display: flex; flex-direction: column; width: min(540px, calc((100vw - 400px) / 2 - 44px)); border: 1px solid #2a3034; border-top: 4px solid #a2a6a8; max-height: calc(100% - 66px); margin: 0; border-radius: 6px; box-shadow: 0 14px 24px #000a; transition: transform 420ms cubic-bezier(.2, .75, .2, 1); }
  /* A slice of the machine base under each tray, so the drawer reads as part of the chassis. */
  .bb-tray::after { content: ""; position: absolute; z-index: -1; left: -1px; right: -1px; bottom: -11px; height: 12px; border: 1px solid #696b6c; border-top: 1px solid #f2f3f3; border-radius: 0 0 12px 12px; background: url("./alloy-grain.svg"), linear-gradient(#b2b4b4 0%, #919494 25%, #656869 77%, #4b4e4f); box-shadow: inset 0 1px 0 #ffffff4d, 0 3px 0 #17191a; pointer-events: none; }
  .bb-tray-body { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; scrollbar-color: #3a4348 #101214; }
  .bb-trim { left: 0; }
  .bb-trim.bb-open { transform: translateX(calc(16px - 100%)); }
  /* The tray runs 16px under the machine, so it reads as one slab; its content stops 16px clear of the machine's edge. */
  .bb-trim .bb-tray-body { padding-right: 30px; }
  .bb-beat { right: 0; }
  .bb-beat.bb-open { transform: translateX(calc(100% - 16px)); }
  .bb-beat .bb-tray-body { padding-left: 30px; }
  /* The body's last inch fades out, so content scrolled under the foot reads as more below. */
  .bb-tray-body { -webkit-mask-image: linear-gradient(#000 calc(100% - 6px), #0000); mask-image: linear-gradient(#000 calc(100% - 6px), #0000); }
  .bb-beat .bb-beat-foot { padding-left: 30px; }
  .bb-tray-handle { display: block; position: absolute; top: 8px; width: 34px; height: 72px; padding: 0; border: 1px solid #3a4348; background: #1c2124; color: #7d8990; font: inherit; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; writing-mode: vertical-rl; }
  .bb-trim .bb-tray-handle { left: -34px; border-right: 0; border-radius: 5px 0 0 5px; transform: rotate(180deg); }
  .bb-beat .bb-tray-handle { right: -34px; border-left: 0; border-radius: 0 5px 5px 0; }
  .bb-tray-handle[aria-expanded="true"] { color: #3fe0e0; }
}
@media (prefers-reduced-motion: reduce) { .bb-tray { transition: none; } }
/* The beat tray's two views: the pattern grid and the Drums list. */
/* Pattern and Drums are tabs, not keys: no box, the selected one underlined. */
/* A locked view's card: the skill token, what it does, how far off it is, and the way to the ladder. */
/* The locked-skill card lives in the Beat tray only: the board's own bb-locked is a pad holding a drum. */
/* The same card in either tray: token on the left, text beside it, no box around it. */
.bb-skill-card { display: flex; gap: 14px; align-items: flex-start; padding: 6px 28px 4px 0; }
.bb-skill-card-name { margin: 2px 0 0; color: #e8eff3; font-size: 15px; }
.bb-skill-card-about { margin: 6px 0 0; color: #cfd6da; line-height: 1.5; }
.bb-skill-card-at { margin: 10px 0 0; color: #9aa7ae; font: 11px/1.5 var(--bb-readout); }
/* The faders tray is narrower than the beat tray, and "Unlocked with 30 beats in your library" wrapped in it. */
.bb-trim .bb-skill-card-at { font-size: 10px; letter-spacing: -.1px; }
.bb-skill-card-at b { color: var(--bb-amber); }
.bb-beat-views button[data-locked] { color: #5c666b; }
.bb-beat-views { display: flex; gap: 18px; margin: 0 28px 12px 0; border-bottom: 1px solid #2a3034; }
.bb-beat-views button { min-height: 28px; padding: 0 0 6px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; border-radius: 0; background: none; color: #7d8990; font: inherit; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; }
.bb-beat-views button[aria-selected="true"] { color: #3fe0e0; border-bottom-color: #3fe0e0; }
/* Drums: one row per drum in arrival order, on one line: grip, name, lock, swing, fills, pitch. The keys are icons
   rather than words to keep the line, and the legend above the list names them. */
.bb-drum-head { padding: 0 0 10px; border-bottom: 1px solid #1c2124; margin-bottom: 10px; color: #7d8990; }
/* The row-key legend lives in the ? popover now, where its icons sit on the popover's own background. */
.bb-beat-legend .bb-drum-legend dd { color: #cfd6da; }
.bb-drum-order { margin: 0 0 8px; color: #cfd6da; font-weight: 600; }
.bb-drum-legend { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 5px 8px; align-items: start; margin: 0; }
.bb-drum-legend dt { display: flex; justify-content: center; color: #9aa7ae; }
.bb-drum-legend dd { margin: 0; }
.bb-drum-legend b { color: #cfd6da; font-weight: 600; }
.bb-drum-list { display: flex; flex-direction: column; gap: 6px; }
/* grip | name | Lock | Fills | Solo. Swing is a knob under the grid, not a key here (Mike, 2026-09-20). The grip is a
   thumb wide and the row's full height, and the row you hold lights until you let go (Mike, 2026-09-27). */
.bb-drum-row { display: grid; grid-template-columns: 36px minmax(40px, 1fr) auto auto auto; gap: 6px; align-items: center; padding: 0 8px 0 0; border: 1px solid #1c2124; border-radius: 3px; background: #14181a; }
.bb-drum-grip { display: grid; place-items: center; align-self: stretch; min-height: 40px; cursor: grab; touch-action: none; }
.bb-drum-grip::before { content: ''; width: 14px; height: 18px; background: radial-gradient(circle, #4a555c 1.2px, transparent 1.4px) 0 0 / 7px 6px; }
.bb-drum-row.bb-drum-held .bb-drum-grip::before { background-image: radial-gradient(circle, #3fe0e0 1.2px, transparent 1.4px); }
.bb-drum-row.bb-drum-held { border-color: #3fe0e0; background: #173236; box-shadow: 0 0 0 1px #3fe0e0; }
.bb-drum-row.bb-drum-held .bb-drum-name { color: #3fe0e0; }
.bb-drum-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #cfd6da; font-weight: 600; }
.bb-drum-key { display: flex; align-items: center; justify-content: center; width: 28px; min-height: 26px; padding: 0; border: 1px solid #3a4348; border-radius: 3px; background: #1c2124; color: #7d8990; font: inherit; }
.bb-drum-key svg, .bb-drum-legend svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.bb-drum-key svg [fill="currentColor"] { stroke: none; }
.bb-drum-lock[data-state="backing"] { opacity: .5; }
.bb-drum-key[aria-pressed="true"] { color: #3fe0e0; border-color: #3fe0e0; }
.bb-beat-drums { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; padding: 0 28px 14px 0; border-bottom: 1px solid #1c2124; }
.bb-beat-drums button { position: relative; min-height: 28px; padding: 0 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; border: 1px solid #3a4348; border-radius: 3px; background: #1c2124; color: #7d8990; font: inherit; }
/* Groove swap (Remixer): armed from the Pattern tab, the next drum tapped is the one to trade with, so the drum row
   says so rather than growing a fourth tab on a phone that already carries three. */
/* Swap pattern rides the same line as Swing, under the grid: both are things you do to the drum you are looking at,
   and the key says what it swaps, so the line of hint text under it went (Mike, 2026-09-19). */
.bb-beat-drums button[aria-selected="true"] { color: #3fe0e0; border-color: #3fe0e0; }
.bb-beat-lane + .bb-beat-lane { margin-top: 10px; }
/* Over each bar: the lane's name at the left of its first bar, the bar number at the right, on one baseline. */
.bb-beat-bar-head { display: flex; justify-content: space-between; align-items: flex-end; margin: 22px 0 6px; }
.bb-beat-lane-name { margin: 0; color: #cfd6da; font-size: 12px; font-weight: 600; line-height: 1; }
.bb-beat-bar-label { color: #9aa7ae; font-size: 10px; line-height: 1; }
/* Help lives in the footer beside Reset (Mike, 2026-09-19): in the corner it sat on the first tab's name. */
.bb-beat-foot .bb-beat-help { position: static; margin-right: auto; }
.bb-beat-help { position: absolute; top: 12px; right: 12px; width: 22px; height: 22px; padding: 0; border: 1px solid #3a4348; border-radius: 50%; background: #1c2124; color: #7d8990; font: 700 12px/1 inherit; font-family: inherit; }
.bb-beat-legend { position: absolute; z-index: 2; bottom: 46px; top: auto; left: 12px; right: 12px; margin: 0; padding: 10px 14px 16px; border: 1px solid #3a4348; border-radius: 4px; background: #1c2124; color: #cfd6da; font-size: 11px; line-height: 1.5; box-shadow: 0 6px 16px #000a; }
.bb-beat-legend p { margin: 0 0 4px; }
.bb-beat-legend b { color: #e8eff3; }
.bb-beat-legend p:last-child { margin-bottom: 0; }
/* A bar is two halves of eight, each half an even eight-column grid: the half-bar break is a gap between the halves,
   not a margin on one cell, which used to shave 4px off the first step of beats 2 and 4 (Mike, 2026-09-18: "squashed
   first beat of last"). Narrow, the halves stack as the two rows of eight they always were. */
.bb-beat-bar { display: grid; grid-template-columns: 1fr; gap: 6px 8px; }
.bb-beat-half { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 6px 4px; }
@media (min-width: 1200px) { .bb-beat-bar { grid-template-columns: 1fr 1fr; } }
.bb-beat-cell { position: relative; height: 48px; min-width: 0; padding: 0; border: 1px solid #1c2124; border-radius: 3px; background: #15191c; color: #101214; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; overflow: hidden; touch-action: none; -webkit-user-select: none; user-select: none; }
.bb-beat-half > .bb-beat-cell:nth-child(4n+1) { border-left-color: #3a4348; }
.bb-beat-cell.bb-on::before { content: ""; position: absolute; z-index: 1; left: 0; right: 0; top: 50%; border-top: 1px solid #ffffff26; }
@media (pointer: coarse) { .bb-beat-cell { height: 60px; } }
.bb-beat-cell::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; top: 0; background: repeating-linear-gradient(to top, var(--fill, #3fe0e0) 0, var(--fill, #3fe0e0) calc(25% - 1px), #15191c calc(25% - 1px), #15191c 25%); clip-path: inset(calc((1 - var(--v)) * 100%) 0 0 0); }
/* Voices without letters: an open hat is outlined, a crash lighter, a splash paler; a roll is hatched. */
.bb-beat-cell.bb-open::after { background: repeating-linear-gradient(to top, transparent 0, transparent 1px, #15191c 1px, #15191c calc(25% - 2px), transparent calc(25% - 2px), transparent 25%), #3fe0e0; box-shadow: inset 0 0 0 1.5px #3fe0e0; }
.bb-beat-cell.bb-voice-crash { --fill: #9af0f0; }
.bb-beat-cell.bb-voice-splash { --fill: #d6fafa; }
.bb-beat-cell.bb-more::after { background-image: repeating-linear-gradient(135deg, transparent 0, transparent 3px, #15191c66 3px, #15191c66 4px), repeating-linear-gradient(to top, var(--fill, #3fe0e0) 0, var(--fill, #3fe0e0) calc(25% - 1px), #15191c calc(25% - 1px), #15191c 25%); }
.bb-beat-cell > span { position: relative; z-index: 1; }
.bb-beat-cell.bb-now { box-shadow: inset 0 0 0 1px #e8eff3; }
/* The foot sits outside the scrolling body, so Update and Reset stay put and a long list stops against a rule
   rather than being sliced in half by the chassis. */
.bb-beat-foot { display: flex; gap: 8px; align-items: center; padding: 12px 16px; border-top: 1px solid #2a3034; }
.bb-beat-count { flex: 1; color: #cfd6da; }
/* The mixer is a list of closed rows with one open (app.mjs openTrimRow). A closed row is a 44px thumb target holding
   the name, the solo and mute flags and the level; the open one hands the fader the whole tray width, which is the only
   way it gets real travel on a phone: laid out in one line the fader kept about 66px for 24dB (Mike, 2026-09-17). */
/* One enclosure, not a box a drum: the rows sit in a single dark bay divided by hairlines, the way a fader bank reads
   (Mike, 2026-09-18). */
.bb-trim-rows { display: grid; border: 1px solid #222a2f; border-radius: 8px; background: #12171a; overflow: hidden; }
.bb-trim-row { padding-bottom: 8px; border-top: 1px solid #1c242a; }
.bb-trim-row:first-child { border-top: 0; }
/* Master is the last strip, set apart by a heavier line rather than a gap. */
/* The master is its own view now (FX), so it carries no separator from a list it no longer sits under. */
.bb-trim-master .bb-trim-row[data-master] { border-top: 0; }
.bb-trim-voice .bb-trim-voice-note { color: #6b7a83; font-size: 11px; line-height: 24px; }
.bb-trim-voice .bb-trim-name { color: #9aa7ae; }
.bb-trim-row[data-master] { border-top: 1px solid #36434b; background: #0f1417; }
.bb-trim-row[data-unused] { opacity: .45; }
/* Two lines, always open: the name and its keys, then the fader (Mike, 2026-09-18). */
.bb-trim-head { display: grid; grid-template-columns: minmax(0, 1fr) 46px; align-items: center; gap: 10px; width: 100%; min-height: 34px; padding: 6px 12px 0; border: 0; background: none; color: #cfd6da; font: inherit; text-align: left; }
/* A closed row has to read as something you can open, or the tray looks like a list that does nothing. */
.bb-trim-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bb-trim-name .bb-slot-key { margin-left: 6px; color: #5f6b72; font-size: 10px; }
.bb-trim-name .bb-swapped { color: #ffc46a; }
.bb-trim-row.bb-engaged .bb-trim-name { font-weight: 700; color: #3fe0e0; }
.bb-trim-row[data-master] .bb-trim-name { letter-spacing: 1px; text-transform: uppercase; font-size: 11px; font-weight: 700; color: #8fa3b0; }
.bb-trim-flag { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid #2c353b; border-radius: 4px; background: #1c2124; color: #7d8990; font: 700 11px/1 var(--bb-readout); }
.bb-trim-flag[data-flag="solo"][data-on="true"] { border-color: #ffd183; background: #ffd183; color: #10161a; }
.bb-trim-flag[data-flag="mute"][data-on="true"] { border-color: #ff7bc8; background: #ff7bc8; color: #10161a; }
.bb-pitch-key[data-on="true"] { border-color: #5ee06a; background: #5ee06a; color: #10161a; }
.bb-trim-fx { width: auto; padding: 0 7px; }
.bb-trim-fx[data-on="true"] { border-color: #3fe0e0; color: #3fe0e0; }
.bb-trim-fx[aria-expanded="true"] { border-color: #3fe0e0; background: #3fe0e0; color: #10161a; }
/* The readout column is a fixed width and right-aligned, so every level, dash or not, and every S and M above them
   line up down the tray (Mike, 2026-09-18). */
.bb-trim-head output { text-align: right; color: #9aa7ae; font: 12px/1 var(--bb-readout); font-variant-numeric: tabular-nums; }
.bb-trim-row[data-changed="true"] .bb-trim-head output { color: #ffc46a; }
.bb-trim-row.bb-engaged .bb-trim-head output { color: #3fe0e0; }

/* The fader's line: S and M to its left, then the fader taking the rest (Mike, 2026-09-19). */
.bb-trim-body { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 10px; padding: 0 12px 6px; }
.bb-trim-body > .bb-fx, .bb-trim-body > .bb-fx-locked, .bb-trim-body > .bb-fx-range { grid-column: 1 / -1; }
/* The fader is the one control this tray exists for, so it is built rather than accented: a narrow engraved slot with
   a tick at the packaged level, and a cap cut from the machine's own silver: one plain face, a crisp edge, a close
   shadow, and nothing drawn on it (Mike, 2026-09-18: less chrome, clean, no line across the middle). The level itself says
   what moved: the row's readout goes amber off the packaged level and cyan on the engaged drum. */
.bb-trim-body input[type="range"], .bb-strip-fader input[type="range"], .bb-master-level input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 40px; margin: 0; background: none; touch-action: none; }
/* A fader has to be catchable with a thumb, not a cursor (Mike, 2026-09-21: "mix sliders are hard to adjust"). The
   rack's faders are rotated a quarter turn, so the input's height is how wide the live strip is on the panel: 28px of
   it left more than half the 62px column dead, and the cap was 16px of travel to land on. The cap is bigger, the slot
   is a touch deeper, and the strip is as wide as the column allows. */
.bb-trim-body input[type="range"]::-webkit-slider-runnable-track, .bb-strip-fader input[type="range"]::-webkit-slider-runnable-track, .bb-master-level input[type="range"]::-webkit-slider-runnable-track { height: 6px; border-radius: 3px;
  background: linear-gradient(#57646c, #57646c) center/1px 6px no-repeat, var(--bb-slot, #090e11);
  box-shadow: inset 0 1px 1px #000c, 0 1px 0 #2b353b, var(--bb-glow, 0 0 0 #0000); }
.bb-trim-body input[type="range"]::-moz-range-track, .bb-strip-fader input[type="range"]::-moz-range-track, .bb-master-level input[type="range"]::-moz-range-track { height: 6px; border-radius: 3px;
  background: linear-gradient(#57646c, #57646c) center/1px 6px no-repeat, var(--bb-slot, #090e11);
  box-shadow: inset 0 1px 1px #000c, 0 1px 0 #2b353b, var(--bb-glow, 0 0 0 #0000); }
/* A channel off its packaged level draws one cyan line under itself. It used to light the fader's own slot with an
   amber glow, which read as a fault rather than a mark and smeared the metal (Mike, 2026-09-20: "i don't like the
   orange glow - just make it an orange line under it if adjusted (or cyan better)"). */
.bb-strip[data-changed], .bb-master[data-changed] { box-shadow: inset 0 -2px 0 var(--bb-moved); }
.bb-strip[data-changed="up"], .bb-master[data-changed="up"] { --bb-moved: #ffc46a; }
.bb-strip[data-changed="down"], .bb-master[data-changed="down"] { --bb-moved: #3fe0e0; }
.bb-strip[data-changed] > output, .bb-master[data-changed] .bb-master-level output { color: var(--bb-moved); }
.bb-trim-body input[type="range"]::-webkit-slider-thumb, .bb-strip-fader input[type="range"]::-webkit-slider-thumb, .bb-master-level input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 34px; margin-top: -14px; border: 1px solid #8b99a2; border-radius: 4px;
  background: linear-gradient(#f3f6f8, #dbe3e8 55%, #ccd7dd);
  box-shadow: 0 1px 2px #0008, inset 0 1px 0 #fff; }
.bb-trim-body input[type="range"]::-moz-range-thumb, .bb-strip-fader input[type="range"]::-moz-range-thumb, .bb-master-level input[type="range"]::-moz-range-thumb { width: 24px; height: 34px; border: 1px solid #8b99a2; border-radius: 4px;
  background: linear-gradient(#f3f6f8, #dbe3e8 55%, #ccd7dd);
  box-shadow: 0 1px 2px #0008, inset 0 1px 0 #fff; }
.bb-trim-body input[type="range"]:focus-visible::-webkit-slider-thumb, .bb-strip-fader input[type="range"]:focus-visible::-webkit-slider-thumb, .bb-master-level input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid #3fe0e0; outline-offset: 1px; }
.bb-trim-body input[type="range"]:focus-visible::-moz-range-thumb, .bb-strip-fader input[type="range"]:focus-visible::-moz-range-thumb { outline: 2px solid #3fe0e0; outline-offset: 1px; }
.bb-trim-keys { display: flex; gap: 6px; align-items: center; }
/* Master's pair are the board's own keys, and its empty swap slot keeps the column aligned with the drums above. */
.bb-trim-row[data-master] .bb-swap { pointer-events: none; }
.bb-fx-locked { margin: 10px 0 2px; color: #6b7a83; font: 11px/1.5 var(--bb-readout); }
/* Effects (Engineer) live on the Master row, which is where the engine applies them: across the summed mix, not per drum. */
/* Kit swap (Remixer) is a view of the tray, not a picker under the faders: the rows fill the width the faders had and
   the tray scrolls them, rather than a 260px box scrolling inside a tray that was already scrolling (Mike, 2026-09-19).
   Each row reads like a Library row — the machine's name, the style it came from under it. */
/* Blank groove (Beatmaker) opens the Library, since that is where a player's own beats live. */
.bb-blank-key { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; width: 100%; margin-bottom: 12px; padding: 12px 14px; border: 1px solid #3fe0e0; border-radius: 6px; background: #0e2629; color: #3fe0e0; font: inherit; text-align: left; }
.bb-blank-key b { font-size: 14px; }
.bb-blank-key span { color: #7fb6bb; font-size: 11px; }
.bb-kit-box, .bb-pads-box { margin-top: 4px; }
.bb-kit-box[hidden], .bb-pads-box[hidden] { display: none; }
.bb-kit-note { margin: 0 0 10px; }
.bb-kit-note { margin: 0 0 8px; color: #7d8990; font-size: 11px; }
/* The Pad layout view (Mix tier, 2026-10-16): numbered rows of extras with up, down and off keys, then keys to add. */
.bb-pad-row { grid-template-columns: 24px minmax(40px, 1fr) auto auto auto; padding-left: 8px; min-height: 40px; }
.bb-pad-no { color: #7d8990; font: 700 11px/1 var(--bb-readout); }
.bb-pad-adds { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 0 0; }
.bb-pad-add, .bb-pad-reset { min-height: 32px; padding: 0 10px; border: 1px solid #222a2f; border-radius: 6px; background: #12171a; color: #cfd6da; font: 600 12px/1.3 inherit; font-family: inherit; }
.bb-pad-reset { margin-top: 10px; color: #7d8990; }
.bb-kit-rows { display: grid; gap: 6px; }
.bb-kit-row { display: grid; gap: 2px; min-height: 48px; padding: 7px 12px; border: 1px solid #222a2f; border-radius: 6px; background: #12171a; color: #cfd6da; font: 600 14px/1.3 inherit; font-family: inherit; text-align: left; }
.bb-kit-row[aria-current="true"] { border-color: #3fe0e0; color: #3fe0e0; }
.bb-kit-row[data-loading="true"] { opacity: .5; }
.bb-kit-id { color: #7d8990; font: 10px/1.4 var(--bb-readout); text-transform: uppercase; letter-spacing: .5px; }
.bb-kit-row[aria-current="true"] .bb-kit-id { color: #3fe0e0aa; }
/* Where a drum is routed, on the drum's own row: the FX key beside S and M opens its sends, and each effect is a key
   that is on or off — none, one or several (Mike, 2026-09-19). */
.bb-trim-fx { letter-spacing: 0; }
.bb-fx { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.bb-fx .bb-knobs { margin-top: 0; padding-top: 12px; border-top: 1px solid #2a3034; }
/* The room is one little backlit window, not four buttons: the four names sit behind the same glass, divided by hairlines,
   the chosen one lit and the rest dim (Mike, 2026-09-20: "make dry room ... look like a mini backlit lcd with the
   unselected ones dim"). The texture is the LCD's own: a faint scanline over a dark green-black ground, and an inner
   shadow so the glass sits below the panel. */
.bb-fx-keys { display: flex; gap: 0; overflow: hidden; border: 1px solid #0b1013; border-radius: 4px;
  background: repeating-linear-gradient(#0d1512 0 1px, #0a1110 1px 3px);
  box-shadow: inset 0 2px 5px #000b, 0 1px 0 #2b353b; }
.bb-fx-keys button { flex: 1; min-height: 34px; padding: 0; border: 0; border-left: 1px solid #16211d; background: none;
  color: #37544c; font: 700 10px/1 var(--bb-readout); letter-spacing: .8px; text-transform: uppercase; text-shadow: 0 0 6px #0000; }
.bb-fx-keys button:first-child { border-left: 0; }
.bb-fx-keys button[aria-pressed="true"] { background: #11201c; color: #6fe7c8; text-shadow: 0 0 8px #6fe7c880; }
.bb-swap { display: none; width: 26px; height: 24px; padding: 0; border: 0; border-radius: 4px; background: none; color: #cfd6da; cursor: pointer; font: inherit; }
.bb-swap svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; vertical-align: middle; }
.bb-swap:hover, .bb-swap:focus-visible { color: #fff; }
.bb-archive .bb-swap, .bb-swap[data-colors] { display: inline-grid; place-items: center; }
/* The color pack picker: rows in the Kit view's own shape -- the machine's name with the style that plays it under
   it -- so choosing a Hit reads like choosing a kit, which is what it is (Mike, 2026-09-19). */
.bb-color { min-height: 40px; padding: 5px 10px; font-size: 13px; }
.bb-color .bb-kit-id { font-size: 9px; }
/* The archive search only means something where a dev server answers it; the colors are always there. */
.bb-swap-q { display: none; }
.bb-archive .bb-swap-q { display: block; }
.bb-archive .bb-swap-hits { max-height: 18em; }
/* The desk. Every channel stands up -- scribble strip, sends, keys, fader, level -- and the rack scrolls sideways with
   a channel snapping under the thumb (Mike, 2026-09-19: "convinced upright fader"). Nothing taps open. The channel is a
   fixed column: the rotated fader keeps its 172px layout box, so it is taken out of flow or it sets the column's width
   and every plate above it stretches to match. */
.bb-trim-strips { display: flex; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
  margin: 0 -12px; padding: 2px 12px 8px; background: linear-gradient(#0c1114, #10161a); }
/* A narrow channel: the sends stack instead of sitting side by side, which costs nothing -- a channel is 320px tall and
   they were using half of it -- and buys back a quarter of the desk's width, so five drums are in view where three were
   (Mike, 2026-09-20: "channels can be thinner with fx stacked"). */
.bb-strip { flex: none; display: grid; grid-template-columns: 62px; justify-items: center; gap: 6px; padding: 8px 4px 10px;
  background: linear-gradient(#141b1f, #0e1417); border-left: 1px solid #222b31; scroll-snap-align: center; }
.bb-strip:first-child { border-left: 0; }
.bb-strip[data-unused] { opacity: .45; }
/* The scribble strip: the drum's name on a plate at the head of the channel, the way a desk tapes it there. */
.bb-strip-name { display: block; width: 100%; height: 34px; padding: 4px 3px; border-radius: 3px;
  background: #171e22; box-shadow: inset 0 1px 0 #2b353b; color: #cfd6da; font: 10px/1.3 inherit;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center; }
.bb-strip-name .bb-slot-key { display: block; margin: 1px 0 0; color: #5f6b72; font-size: 8px; }
.bb-strip-name .bb-swapped { color: #ffc46a; }
.bb-strip.bb-engaged .bb-strip-name { color: #3fe0e0; font-weight: 700; }
/* Solo and mute stack and take the channel's full width: this is worked with a thumb, and a 24px square is not a
   target (Mike, 2026-09-20: "solo and mute can stack too - people with fat fingers need to be able to work it"). */
.bb-strip-keys { display: grid; gap: 4px; width: 100%; }
/* The channel's sample key: a waveform under Mute, the same plate as S and M. It is always on the channel, and goes
   grey and dead where there is no archive to search — the deployed machine, or a browser that is not the one
   serve.mjs runs on. Hiding it read as the picker having been taken away again (Mike, 2026-09-20: "mixer makes good
   but i don't see it"); a tool you are missing is shown greyed, not hidden. */
.bb-swap-key svg { width: 16px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
.bb-swap-key[data-on="true"] { color: #3fe0e0; border-color: #3fe0e0; }
.bb-trim:not(.bb-archive) .bb-swap-key:not([data-colors]) { opacity: .32; filter: grayscale(1); }
.bb-strip .bb-trim-flag { width: 100%; height: 30px; font-size: 12px; }

/* The fader stands in an engraved well. Rotated rather than writing-mode, so it is the same control and the same cap
   on every browser the game runs on. */
/* The fader stands by being centred and then turned, not by being nudged: the 172px control is centred in the 84px
   well by the grid, and rotating about its own centre leaves it exactly there. The well's column is a fixed 84px, so
   the control's wider layout box can never set the channel's width. */
/* The standing fader gives back what the bigger knobs and the stacked keys took: 150px of travel still reads a level
   at half a dB, and the channel stays inside a phone's tray. */
.bb-strip-fader { position: relative; display: grid; place-items: center; width: 62px; height: 150px; }
/* The notch: where this fader stood before you moved it, in the same color as the line under the channel. It sits on
   the fader's own travel -- 12px in at each end, which is half the cap -- so it lines up with where the cap was. It is
   painted behind the fader, so a notch the cap has not left yet is hidden rather than drawn across the metal face
   (Mike, 2026-09-20: "no line on this thing"; the cap carries nothing, same as 2026-09-18). */
.bb-strip[data-changed] .bb-strip-fader::after { content: ""; position: absolute; left: 12px; right: 12px; height: 2px;
  bottom: calc(12px + (100% - 24px) * var(--bb-t, .5)); border-radius: 1px; background: var(--bb-moved); }
.bb-strip-fader input[type="range"] { position: absolute; z-index: 1; left: 50%; top: 50%; width: 150px; height: 58px; transform: translate(-50%, -50%) rotate(-90deg); }
/* A channel with no slot to trim says where its level is instead, where its fader would stand. */
/* A drum that plays another pad's sample names that pad's bus where its own fader would stand: two words, quiet, so
   the channel says where its level is without a sentence (Mike, 2026-09-20: "just say PERC / BUS"). */
  font: 700 11px/1.2 var(--bb-readout); text-align: center; text-transform: uppercase; letter-spacing: 1px; padding: 0 4px; }
/* A drum the kit has no sample for does not play: Dropbeat is a sampler, and the synth stand-in sounded nothing like
   the machine around it. The channel says so and offers nothing to set (Mike, 2026-09-20). */
/* The level reads in its own little window under the fader, the way a channel prints its trim. */
.bb-strip > output { width: 62px; padding: 2px 0; border-radius: 3px; background: #0a0f12; box-shadow: inset 0 1px 1px #000a;
  color: #9aa7ae; font: 11px/1.3 var(--bb-readout); font-variant-numeric: tabular-nums; text-align: center; }

.bb-strip.bb-engaged > output { color: #3fe0e0; }
/* The sends stack at the top of the channel, both always out, and the dial takes the width: the name goes under it and
   the percentage goes away, because the ring already says how far it is turned and this is a control for a thumb
   (Mike, 2026-09-20: "so knobs can be bigger - % not necessary"). */
.bb-strip .bb-sends { flex-direction: column; margin: 0; padding: 0; border: 0; gap: 6px; }
.bb-strip .bb-knob { width: 100%; padding: 0; gap: 2px; grid-template-columns: 100%; grid-template-areas: "dial" "name"; justify-items: center; }
.bb-strip .bb-knob-dial { grid-area: dial; }
.bb-strip .bb-knob-name { grid-area: name; font-size: 9px; letter-spacing: .4px; }
.bb-sends-locked { height: 118px; }
/* A drum's tab is its solo key too: held, it lights the solo yellow the board uses for a held pad; latched by a double
   tap, it stays lit until the next double tap. Muted reads in the mute pink, so the tab row says what each drum is
   doing in the mix without leaving the pattern (Mike, 2026-09-20). */
.bb-beat-drums button.bb-soloing, .bb-beat-drums button[data-soloed] { border-color: #ffd183; color: #ffd183; }
.bb-beat-drums button[data-soloed]::after { content: "\2022"; margin-left: 5px; color: #ffd183; }
.bb-beat-drums button[data-muted] { border-color: #ff7bc8; color: #ff7bc8; }

/* The drum channel is the master section's panel: same wash, same head, same flat fader, five knobs with their names
   under them and no numbers (Mike, 2026-09-20, picking A from the mockups). */
/* In the Beat tray the channel is a panel sitting below the grid, not a section butted onto it the way the master is
   butted onto the rack: it gets air above it and corners all round (Mike, 2026-09-20: "needs some real breath room
   here and make the top rounded corners - like it's a panel below"). */
/* The channel card carries a bigger knob row now, so it spends less on its own margins (Mike, 2026-09-20). */
.bb-master.bb-beat-chan { margin-top: 12px; border-radius: 6px; padding: 12px 12px 8px; }
/* Three to a row, always: a flex row wraps them into rags on a narrow tray, and a rack of controls that reflows is not
   a panel. The dial takes the column and shrinks with it rather than dropping to the next line. */
/* The channel's knobs are the machine's knobs, at the machine's size: they were shrunk to 46px to fit five across and
   were hard to get a thumb on (Mike, 2026-09-20: "all the knobs are small - hard for touch"). */
/* Two rows of three rather than one row of six: the sends on the first line, the drum's own feel under them
   (Mike, 2026-09-21: "on pattern all 3 fx in a second line"). Only the first carries the rule above it. */
.bb-beat-chan .bb-knobs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; justify-items: center; }
.bb-beat-chan .bb-knobs { margin-top: 4px; padding-top: 6px; }
.bb-beat-chan .bb-chan-fx + .bb-knobs { margin-top: 0; padding-top: 4px; border-top: 0; }
.bb-beat-chan .bb-knob { width: 100%; gap: 2px; padding: 0 0 2px; grid-template-columns: 100%; grid-template-areas: "dial" "name"; justify-items: center; }
.bb-beat-chan .bb-knob-dial { grid-area: dial; width: 52px; height: 52px; }
.bb-beat-chan .bb-knob-name { grid-area: name; font-size: 9px; letter-spacing: .4px; }
/* A knob whose zero is the middle fills from the middle, not from the left. */
.bb-knob[data-bipolar] .bb-knob-dial {
  background: conic-gradient(from 220deg, #090e11 0 calc(var(--bb-from) * 280deg), #3fe0e0 calc(var(--bb-from) * 280deg) calc(var(--bb-to) * 280deg), #090e11 calc(var(--bb-to) * 280deg) 280deg, #0000 280deg); }

/* The level on the drum channel reads like the master's: the fader takes the line and the number sits at its end. */
.bb-beat-level { width: 100%; }
.bb-beat-level-out { color: #9aa7ae; font: 13px/1 var(--bb-readout); font-variant-numeric: tabular-nums; text-align: right; min-width: 3.6em; }
/* The color pack in the Kit view: the machine's three substitutable slots, each naming what is on it now. */
.bb-kit-colors-note { margin-top: 14px; padding-top: 12px; border-top: 1px solid #222a2f; }
.bb-color-slots { display: grid; gap: 6px; }
.bb-color-key .bb-kit-id.bb-swapped { color: #ffc46a; }
/* FX: the master channel of the same build, the whole-mix effects beside it, and room for the effects still to come. */
.bb-fx-desk { display: flex; gap: 16px; align-items: flex-start; padding: 10px 0 4px; }
.bb-fx-desk .bb-strip-master { flex: none; }
.bb-strip-master { background: linear-gradient(#1b242a, #121a1e); border-left: 0; border-radius: 6px; }
.bb-strip-master .bb-strip-name { width: 62px; background: #202b31; color: #8fa3b0; font: 700 10px/1.3 var(--bb-readout); letter-spacing: 1.2px; text-transform: uppercase; }
.bb-fx-desk .bb-fx { flex: 1; min-width: 0; margin: 0; }

/* The master panel, under the channels, the way a desk keeps it: the name with the board's own S and M on one line,
   the fader lying flat with its level beside it, then the effects and the room they play in. */
/* The master's wash runs to the top of the panel: it had a dark band above it where the margin sat, so the gradient
   started partway down and the panel read as two things (Mike, 2026-09-20: "make the color wash here go to the top"). */
.bb-trim-master { margin: 0; }
.bb-master { display: grid; gap: 10px; margin: 20px 0 0; padding: 16px 12px 14px; border-radius: 6px;
  background: linear-gradient(#22303a, #121a1e 70%); box-shadow: inset 0 1px 0 #3a4a55; }
.bb-master-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.bb-master-name { color: #8fa3b0; font: 700 11px/1 var(--bb-readout); letter-spacing: 1.4px; text-transform: uppercase; }
.bb-master .bb-trim-keys { display: flex; gap: 6px; }
.bb-master .bb-trim-flag { width: 34px; height: 30px; font-size: 12px; }
/* A tool the ladder has not handed over yet, and a tab whose pad has not landed: shown where it will be, dark and
   inert, so you can see what you are working towards (Mike, 2026-09-20: "always show what they're missing - gray it
   out"). The control underneath is the real one, so the greyed shape is exactly the shape you get. */
/* A locked tool is shown exactly where and how the earned one will be: the wrapper generates no box at all, so the
   controls inside it stay in their parent's grid rather than stacking in a box of their own — two send knobs on a
   channel were wrapping onto two lines and shrinking to fit (Mike, 2026-09-20: "should be normal button size and
   position no?"). The greying rides on the controls themselves. */
.bb-locked-tool { display: contents; }
.bb-locked-tool > * { opacity: .32; filter: grayscale(1); cursor: pointer; }
.bb-beat-views .bb-view-locked { opacity: .35; }
.bb-die-key { padding: 0; }
.bb-die { width: 16px; height: 16px; fill: currentColor; }
.bb-die rect { fill: none; stroke: currentColor; stroke-width: 1.4; }
.bb-die-key:active { border-color: #3fe0e0; color: #3fe0e0; }
.bb-master-level { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; }
.bb-master-level { position: relative; }
.bb-master-level input[type="range"] { position: relative; z-index: 1; width: 100%; }
.bb-master[data-changed] .bb-master-level::after { content: ""; position: absolute; top: 2px; bottom: 2px; width: 2px;
  left: calc(8px + (100% - 16px) * var(--bb-t, .5)); border-radius: 1px; background: var(--bb-moved); }
.bb-master-level output { color: #9aa7ae; font: 13px/1 var(--bb-readout); font-variant-numeric: tabular-nums; text-align: right; min-width: 3.6em; }
.bb-master .bb-fx { display: grid; gap: 10px; margin: 0; }
.bb-master .bb-knobs { margin: 0; padding: 10px 0 0; border-top: 1px solid #2a3440; justify-content: space-around; }
.bb-master .bb-knob output { display: none; }
/* A knob with real units says the number: a percentage under a 46px dial is no readout, but +6 st is (Mike,
   2026-09-20). The ring still carries the amount; this carries which amount it is. */
/* The channel has no room for a figure under every dial and does not want one: the name line says it (app.mjs knobLabel). */
.bb-beat-chan .bb-knob output, .bb-strip .bb-knob output, .bb-master .bb-knob output { display: none; }
.bb-beat-chan .bb-knob-name, .bb-strip .bb-knob-name { font-variant-numeric: tabular-nums; }

/* Knobs. Cut from the same machine as the faders: an engraved ring that fills cyan as it turns, a milled cap with one
   pointer, and the amount under it in the readout face. Two to a row, centred, so the panel reads as a small rack
   rather than a form (Mike, 2026-09-19: "maybe some knobs - this is an audio game - make it fucking cool"). */
.bb-knobs { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 22px; margin-top: 10px; padding-top: 12px; border-top: 1px solid #222a2f; }
.bb-knob { display: grid; justify-items: center; gap: 4px; width: 74px; padding: 2px 0 6px; touch-action: none; cursor: ns-resize; }
.bb-knob:focus { outline: none; }
/* The ring: 280 degrees of travel from bottom-left round to bottom-right, the filled part cyan, the rest the fader's
   own engraved slot. --bb-turn is 0..1 and is the only thing the drag writes. */
/* The unfilled travel is the fader's own engraved slot, so the ring reads as a groove cut in the panel with the same
   lip of light under it; the 40 degrees left open at the bottom stay panel-colored. */
.bb-knob-dial { position: relative; display: block; width: 54px; height: 54px; border-radius: 50%;
  background: conic-gradient(from 220deg, #3fe0e0 0 calc(var(--bb-turn) * 280deg), #090e11 calc(var(--bb-turn) * 280deg) 280deg, #0000 280deg);
  box-shadow: inset 0 0 0 1px #000c, 0 1px 0 #2b353b; }
/* The cap sits inside the groove, lit from above like the fader's silver, with a close shadow under it. */
.bb-knob-dial::before { content: ""; position: absolute; inset: 8px; border-radius: 50%; border: 1px solid #8b99a2;
  background: linear-gradient(#f3f6f8, #dbe3e8 55%, #ccd7dd); box-shadow: 0 1px 3px #000a, inset 0 1px 0 #fff; }
/* One pointer, from the cap's centre to its edge, turning with the value. */
.bb-knob-dial i { position: absolute; left: 50%; top: 10px; width: 2px; height: 15px; margin-left: -1px; border-radius: 1px;
  background: #3a464e; transform-origin: 50% 17px; transform: rotate(calc(-140deg + var(--bb-turn) * 280deg)); }
.bb-knob-name { color: #7d8990; font: 700 9px/1 var(--bb-readout); letter-spacing: .6px; text-transform: uppercase; }
.bb-knob output { color: #9aa7ae; font: 12px/1 var(--bb-readout); font-variant-numeric: tabular-nums; }
/* A knob off zero lights its own readout and ring glow, the way a fader off its packaged level lights its slot. */
.bb-knob[aria-valuenow="0"] output { color: #5f6b72; }
.bb-knob:not([aria-valuenow="0"]) .bb-knob-dial { box-shadow: inset 0 0 0 1px #0b1013, 0 0 10px #3fe0e040; }
.bb-knob:focus-visible .bb-knob-dial::before { border-color: #3fe0e0; box-shadow: 0 0 0 2px #3fe0e0, inset 0 1px 0 #fff; }
.bb-sends { margin-top: 8px; }
.bb-swap-box { margin-top: 8px; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 8px; align-items: center; }
.bb-swap-for { color: #e8eff3; }
.bb-swap-q { min-height: 26px; padding: 2px 6px; border: 1px solid #3a4348; border-radius: 4px; background: #1c2124; color: #cfd6da; font: inherit; }
.bb-swap-hits { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 4px; max-height: 17em; overflow-y: auto; }
.bb-swap-hit small { display: block; color: #7d8990; font-size: 10px; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }
.bb-swap-hit { flex: none; text-align: left; padding: 3px 6px; border: 0; border-radius: 3px; background: #1c2124; color: #cfd6da; font: inherit; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bb-swap-hit:hover, .bb-swap-hit:focus { background: #2a3238; }
.bb-swap-none { color: #7d8990; padding: 3px 6px; }
.bb-trim-rows label small { color: #7d8990; font-size: 10px; }
.bb-trim-rows input[type="range"] { width: 100%; margin: 0; accent-color: #9aa7ae; }
.bb-trim-rows output { text-align: right; }
/* A row off its packaged value (unsaved) goes amber, slider and readout alike; a swapped slot's name too. */
.bb-trim-rows input[type="range"].bb-changed { accent-color: #ffc46a; }
.bb-trim-rows output.bb-changed { color: #ffc46a; }
/* Master: set apart under the slots. */
/* The master panel is the surface itself: it used to be a box inside a box, with the rows wrapper painting its own dark
   rounded ground and a 12px margin above the panel, which is the band that sat over the wash (Mike, 2026-09-20). */
.bb-trim-master { border: 0; border-radius: 0; background: none; }
.bb-trim-foot { display: flex; gap: 8px; align-items: center; margin-top: 16px; }
.bb-trim-spacer { flex: 1; }
/* The tray's own keys wear the front row's chrome, scaled to the foot: the same gradient and edge, the same raised
   lip, the same 3px press (Mike, 2026-09-20). They were flat plates before, the only unmoulded keys on the machine. */
.bb-trim-save, .bb-trim-reset, .bb-beat-save, .bb-beat-reset { min-height: 28px; margin-bottom: 4px; padding: 4px 11px 5px; border: 1px solid #9eadb6; border-bottom-color: #5c6f7b; border-radius: 4px; background: linear-gradient(#e3eaee, #c2ced5 60%, #aebbc3); color: #2e4555; font: inherit; font-weight: 600; text-shadow: 0 1px 0 #ffffffb0; box-shadow: 0 3px 0 #7d8f9b, 0 4px 0 1px #3f4c54, 0 6px 6px -2px #0007, inset 0 1px 0 #f7fafc, inset 0 -1px 0 #ffffff40; }
.bb-trim-save:not(:disabled):active, .bb-trim-reset:not(:disabled):active, .bb-beat-save:not(:disabled):active, .bb-beat-reset:not(:disabled):active { transform: translateY(3px); box-shadow: 0 0 0 #7d8f9b, 0 1px 0 1px #3f4c54, inset 0 1px 0 #f7fafc; }
@keyframes bb-swap-pulse { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.2); box-shadow: 0 0 6px 1px rgba(255,255,255,.35); } }
@media (prefers-reduced-motion: reduce) { .bb-pad.bb-swap-source { animation: none; box-shadow: inset 0 0 0 3px #fff; } }

/* A single top-contact opportunity per column; spending it removes its marker. */
/* Hold is the lane's last bay, marked off by an engraved divider with HOLD printed up its side (Mike, 2026-10-06:
   Hold was "easy to ignore"): a parked tile rests where tiles already are, so it reads as a place, not a command.
   Empty, a recessed tile outline; new, the legend and outline light amber like an LED behind a print. */
.bb-queue .bb-lane-hold { position: relative; display: grid; place-items: center; flex: none; align-self: stretch; min-width: 58px; margin: -5px -5px -5px auto; padding: 0 3px 0 13px; border: 0; border-left: 1px solid #0007; border-radius: 0 5px 5px 0; background: rgba(0, 0, 0, .18); box-shadow: inset 1px 0 0 #ffffff12; outline: none; cursor: pointer; }
.bb-queue .bb-lane-hold:disabled { cursor: default; }
.bb-queue .bb-lane-hold:not(:disabled):active { background: rgba(0, 0, 0, .3); }
.bb-queue .bb-lane-hold .bb-token { width: 38px; height: 36px; border-radius: 4px; box-shadow: inset 0 1px 1px #ffffff2e, inset -1px -2px 1px #000b, 0 2px 0 #08090a; }
.bb-queue .bb-lane-hold .bb-token::after { display: none; }
.bb-queue .bb-lane-hold .bb-symbol { width: 25px; height: 24px; }
.bb-hold-legend { position: absolute; top: 0; bottom: 0; left: 1px; width: 10px; display: grid; place-items: center; writing-mode: vertical-rl; transform: rotate(180deg); font: 700 7px/1 var(--bb-readout); letter-spacing: .24em; text-transform: uppercase; color: #c3ccd1; pointer-events: none; }
.bb-hold-empty { display: block; width: 38px; height: 36px; border: 1px solid #ffffff40; border-radius: 5px; box-shadow: inset 0 1px 2px #0006; }
/* Until it is first used the bay pulses amber hard enough to be seen mid-run: the faint glow it had went unnoticed
   (Mike, 2026-10-09: "we're not advertising it enough, the flashing should be more obvious"). */
.bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) { animation: bb-hold-new 1s ease-in-out infinite; }
@keyframes bb-hold-new { 50% { background: rgba(255, 176, 74, .28); box-shadow: inset 1px 0 0 #ffffff12, inset 0 0 14px #ffb04a99; } }
.bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) .bb-hold-legend { color: #ffc46a; animation: bb-hold-legend 1s ease-in-out infinite; }
@keyframes bb-hold-legend { 50% { color: #fff1d6; text-shadow: 0 0 6px #ffb04a; } }
.bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) .bb-hold-empty { border: 2px solid #ffc46a; animation: bb-hold-slot 1s ease-in-out infinite; }
@keyframes bb-hold-slot { 0%, 100% { box-shadow: 0 0 4px #ffb04a66, inset 0 0 4px #ffb04a40; } 50% { border-color: #ffe0ad; box-shadow: 0 0 14px #ffb04acc, inset 0 0 10px #ffb04a99; } }
@media (prefers-reduced-motion: reduce) { .bb-queue .bb-lane-hold[data-new="true"]:not(:disabled), .bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) .bb-hold-legend, .bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) .bb-hold-empty { animation: none; } .bb-queue .bb-lane-hold[data-new="true"]:not(:disabled) { background: rgba(255, 176, 74, .2); } }
/* When Hold is the only move left the whole bay flashes, fast and bright, over the new-tile pulse (Mike, 2026-10-10:
   "flash that thing ... somebody's gonna think it's broken"). */
.bb-queue .bb-lane-hold[data-only="true"]:not(:disabled) { animation: bb-hold-only .45s steps(1, end) infinite; }
@keyframes bb-hold-only { 0% { background: #ffb04a; box-shadow: 0 0 18px #ffb04a, inset 0 0 10px #fff3; } 50% { background: rgba(0, 0, 0, .18); box-shadow: inset 1px 0 0 #ffffff12; } }
.bb-queue .bb-lane-hold[data-only="true"]:not(:disabled) .bb-hold-legend { color: #fff; animation: none; }
@media (prefers-reduced-motion: reduce) { .bb-queue .bb-lane-hold[data-only="true"]:not(:disabled) { animation: none; background: #ffb04a; } }
@media (max-width: 389px) { .bb-queue .bb-lane-hold { min-width: 54px; padding: 0 2px 0 12px; } }
@media (max-width: 370px) { .bb-queue .bb-lane-hold { margin: -4px -4px -4px auto; } .bb-queue .bb-lane-hold .bb-token, .bb-hold-empty { width: 36px; height: 34px; } }
/* Pause is a small round black rubber key in a machined ring at the nameplate's right corner while a run is live, away from
   every tap zone; its ::after widens the hit area. It is the only way to pause; a tap on the LCD resumes. */
.bb-nameplate .bb-pause-key { flex: none; position: relative; width: 30px; min-width: 0; height: 30px; min-height: 0; margin: -4px 4px -4px 0; padding: 0; display: grid; place-items: center; border: 1px solid #8c9091; border-radius: 50%; background: radial-gradient(circle at 50% 35%, #3a3f43, #222629 60%, #15181a); border-color: #0c0d0e; color: #e6ecef; box-shadow: 0 0 0 2px #7a7e7f, 0 0 0 3px #ffffff70, 0 2px 0 2px #0c0d0e, inset 0 1px 0 #ffffff26; outline: none; }
.bb-nameplate .bb-pause-key::before { content: ''; position: absolute; inset: 1px; border-radius: 50%; background: url('./rubber-grain.svg') repeat; background-size: 112px 112px; opacity: .3; mix-blend-mode: overlay; pointer-events: none; }
.bb-nameplate .bb-pause-key::after { content: ''; position: absolute; inset: -10px -10px -10px -14px; }
.bb-nameplate .bb-pause-key:not(:disabled):active { transform: translateY(1px); box-shadow: 0 0 0 2px #7a7e7f, 0 0 0 3px #ffffff70, 0 1px 0 2px #0c0d0e, inset 0 1px 3px #000a; }
.bb-nameplate .bb-pause-key:disabled { color: #5d666b; }
.bb-nameplate .bb-pause-key .bb-transport-icon { position: relative; width: 11px; height: 11px; margin: 0; }
/* A closed column's top pad: it lights, and the pad glows amber when the tile in hand can lock it from above. */
.bb-pad.bb-parked { box-shadow: inset 0 0 0 1px #9a742f, 0 0 6px #ffd18355; }
.bb-pad.bb-parked[data-ready="true"] { box-shadow: inset 0 0 0 2px #e4c384, 0 0 10px #ffd183aa; }
.bb-beat-swing-drum { display: flex; justify-content: flex-end; gap: 6px; margin-top: 10px; padding-right: 28px; }
.bb-beat-swing { display: grid; grid-template-columns: minmax(60px, 1fr) 5em; gap: 10px; align-items: center; margin: 22px 0 0; font-size: 11px; color: #cfd6da; }
.bb-beat-swing .bb-beat-lane-name { grid-column: 1 / -1; margin-bottom: 4px; }
.bb-beat-swing input[type="range"] { width: 100%; margin: 0; accent-color: #9aa7ae; }
.bb-beat-swing output { text-align: right; font-variant-numeric: tabular-nums; }
.bb-beat-swing.bb-changed input[type="range"] { accent-color: #ffc46a; }
.bb-beat-swing.bb-changed output { color: #ffc46a; }
/* Feel sits under Swing with the same layout; its readout is words, not a percent, so it takes a wider column. */
.bb-beat-feel { grid-template-columns: minmax(60px, 1fr) 6.5em; margin-top: 16px; }
/* The drum channel's two keys. They used to wear the swing key's class because they sat beside it; the swing flag now
   lives on the Order row, with the drum's other settings (Mike, 2026-09-20: "remove swing"). */

/* The Career face: the device flipped over (data-face="career"). A static chrome heading, who you are, that a visitor
   will see on your page; four keys; one black panel that every view paints on, running to the foot of the device. */
.bb-career-panel { display: flex; flex-direction: column; padding: 14px 14px 12px; }
.bb-career-who { display: flex; align-items: center; gap: 12px; }
.bb-career-lines { flex: 1; min-width: 0; }
.bb-career-handle { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #25313a; font-size: 22px; line-height: 1.05; font-weight: 700; letter-spacing: -.6px; text-shadow: 0 1px 0 #ffffff70; }
.bb-career-rank { margin-top: 4px; color: #4b5b66; font: 10px/1.4 var(--bb-readout); letter-spacing: .6px; text-transform: uppercase; text-shadow: 0 1px 0 #ffffffb8; }
.bb-career-cash { display: inline-block; margin-left: 6px; padding: 2px 7px; border-radius: 3px; background: #11181e; box-shadow: inset 0 1px 2px #000c, 0 1px 0 #ffffffb8; color: var(--bb-amber); font: 700 15px/1.2 var(--bb-readout); letter-spacing: 0; text-shadow: 0 0 6px #ffd18366; vertical-align: middle; }
/* The way out sits in the top right corner, where the campaign map's does (Mike, 2026-09-17). */
.bb-career-close { align-self: start; display: grid; place-items: center; width: 36px; min-height: 36px; padding: 0; }
.bb-career-close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.bb-career-close { padding: 0 !important; }
.bb-career-close { flex-shrink: 0; min-height: 36px; padding: 6px 10px; border: 1px solid #9eadb6; border-bottom-color: #718591; border-radius: 4px; background: linear-gradient(#d7e0e5, #b6c3cc); color: #2e4555; font: 600 12px/1.2 inherit; font-family: inherit; box-shadow: 0 2px 0 #8395a1, inset 0 1px 0 #f5f9fc; }
.bb-career-about { margin: 12px 0 0; color: #3f5260; font-size: 12px; line-height: 1.5; }
.bb-career-bio { margin: 6px 0 0; color: #25313a; font-size: 12px; line-height: 1.5; font-weight: 600; }
.bb-career-links { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; margin-top: 8px; }
.bb-career-link-list { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; }
.bb-career-link-list a { display: inline-flex; align-items: center; gap: 6px; color: #34617c; font-size: 12px; font-weight: 600; text-decoration: none; text-shadow: 0 1px 0 #ffffff90; }
.bb-career-ghost { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: none; font: inherit; color: #25313a; opacity: .38; font-style: italic; cursor: pointer; text-shadow: inherit; }
.bb-career-ghost:hover, .bb-career-ghost:focus-visible { opacity: .7; outline: none; }
.bb-career-link-list .bb-career-ghost { font-style: normal; font-weight: 600; font-size: 12px; }
.bb-career-link-list svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
/* The Career keys are the front's transport keys (Mike, 2026-09-16): raised, icon over legend. */
.bb-career-keys { margin-top: 14px; }
.bb-career-view { position: relative; flex: 1; min-height: 360px; margin-top: 12px; display: flex; flex-direction: column; gap: 12px; color: #cfd6da; font: 12px/1.4 var(--bb-readout); }
.bb-career-lcd { position: relative; padding: 16px; border: 1px solid #090c0f; border-radius: 4px; background: linear-gradient(120deg, #1c242b, #11181e 72%, #1b2228); box-shadow: 0 1px 0 #ffffffbd, 0 -1px 0 #62676b, inset 0 2px 4px #000c, inset 1px 0 2px #0008; }
.bb-career-lcd.bb-career-list { flex: 1; min-height: 0; padding-bottom: 0; }
.bb-career-lcd.bb-career-scroll { flex: 1; min-height: 0; }
.bb-career-lcd h2 { margin: 0 0 6px; color: #ffd183; font: 700 9px/1.4 var(--bb-readout); letter-spacing: 1.4px; text-transform: uppercase; }
/* Rank and skill tokens: the game's pads, lit. Earned teal, the next one amber, the rest dark with no name. */
.bb-rank-token, .bb-skill-token { --bb-skill-hi: #ffd183; --bb-skill: #ffc46a; display: grid; place-items: center; flex: none; border-radius: 5px; border: 1px solid var(--bb-skill); background: radial-gradient(ellipse at 43% 30%, #262a2c, #1a1e20 70%, #131617); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bb-skill) 50%, transparent), 0 3px 0 #08090a, 0 4px 0 1px color-mix(in srgb, var(--bb-skill) 60%, transparent); color: var(--bb-skill-hi); font-weight: 700; text-shadow: 0 0 10px color-mix(in srgb, var(--bb-skill) 60%, transparent); }
.bb-rank-token { width: 52px; height: 52px; font-size: 26px; }
.bb-rank-token-big { width: 72px; height: 72px; font-size: 36px; border-radius: 6px; }
.bb-skill-token { width: 44px; height: 44px; padding: 0; font: 700 15px/1 inherit; font-family: inherit; letter-spacing: -.5px; }
/* Each rank owns a color (Mike, 2026-09-17): the Collector's amber, the Arranger's cyan, the Creator's magenta, the
   Producer's orange (gold would read as the amber of the next skill). Four ranks since 2026-10-14, one a run.
   Everything tinted by rank reads these four variables. */
[data-rank] { --bb-skill-hi: #5cf0f0; --bb-skill: #2ec2c2; --bb-skill-lo: #22a8a8; --bb-skill-ink: #04191b; }
[data-rank="Collector"] { --bb-skill-hi: #ffd183; --bb-skill: #ffc46a; --bb-skill-lo: #e0a94f; --bb-skill-ink: #2a1a02; }
[data-rank="Creator"] { --bb-skill-hi: #ff9ae6; --bb-skill: #e75fc6; --bb-skill-lo: #c948aa; --bb-skill-ink: #2a0622; }
[data-rank="Producer"] { --bb-skill-hi: #ffb078; --bb-skill: #f2712e; --bb-skill-lo: #d55d1c; --bb-skill-ink: #2e0f02; }
/* Earned wears the color, not an outline of it (Mike, 2026-09-17): the token fills and the letters go dark. */
.bb-skill-token[data-state="earned"] { border-color: color-mix(in srgb, var(--bb-skill) 90%, transparent); background: linear-gradient(var(--bb-skill-hi), var(--bb-skill) 70%, var(--bb-skill-lo)); box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 3px 0 #08090a, 0 4px 0 1px color-mix(in srgb, var(--bb-skill) 45%, transparent); color: var(--bb-skill-ink); text-shadow: 0 1px 0 rgba(255,255,255,.35); }
.bb-skill-token[data-state="next"] { box-shadow: inset 0 0 0 1px var(--bb-skill), 0 3px 0 #08090a, 0 4px 0 1px var(--bb-skill); }
.bb-skill-token[data-state="locked"] { border-color: #3a3f42; box-shadow: inset 0 0 0 1px #000a, 0 2px 0 #08090a; color: #4a5155; text-shadow: none; opacity: .6; }
.bb-skill-token[aria-pressed="true"] { box-shadow: inset 0 0 0 1px var(--bb-skill), 0 3px 0 #08090a, 0 4px 0 1px var(--bb-skill), 0 0 0 2px #fff; }
.bb-skill-token[data-state="earned"][aria-pressed="true"] { box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 3px 0 #08090a, 0 4px 0 1px color-mix(in srgb, var(--bb-skill) 45%, transparent), 0 0 0 2px #fff; }
.bb-skill-token[data-state="next"][aria-pressed="true"] { box-shadow: inset 0 0 0 1px var(--bb-skill), 0 3px 0 #08090a, 0 4px 0 1px var(--bb-skill), 0 0 0 2px #fff; }
.bb-career-next { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bb-career-next-who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.bb-career-next-label { color: var(--bb-skill-hi, #ffc46a); font-weight: 700; letter-spacing: 1px; font-size: 9px; }
.bb-career-next-name { margin-top: 3px; color: #e8eff3; font: 700 18px/1.2 var(--bb-face, inherit); font-family: inherit; letter-spacing: -.3px; }
.bb-career-next-about { margin-top: 2px; color: #9aa7ae; font-size: 10px; }
.bb-career-next-count { text-align: right; }
.bb-career-next-count strong { display: block; color: var(--bb-skill-hi, #ffc46a); min-height: 30px; font-size: 30px; line-height: 1; font-weight: 500; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.bb-career-next-count span { display: block; margin-top: 3px; color: #9aa7ae; font-size: 10px; }
.bb-career-seg { display: flex; gap: 3px; margin-top: 12px; }
.bb-career-seg i, .bb-finish-seg i { flex: 1; height: 7px; border-radius: 2px; background: #2a3439; }
.bb-finish-seg i.bb-lit { background: #3fe0e0; box-shadow: 0 0 5px #3fe0e088; }
.bb-career-seg i.bb-lit { background: var(--bb-skill-hi); box-shadow: 0 0 5px color-mix(in srgb, var(--bb-skill-hi) 55%, transparent); }
.bb-career-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid #2a3034; }
.bb-career-row:last-child { border-bottom: 0; }
.bb-career-row-main { min-width: 0; }
.bb-career-row-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #e8eff3; font: 600 15px/1.3 inherit; font-family: inherit; letter-spacing: -.2px; }
.bb-career-row-name.bb-edited, .bb-career-row-name.bb-all-pads { color: #3fe0e0; }
.bb-career-row-note { margin-top: 2px; color: #7d8990; font-size: 10px; }
.bb-career-row-note .bb-edited { color: #3fe0e0aa; }
.bb-career-row-keys { display: flex; align-items: center; gap: 6px; }
.bb-career-row-place { width: 26px; flex: none; color: #7d8990; font-size: 11px; }
.bb-career-row-score { color: #ffe16b; font-size: 16px; font-weight: 500; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
/* A Scores row is two things: the row itself opens the level, the Share key beside it sends the score. The inner key
   carries the press, so the border and the padding stay on the row (Mike, 2026-09-19). */
.bb-career-row-open { display: flex; flex: 1; min-width: 0; align-items: center; gap: 12px; }
#bb-pairs .bb-career-row-key.bb-career-row-open { width: auto; padding: 0; border-bottom: 0; }
.bb-career-row.bb-career-row-key { cursor: pointer; }
.bb-career-row[data-mine="true"] { margin: 2px -10px 0; padding-left: 10px; padding-right: 10px; border-radius: 4px; border-bottom: 0; background: rgba(255,196,106,.08); box-shadow: inset 0 0 0 1px rgba(255,196,106,.45); }
.bb-career-row[data-mine="true"] .bb-career-row-place, .bb-career-row[data-mine="true"] .bb-career-row-name { color: #ffe16b; }
.bb-dkey { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 30px; padding: 0 10px; border: 1px solid #3a4348; border-radius: 3px; background: #1c2124; color: #cfd6da; font: 600 11px/1 inherit; font-family: inherit; letter-spacing: .5px; }
.bb-dkey.bb-on { color: #3fe0e0; border-color: #3fe0e0; }
.bb-dkey.bb-dim { color: #7d8990; }
.bb-dkey:disabled { opacity: .35; }
.bb-dkey svg { width: 12px; height: 12px; fill: currentColor; }
.bb-ikey { flex-direction: column; gap: 3px; width: 40px; min-height: 40px; padding: 4px 0 3px; }
.bb-ikey svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.bb-ikey svg .bb-led { fill: currentColor; stroke: none; }
.bb-ikey-bare { width: 32px; min-height: 32px; padding: 0; gap: 0; }
.bb-ikey-bare svg { width: 16px; height: 16px; }
.bb-career-row-level { flex: none; width: 34px; color: #7d8990; font-size: 11px; font-variant-numeric: tabular-nums; }
/* A Library row is two lines on the left, "2.4 Madchester" and "100 BPM", and glyph-only keys on the
   right standing the full height of those lines (Mike, 2026-09-26). */
.bb-lib-row { align-items: stretch; gap: 10px; }
.bb-lib-text { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 3px; }
.bb-lib-top { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.bb-lib-row .bb-career-row-level { width: auto; flex: none; font-size: 13px; }
.bb-lib-row .bb-career-row-note { margin: 0; font-size: 11px; }
.bb-lib-row .bb-career-row-keys { flex: none; align-items: stretch; gap: 5px; }
.bb-lib-row .bb-ikey { width: 38px; min-height: 36px; height: auto; padding: 0; }
.bb-lib-row .bb-ikey svg { width: 20px; height: 20px; }
/* A Scores row reads "2.4 Madchester" on one line, a size down from the Library's so the whole name fits. */
.bb-score-row .bb-career-row-name { font-size: 13px; letter-spacing: -.4px; }
/* High Scores as columns: place, level, moves, pads, score, then the Share key outside the tappable part. The whole row
   lights on hover or focus, the key with it, not just the part that opens the level. */
#bb-pairs .bb-score-cols, #bb-pairs .bb-career-row-key.bb-score-cols { display: grid; grid-template-columns: 22px minmax(0, 1fr) 56px; align-items: center; gap: 8px; }
#bb-pairs .bb-score-head { grid-template-columns: 22px minmax(0, 1fr) 56px 32px; padding: 4px 0 6px; color: #7d8990; font: 600 8px/1 var(--bb-readout); letter-spacing: 1.2px; text-transform: uppercase; border-bottom: 1px solid #2a3034; }
.bb-score-head span { white-space: nowrap; }
.bb-score-num { text-align: center; color: #7d8990; font-size: 11px; font-variant-numeric: tabular-nums; }
.bb-score-right { text-align: right; }
.bb-score-row { gap: 8px; margin: 0 -10px; padding-left: 10px; padding-right: 10px; }
.bb-score-row .bb-career-row-open { flex: 1; min-width: 0; }
.bb-score-row .bb-career-row-score { text-align: right; }
.bb-score-row .bb-career-row-keys { width: 32px; flex: none; justify-content: flex-end; }
.bb-score-row:hover, .bb-score-row:focus-within { background: rgba(255, 196, 106, .08); }
#bb-pairs .bb-score-row .bb-career-row-key:hover, #bb-pairs .bb-score-row .bb-career-row-key:focus-visible { background: none; }
.bb-career-views { display: flex; gap: 18px; margin-bottom: 10px; border-bottom: 1px solid #2a3034; }
.bb-career-views button { min-height: 28px; padding: 0 0 6px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; border-radius: 0; background: none; color: #7d8990; font: 600 11px/1 inherit; font-family: inherit; letter-spacing: 1px; text-transform: uppercase; }
.bb-career-views button[aria-selected="true"] { color: #3fe0e0; border-bottom-color: #3fe0e0; }
.bb-career-empty { margin: 14px 0; color: #7d8990; font-size: 12px; line-height: 1.5; }
.bb-career-form { display: grid; gap: 10px; }
.bb-career-form label { display: grid; gap: 4px; color: #9aa7ae; font-size: 10px; letter-spacing: .5px; text-transform: uppercase; }
.bb-career-form input, .bb-career-form textarea { width: 100%; box-sizing: border-box; padding: 8px 10px; border: 1px solid #3a4348; border-radius: 3px; background: #15191c; color: #e8eff3; font-size: 16px; line-height: 1.4; font-family: inherit; } /* 16px as separate properties: a `font:` shorthand with `inherit` is invalid and dropped, so the fields fell to 13px and iOS Safari zoomed into them (Mike, 2026-10-11). */
/* 16px, not 13: iOS zooms into any field under 16px when it takes focus and never zooms back out, so the device sat
   off to one side after the keyboard closed (Mike, 2026-10-09: "it moves the device to the side"). */
.bb-career-form textarea { min-height: 56px; resize: vertical; }
/* Start over asks first: the question in the key's place, and the way out reads as the plain choice. */
/* The Start over question opens above its key, not under it: the key is at the foot of the screen (Mike, 2026-09-21). */
.bb-career-form-keys { display: flex; gap: 8px; margin-top: 4px; }
.bb-career-form-keys .bb-dkey { flex: 1; min-height: 36px; }
.bb-career-h2-gap { margin-top: 14px !important; }
.bb-career-share-text { margin: 0 0 12px; color: #cfd6da; font-size: 13px; line-height: 1.5; }
.bb-key-amber { border: 1px solid rgba(255,209,131,.5); border-top-color: rgba(255,209,131,.7); border-bottom-color: #111416; border-radius: 4px; background: linear-gradient(145deg, #36393b, #202325 78%); color: #ffc46a; font: 600 14px/1 inherit; font-family: inherit; min-height: 44px; padding: 10px 12px; text-shadow: 0 0 5px rgba(255,209,131,.35), 0 0 12px rgba(255,209,131,.18); box-shadow: 0 3px 0 #0c0d0e, 0 4px 0 rgba(255,209,131,.7), 0 5px 0 1px #08090a, 0 6px 1px 1px #ffffff70, inset 0 1px 1px #ffffff2e, inset 0 -2px 1px #000b; }
.bb-teal { color: #3fe0e0; } .bb-amber { color: #ffc46a; } .bb-met { color: #5ee06a; } .bb-miss { color: #ffc46a; }

/* The closing dialog's cards. The result and Stage complete cards are the silver dialog with an LCD; Level up and Beat
   kept are black cards, amber and teal, so the run ends on its beat. */
.bb-finish-readout .bb-lcd-note { text-align: right; }
.bb-stage-screen { display: grid; gap: 9px; }
.bb-stage-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.bb-stage-head .bb-lcd-note:first-child { text-align: left; text-transform: uppercase; letter-spacing: .5px; }
.bb-stage-screen .bb-genre { font-size: 27px; }
.bb-stage-screen .bb-finish-bar { margin-top: 1px; padding-top: 11px; border-top: 1px solid #54677570; }
.bb-finish-eyebrow { display: block; margin-bottom: 2px; font-size: 12px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: #6b7a83; text-shadow: 0 1px 0 #ffffffb8; }
.bb-finish-bar { display: flex; align-items: center; justify-content: flex-start; gap: 12px; margin-top: 8px; }
.bb-finish-bar-label { color: #3fe0e0; font: 700 11px/1.4 var(--bb-readout); letter-spacing: .5px; text-transform: uppercase; white-space: nowrap; }
.bb-finish-bar-label.bb-met { color: #5ee06a; }
/* A kept beat is stated on the LCD itself: a record and the words in cyan, in place of the drum bar's squares. */
.bb-finish-bar-label[data-met="false"] { color: #ffc46a; }
.bb-finish-bar > :last-child { flex: 1; display: flex; justify-content: flex-start; }
.bb-new-scoring { display: block; margin: 12px 0 0; padding: 0; border: 0; background: none; color: #ffc46a; font: 700 12px/1.3 var(--bb-readout); letter-spacing: .4px; text-transform: uppercase; cursor: pointer; }
.bb-new-scoring[hidden] { display: none; }
.bb-score-grid { display: grid; grid-template-columns: 1fr auto; gap: 5px 14px; margin-top: 4px; font-size: 13px; }
.bb-score-grid .bb-new-points { margin: 0; text-align: right; }
.bb-finish-seg { display: flex; flex: 1; max-width: 140px; gap: 3px; }
.bb-finish-seg i.bb-green { background: #5ee06a; }
.bb-finish-seg[data-met="false"] i.bb-lit { background: #ffc46a; box-shadow: 0 0 5px #ffc46a88; }
/* The stage card: its own step before the level screen, because prose does not read on the machine's silver face
   (Mike, 2026-09-18). The dialog is the card — black to its edges, amber-lit, one key out of it. */
.bb-reset-keys { display: flex; gap: 10px; margin-top: 22px; }
.bb-reset-keys .bb-dialog-action { flex: 1; min-height: 48px; }
.bb-stage-dialog, .bb-new-dialog, .bb-reset-dialog { border-color: #ffd183; background: #101214; box-shadow: 0 0 0 1px #ffd18333, 0 0 28px #ffd1834d, 0 25px 70px #000c; }
.bb-stage-dialog .bb-dialog-body, .bb-new-dialog .bb-dialog-body, .bb-reset-dialog .bb-dialog-body { padding-top: 16px; }
.bb-stage-card, .bb-new-card { margin-bottom: 0; border-bottom: 1px solid #2a3034; }
/* A title at the card's tight tracking ran its words together ("Dig It" reading as "Digit"), so it sits at normal. */
.bb-stage-card .bb-finish-black-title { color: var(--bb-amber); letter-spacing: 0; }
.bb-stage-note { margin: 14px 0 0; color: #cfd6da; font-size: 15px; line-height: 1.6; text-wrap: pretty; }
.bb-finish-black { margin: -16px -20px 0; padding: 0; border-bottom: 1px solid #2a3034; background: #101214; color: #cfd6da; }
.bb-finish-dialog:has(.bb-finish-black:not([hidden])) { border-color: #3fe0e0; background: #101214; box-shadow: 0 0 0 1px #3fe0e033, 0 0 28px #3fe0e04d, 0 25px 70px #000c; }
.bb-finish-dialog:has(.bb-finish-amber:not([hidden])) { border-color: #ffd183; box-shadow: 0 0 0 1px #ffd18333, 0 0 28px #ffd1834d, 0 25px 70px #000c; }
/* Stage cleared is the green card: the stage you just finished, its four levels lit, and nothing about the next one —
   that stage introduces itself on the way in (Mike, 2026-09-18). */
.bb-finish-dialog:has(.bb-finish-green:not([hidden])) { border-color: #5ee06a; box-shadow: 0 0 0 1px #5ee06a33, 0 0 28px #5ee06a4d, 0 25px 70px #000c; }
.bb-finish-stage-head { color: #5ee06a; }
.bb-finish-stage .bb-finish-black-title { color: #5ee06a; }
.bb-finish-dialog:has(.bb-finish-black:not([hidden])) .bb-dialog-heading { display: none; }
.bb-finish-black-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 18px 12px; border-bottom: 1px solid #2a3034; font: 700 10px/1.4 var(--bb-readout); letter-spacing: 1.6px; }
.bb-finish-black-head > span:last-child { color: #7d8990; font-weight: 400; letter-spacing: .5px; }
.bb-finish-black-body { padding: 16px 18px 18px; }
.bb-finish-black-title { color: #e8eff3; font-size: 30px; line-height: 1; font-weight: 700; letter-spacing: -.9px; }
.bb-finish-black-note { margin-top: 6px; color: #9aa7ae; font: 12px/1.4 var(--bb-readout); }
.bb-finish-levelup-about { margin: 18px 0 0; color: #cfd6da; font-size: 14px; line-height: 1.55; }
.bb-finish-levelup-about b, .bb-finish-skill-about b { font-weight: 600; }
/* What is still coming, under the rank's line: an LCD note, its lead in amber like the card's own accents. */
/* The New skill card is the rank card in the skill's own rank color (the Career face's colors, [data-rank]). */
.bb-finish-skill-head { color: var(--bb-skill-hi, #3fe0e0); }
/* The two cards told the same story in the same shape (Mike, 2026-09-18). A rank now closes on the ladder of ranks —
   where you have been, where you are, what is ahead — and a skill on its own rank's slots, the new one lit. The skill
   card is the smaller of the two: a skill token rather than the big rank seal, and a title to match. */
.bb-finish-skill-who { display: flex; align-items: center; gap: 14px; }
.bb-finish-skill-name { color: #e8eff3; font-size: 21px; font-weight: 700; line-height: 1.1; letter-spacing: -.4px; }
/* The Beat kept card is the share moment (Mike, 2026-10-24): name, tempo, the score large, then the ask over its keys. */
/* Challenge wears the share mark; Continue beside it is the quiet one, an outlined word with no key plate. */
.bb-finish-keys .bb-dialog-action svg { width: 16px; height: 16px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.bb-finish-keys .bb-dialog-action svg .bb-led { fill: currentColor; stroke: none; }
.bb-dialog-action[data-tone="quiet"] { border: 1px solid #5b6063; background: none; box-shadow: none; color: #9aa7ae; }
.bb-dialog-action[data-tone="quiet"]::after { display: none; }
/* What the next stage brings, on the card for the stage just cleared: the name in amber over its one line. */
/* Start over is the only key in the machine that throws work away, so it is the only red one. */
.bb-career-reset { color: #ff6b6b; border-color: #6d3a3a; }
.bb-finish-black + .bb-finish-error { margin-top: 12px; }
/* A Scores row is a key back to its level, but it wears the list's face, not a machine key's. */
#bb-pairs .bb-career-row-key { appearance: none; display: flex; width: 100%; margin: 0; padding: 10px 0; border: 0; border-bottom: 1px solid #2a3034; border-radius: 0; background: none; box-shadow: none; color: inherit; font: inherit; text-align: left; cursor: pointer; transform: none; }
#bb-pairs .bb-career-row-key:last-of-type { border-bottom: 0; }
#bb-pairs .bb-career-row-key:hover, #bb-pairs .bb-career-row-key:focus-visible { background: rgba(255, 196, 106, .08); outline: none; }
.bb-career-row-main { display: grid; flex: 1; min-width: 0; }
.bb-career-row-main > * { display: block; }
/* A long face scrolls inside the LCD, never taking the whole device with it (Mike, 2026-09-19). The LCD fills the
   panel to its bottom edge: the panel is the machine's height and the flex chain bounds the list, so a fixed cap
   only left bare metal under a short list on a tall phone (Mike, 2026-09-21). */
.bb-career-lcd.bb-career-list, .bb-career-lcd.bb-career-scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: #3a4348 #101214; overscroll-behavior: contain; }
/* The heading holds the top of the list while it scrolls, so it has to be solid and it has to end in a line: over a
   gradient with no edge, the row passing under it read as a broken row (Mike, 2026-09-19). */
.bb-career-lcd h2 { position: sticky; top: 0; z-index: 2; margin: -16px -16px 6px; padding: 16px 16px 8px; border-bottom: 1px solid #263038; background: #141b21; }
/* The rules are one long read, not a list with a live heading: a section title that sticks to the top of the LCD
   floats over the very text it names (Mike, 2026-09-20: "not a floating header"). Rules headings sit still. This has
   to out-weigh the sticky rule above it, which lands later in the file with the same specificity. */
.bb-career-lcd.bb-career-rules h2 { position: static; z-index: auto; margin: 18px 0 6px; padding: 0; border-bottom: 0; background: none; }
.bb-career-lcd.bb-career-rules h2:first-child { margin-top: 0; }
@media (min-width: 960px) {
  /* A closed drawer is out of sight under the machine; only its handle shows. */
  .bb-tray:not(.bb-open) { visibility: hidden; }
  .bb-tray:not(.bb-open) .bb-tray-handle { visibility: visible; }
}
/* Chromatic: the pitch lane under each bar of the grid. A cell per step, the semitones only where a hit sits, drag up
   or down to tune it and tap to put it back to the packaged note (Mike, 2026-09-21, picking the lane over a popover
   keyboard and a piano roll). It reads as a readout rather than a key: no chrome, no press, the number is the value.
   Just the numbers, tucked under the beats (Mike, 2026-09-21: "put them just below the beats in the pattern"). */
/* Each half and its numbers are one group, so the numbers can only ever sit under their own eight cells: as a sibling in
   the bar grid the lane landed beside the beats on a wide screen (Mike, 2026-09-21, screenshot). */
.bb-beat-half-group { min-width: 0; }
.bb-pitch-lane { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 0 4px; margin-top: 1px; }
.bb-pitch-cell {
  height: 22px; padding: 0; border: 0; border-radius: 0; background: none;
  color: #3d474c; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; line-height: 22px;
  font-variant-numeric: tabular-nums; touch-action: none;
}
.bb-pitch-cell.bb-pitch-on { color: #5ee06a; }
.bb-pitch-cell:not(:disabled) { color: #5ee06a80; }
.bb-pitch-cell.bb-pitch-on:not(:disabled) { color: #5ee06a; }
.bb-pitch-cell:disabled { cursor: default; }

/* A landed Swap tile pulses every few seconds until the first swap is made, so it reads as something to tap. */
.bb-pad.bb-swap-new .bb-token { animation: bb-swap-nudge 3s ease-in-out infinite; }
@keyframes bb-swap-nudge { 0%, 70%, 100% { transform: scale(1); } 80% { transform: scale(1.12); box-shadow: 0 0 14px 4px #fff, 0 3px 0 #0c0d0e; } 90% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .bb-pad.bb-swap-new .bb-token { animation: none; box-shadow: 0 0 10px 3px #fff; } }
/* When a swap is the only move left its tile flashes, fast and bright, over the first-swap nudge (Mike, 2026-10-10:
   "I don't know where the move is, flash that too"). */
/* The token alone flashing read too subtle (Mike, 2026-10-10): the whole pad lights amber and the token jumps. */
/* Louder since 2026-10-18 (Mike: "the swap doesn't flash too much anymore when you're down to swap being the only thing
   left"); then "super aggressive. Maybe something in the middle"): the pad swells to amber and back, the tile with it. */
.bb-pad.bb-swap-stuck { z-index: 6; animation: bb-swap-stuck-pad .8s ease-in-out infinite; }
.bb-pad.bb-swap-stuck .bb-token { animation: bb-swap-stuck .8s ease-in-out infinite; }
@keyframes bb-swap-stuck-pad { 0%, 100% { box-shadow: 0 0 6px 2px #ffb04a55; } 45% { background: #ffb04a; box-shadow: inset 0 0 0 3px #fff1d6, 0 0 24px 9px #ffb04ad0; } }
@keyframes bb-swap-stuck { 0%, 100% { transform: scale(1); } 45% { transform: scale(1.22); box-shadow: 0 0 20px 7px #fff, 0 3px 0 #0c0d0e; } }
@media (prefers-reduced-motion: reduce) { .bb-pad.bb-swap-stuck, .bb-pad.bb-swap-stuck .bb-token { animation: none; } .bb-pad.bb-swap-stuck { background: #ffb04a; box-shadow: inset 0 0 0 3px #fff1d6, 0 0 18px 6px #ffb04acc; } }
/* With the hold slot in it the lane tightens (narrower flow dots and gaps) so the slot fits a phone without clipping. */
.bb-queue .bb-lane:has(.bb-lane-hold) { gap: 5px; }
.bb-queue .bb-lane:has(.bb-lane-hold) .bb-flow { width: 12px; }
/* The New tiles card, between the stage card and the level screen: a row a tile, name, points and one line, the tile
   on the right. */
.bb-new-item { display: flex; align-items: center; gap: 14px; margin-top: 14px; }
.bb-new-item:first-child { margin-top: 4px; }
.bb-new-text { flex: 1; }
.bb-new-name { color: #e8eff3; font-weight: 700; font-size: 17px; }
.bb-new-points { margin-left: 6px; color: #ffc46a; font: 700 13px/1 var(--bb-readout); }
.bb-new-line { color: #aebbc3; font-size: 14px; line-height: 1.35; }
.bb-new-item .bb-token { flex: none; width: 48px; height: 46px; }
/* The level screen's foot: the set's name with its board's rule under it on the left (the kit's name until 2026-10-12: the
   kit is always the groove's own), the kit meter on the right while the kit loads. */
.bb-intro-foot { order: 2; display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.bb-intro-foot-stage { min-width: 0; }
/* The set's last level wears a chip beside the set's name: the castle door (the four-beats review, 2026-10-24). */
.bb-intro-foot-label { margin-top: 5px; color: #8fa3b0; font: 600 11px/1.25 var(--bb-readout); letter-spacing: .2px; }
.bb-finish-foot-label[data-short] { color: #ffc46a; }
/* The run's tiles sit up top in the band under the BPM, one row across the screen above the divider, so three of them
   never wrap beside a long kit name (Mike, 2026-09-25: "hope the special tiles don't wrap", then "up top, under BPM"). */
.bb-intro-deck { margin: 14px 0 0; }
.bb-intro-deck-tiles { display: flex; flex-wrap: nowrap; gap: 14px; }
.bb-intro-deck-tile { display: flex; align-items: center; gap: 4px; color: #cfd6da; font: 600 10px/1 var(--bb-readout); letter-spacing: .6px; text-transform: uppercase; }
.bb-intro-deck-tile .bb-token { width: 24px; height: 23px; border-radius: 3px; box-shadow: none; }
.bb-intro-deck-tile .bb-token::after { display: none; }
.bb-intro-deck-tile .bb-symbol { width: 17px; height: 16px; }
.bb-stage-tiles { margin-top: 6px; }

/* Upright only (Mike, 2026-09-26: "lock orientation to vertical"). A web page can't lock the rotation, so a phone held
   sideways sees this instead of a squashed machine; app.mjs pauses a run when it turns. */
@media (orientation: landscape) and (max-height: 520px) {
  body > * { visibility: hidden; }
  body::after { content: 'Turn your phone upright'; position: fixed; inset: 0; z-index: 10; display: grid; place-items: center; visibility: visible; color: #e8eff3; font: 600 17px/1.4 'Helvetica Neue', Helvetica, Arial, sans-serif; letter-spacing: -.2px; }
}

/* The level screen's and the end card's keys are the front's transport keys (Mike, 2026-10-05). The lead key keeps its
   amber underglow and nothing else of the dark key; a long Next name ellipses rather than widening the key. */
.bb-dialog .bb-transport-key { min-height: 54px; padding: 7px 6px 5px; }
.bb-dialog .bb-transport-key .bb-key-legend { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.bb-intro-start.bb-transport-key, .bb-finish-primary.bb-transport-key, .bb-intro-start.bb-transport-key:disabled, .bb-finish-primary.bb-transport-key:disabled { border: 1px solid #9eadb6; border-bottom-color: #5c6f7b; color: #2e4555; text-shadow: none; box-shadow: 0 5px 0 #7d8f9b, 0 6px 0 1px #3f4c54, 0 9px 8px #0007, inset 0 1px 0 #f7fafc, inset 0 -1px 0 #ffffff40; }
.bb-intro-start.bb-transport-key, .bb-finish-primary.bb-transport-key { min-height: 60px; }
.bb-intro-start.bb-transport-key .bb-key-legend, .bb-finish-primary.bb-transport-key .bb-key-legend { font-size: 11px; }
.bb-intro-start.bb-transport-key:not(:disabled):active, .bb-finish-primary.bb-transport-key:not(:disabled):not([data-locked]):active { box-shadow: 0 1px 0 #7d8f9b, 0 2px 0 1px #3f4c54, 0 3px 4px #0006, inset 0 1px 0 #f7fafc; }
.bb-intro-start.bb-transport-key::before, .bb-finish-primary.bb-transport-key::before { bottom: -9px; }
.bb-finish-keys .bb-transport-key[data-tone="teal"] { color: inherit; }
/* The level screen is one row of four (Mike, 2026-10-05): Jam · Play · Next · Eject, one size, bigger than the front
   row's. The amber underglow sits under the lead key, Play or Next. */
.bb-intro-keys:has(> .bb-transport-key) { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 22px 0 6px; }
.bb-intro-keys > .bb-transport-key, .bb-intro-keys > .bb-intro-start.bb-transport-key { width: auto; min-height: 66px; margin: 0; gap: 5px; }
.bb-intro-keys > .bb-transport-key .bb-transport-icon { width: 24px; height: 24px; }
.bb-intro-keys > .bb-transport-key .bb-key-legend { font-size: 10px; }
.bb-intro-keys > .bb-transport-key::before { display: none; }
/* On the metal a held key reads near-black and the lead key glows white, on both runs (Mike, 2026-10-10: the orange "really clashes"). */
.bb-intro-keys > .bb-transport-key[data-lead], .bb-intro-start.bb-transport-key, .bb-finish-primary.bb-transport-key { --coin-rgb: 255, 250, 235; }
.bb-intro-keys > .bb-transport-key[data-lead]:not(:disabled)::before { display: block; content: ''; position: absolute; left: 3px; right: 3px; bottom: -9px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, rgba(var(--coin-rgb), .12), rgba(var(--coin-rgb), .82) 30%, rgba(var(--coin-rgb), .82) 70%, rgba(var(--coin-rgb), .12)); box-shadow: 0 5px 6px rgba(var(--coin-rgb), .3), 0 10px 10px rgba(var(--coin-rgb), .12); pointer-events: none; }

/* The kit loading on the level screen's LCD: a row of segments beside the kit's name, lit in the drum bar's green as
   the files land (Mike, 2026-10-07). */
.bb-kit-meter { display: flex; gap: 3px; align-self: center; }
.bb-kit-meter[hidden] { display: none; }
.bb-kit-meter i { width: 6px; height: 14px; border-radius: 1.5px; background: #2c3a41; box-shadow: inset 0 0 0 1px #1f2a30; transition: background .15s, box-shadow .15s; }
.bb-kit-meter i.bb-lit { background: #9ce7c9; box-shadow: 0 0 4px #9ce7c980; }


/* Set 1's lesson pictures (app.mjs lessonPicture): the tiles a move connects, on the right of the card where a tile card
   shows its tile, drawn at the board's size and zoomed down. The pair and the drum play once a loop; the shapes card
   plays its L, then its diagonal, each half the loop. */
/* Lesson pictures (app.mjs lessonPicture). Goal and Connect are still and the size of a tile (Mike, 2026-10-22: "have
   them all the same size"). Connect three and Drop a drum play their move out on a loop, bigger (Mike, 2026-10-24: "this
   is a place where that animation would be nice"): the L, then the diagonal, each half the loop; the pair under the drum
   clears, then the drum drops. The Reverse arrow turns. */
.bb-lesson { flex: none; display: grid; place-items: center; width: 48px; height: 46px; --bb-lesson-time: 1.46s; }
.bb-lesson-board { grid-area: 1 / 1; display: grid; grid-auto-columns: 48px; grid-auto-rows: 46px; gap: 6px; }
.bb-lesson-match .bb-lesson-board { zoom: .47; }
.bb-lesson-board .bb-token { z-index: 1; width: auto; height: auto; margin-bottom: 3px; }
.bb-lesson-board .bb-pad { z-index: 2; aspect-ratio: auto; }
.bb-lesson-shapes, .bb-lesson-drop, .bb-lesson-chain { width: auto; height: auto; }
/* Chain (1.4): the Doubles pair and clear, the Add falls onto the Add, and that pair clears. */
.bb-lesson-chain { zoom: .55; --bb-lesson-time: 3s; }
.bb-lesson-chain .bb-lesson-board { grid-template-columns: repeat(3, 48px); grid-template-rows: repeat(2, 46px); animation: bb-lesson-loop var(--bb-lesson-time) infinite; }
.bb-lesson-first { animation: bb-lesson-first var(--bb-lesson-time) infinite; }
.bb-lesson-fall { animation: bb-lesson-fall var(--bb-lesson-time) infinite; }
.bb-lesson-second { animation: bb-lesson-second var(--bb-lesson-time) infinite; }
@keyframes bb-lesson-first { 0%, 16% { opacity: 1; transform: none; filter: none; } 20%, 28% { opacity: 1; transform: none; filter: brightness(1.45) drop-shadow(0 0 6px rgb(var(--pop-rgb))); } 36%, 92% { opacity: 0; transform: scale(.6); } 94%, 100% { opacity: 1; transform: none; filter: none; } }
@keyframes bb-lesson-fall { 0%, 38% { opacity: 1; transform: none; filter: none; } 46%, 54% { opacity: 1; transform: translateY(52px); filter: none; } 58%, 66% { opacity: 1; transform: translateY(52px); filter: brightness(1.45) drop-shadow(0 0 6px rgb(var(--pop-rgb))); } 74%, 92% { opacity: 0; transform: translateY(52px) scale(.6); } 94%, 100% { opacity: 1; transform: none; filter: none; } }
@keyframes bb-lesson-second { 0%, 54% { opacity: 1; transform: none; filter: none; } 58%, 66% { opacity: 1; transform: none; filter: brightness(1.45) drop-shadow(0 0 6px rgb(var(--pop-rgb))); } 74%, 92% { opacity: 0; transform: scale(.6); } 94%, 100% { opacity: 1; transform: none; filter: none; } }
.bb-lesson-shapes { zoom: .55; --bb-lesson-time: 4s; }
.bb-lesson-drop { zoom: .7; --bb-lesson-time: 1.83s; }
.bb-lesson-shapes .bb-lesson-board { grid-template-columns: repeat(3, 48px); grid-template-rows: repeat(3, 46px); animation: bb-lesson-half var(--bb-lesson-time) infinite; }
.bb-lesson-drop .bb-lesson-board { animation: bb-lesson-loop var(--bb-lesson-time) infinite; }
.bb-lesson-hit { animation: bb-lesson-hit var(--bb-lesson-time) infinite; }
.bb-lesson-shapes .bb-lesson-hit { animation-name: bb-lesson-shape; }
.bb-lesson-board .bb-lesson-drum { animation: bb-lesson-drum var(--bb-lesson-time) infinite; }
.bb-lesson-shapes .bb-lesson-board + .bb-lesson-board, .bb-lesson-shapes .bb-lesson-board + .bb-lesson-board * { animation-delay: calc(var(--bb-lesson-time) / -2); }
@keyframes bb-lesson-loop { 0%, 88.4% { opacity: 1; } 91.1%, 94.5% { opacity: 0; } 100% { opacity: 1; } }
@keyframes bb-lesson-hit { 0%, 27.4% { opacity: 1; transform: none; filter: none; } 32.9%, 41.1% { opacity: 1; transform: none; filter: brightness(1.45) drop-shadow(0 0 6px rgb(var(--pop-rgb))); } 54.8%, 91.8% { opacity: 0; transform: scale(.6); } 93.2%, 100% { opacity: 1; transform: none; filter: none; } }
@keyframes bb-lesson-drum { 0%, 54.8% { transform: none; } 63%, 91.8% { transform: translateY(52px); } 93.2%, 100% { transform: none; } }
@keyframes bb-lesson-half { 0%, 46% { opacity: 1; } 50%, 96% { opacity: 0; } 100% { opacity: 1; } }
@keyframes bb-lesson-shape { 0%, 12% { opacity: 1; transform: none; filter: none; } 18%, 26% { opacity: 1; transform: scale(1.05); filter: brightness(1.45) drop-shadow(0 0 6px rgb(var(--pop-rgb))); } 34%, 50% { opacity: 0; transform: scale(.6); } 52%, 100% { opacity: 1; transform: none; filter: none; } }
.bb-lesson-turn { place-items: center; color: #ffc46a; font-size: 40px; line-height: 1; animation: bb-lesson-turn 2s linear infinite; }
@keyframes bb-lesson-turn { from { transform: rotate(0); } to { transform: rotate(-360deg); } }
/* Without motion each card holds its move, the shapes card its L. */
@media (prefers-reduced-motion: reduce) {
  .bb-lesson *, .bb-lesson-board { animation: none !important; }
  .bb-lesson-shapes .bb-lesson-board + .bb-lesson-board { display: none; }
}
/* Gear (career.mjs GEAR): the Career face's Gear view, the shut tool's card and the closing card's drawing. */
.bb-gear-view .bb-career-next-who { align-items: center; gap: 12px; }
.bb-gear-art { flex: none; display: block; width: 52px; height: 38px; }
.bb-gear-art svg { display: block; width: 100%; height: 100%; }
.bb-gear-big { width: 56px; height: 40px; }
.bb-gear-view .bb-career-next-count { flex: none; }
.bb-gear-buy { min-height: 44px; padding: 0 12px; white-space: nowrap; font-size: 13px; }
.bb-gear-list { display: flex; flex-direction: column; padding: 10px 16px 0; }
.bb-gear-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding-bottom: 6px; border-bottom: 1px solid #2a3034; }
.bb-gear-title { color: #ffd183; font: 700 9px/1.4 var(--bb-readout); letter-spacing: 1.4px; text-transform: uppercase; }
.bb-gear-wallet { color: #ffe16b; font-size: 16px; font-weight: 500; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
.bb-gear-wallet small { margin-left: 5px; color: #7d8990; font-size: 10px; font-weight: 400; letter-spacing: 0; }
.bb-gear-rows { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; margin: 0 -16px; padding: 0 16px 4px; }
.bb-gear-row { appearance: none; display: flex; align-items: center; gap: 10px; width: 100%; min-height: 52px; margin: 0; padding: 6px 0; border: 0; border-bottom: 1px solid #2a3034; border-radius: 0; background: none; box-shadow: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.bb-gear-row:last-child { border-bottom: 0; }
.bb-gear-row[aria-pressed="true"] { width: calc(100% + 16px); margin: 0 -8px; padding: 6px 8px; border-radius: 4px; border-bottom-color: transparent; background: rgba(255, 196, 106, .08); box-shadow: inset 0 0 0 1px rgba(255, 196, 106, .45); }
.bb-gear-main { display: flex; flex: 1; flex-direction: column; gap: 1px; min-width: 0; }
.bb-gear-name { color: #e8eff3; font-size: 14px; font-weight: 600; line-height: 1.2; letter-spacing: -.2px; }
.bb-gear-note { color: #7d8990; font-size: 10px; line-height: 1.3; }
.bb-gear-price { flex: none; min-width: 54px; text-align: right; }
.bb-gear-cost { display: inline-block; color: #9aa7ae; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.bb-gear-togo { display: block; margin-top: 3px; color: #66727a; font-size: 9px; }
.bb-gear-togo:empty { display: none; }
.bb-gear-row[data-state="ready"] .bb-gear-cost { padding: 7px 9px; border: 1px solid rgba(255, 209, 131, .5); border-top-color: rgba(255, 209, 131, .7); border-bottom-color: #111416; border-radius: 4px; background: linear-gradient(145deg, #36393b, #202325 78%); color: #ffc46a; text-shadow: 0 0 5px rgba(255, 209, 131, .35); box-shadow: 0 2px 0 #0c0d0e, 0 3px 0 rgba(255, 209, 131, .6), inset 0 1px 1px #ffffff2e; }
.bb-gear-row[data-state="owned"] .bb-gear-cost { color: #3fe0e0; font-size: 15px; }
.bb-gear-row[data-state="owned"] .bb-gear-togo { color: #3fe0e0aa; }
.bb-gear-row[data-state="short"] .bb-gear-art, .bb-gear-row[data-state="soon"] .bb-gear-art, .bb-gear-row[data-state="closed"] .bb-gear-art { filter: grayscale(1); opacity: .42; }
.bb-gear-row[data-state="short"] .bb-gear-name, .bb-gear-row[data-state="soon"] .bb-gear-name, .bb-gear-row[data-state="closed"] .bb-gear-name { color: #9aa7ae; }
.bb-skill-card > .bb-gear-art { margin-top: 4px; }
.bb-gear-open { min-height: 36px; padding: 8px 10px; font-size: 12px; text-align: left; }
.bb-gear-open:focus-visible, .bb-gear-buy:focus-visible, .bb-gear-row:focus-visible { outline: none; }
/* The Lock pad (Locked In from 5.3): an empty top-row pad where whatever lands locks. A grey lock, nothing else. */
.bb-lock-pad-mark { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
.bb-lock-pad-mark svg { width: 46%; height: 46%; fill: none; stroke: #6f7579; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* A level's stars (app.mjs starMark): cleared, full kit, closed board, lit amber, the ones still to earn dim. */
.bb-stars { flex: none; margin-left: .35em; color: #ffc46a; font-weight: 400; letter-spacing: .05em; white-space: nowrap; }
/* A level's stars on the LCD (app.mjs lcdStars), bottom right across from the set: won ones yellow and lit, the next
   one a replay can win outlined and blinking, the rest dim. On the end card the won ones pop in one at a time. */
.bb-lcd-stars { display: flex; gap: 2px; flex: none; font-size: 26px; line-height: 1; }
.bb-lcd-stars i { display: inline-block; font-style: normal; color: #34434b; }
.bb-lcd-stars i.bb-won { color: #ffd23f; text-shadow: 0 0 8px #ffd23f99; }
.bb-lcd-stars i.bb-chase { color: #ffd23f; animation: bb-star-blink 1.1s ease-in-out infinite; }
.bb-lcd-stars.bb-pop-in i.bb-won { animation: bb-star-pop .5s cubic-bezier(.2,1.6,.4,1) both; animation-delay: calc(.35s + var(--bb-k) * .3s); }
@keyframes bb-star-pop { 0% { transform: scale(0) rotate(-45deg); opacity: 0; } 70% { transform: scale(1.4); opacity: 1; } 100% { transform: scale(1); } }
@keyframes bb-star-blink { 50% { opacity: .15; } }
.bb-lib-top .bb-stars { margin: 0 .4em 0 0; align-self: center; font-size: 11px; }
.bb-map-level .bb-stars { margin: 0; font-size: 10px; }

/* The end card (Mike, 2026-10-25: "the end card that mirrors the start card"): the level screen's LCD with the run on it.
   Two big numbers side by side on the right, a little word under each: the pads in green (amber on a miss, as "7/11"),
   the score in yellow (Mike's mock-up A, 2026-10-28: "big number, little subtext ... pads in green, score in yellow").
   A challenge's score keeps its "to beat" line in the met or short color. */
.bb-finish-readout .bb-groove, .bb-finish-readout .bb-groove:has(.bb-intro-pads:not([hidden])) { grid-template-columns: minmax(0, 1fr) auto auto; grid-template-rows: auto; column-gap: 22px; align-items: end; }
.bb-finish-readout .bb-mode { grid-row: auto; grid-column: 1; }
.bb-finish-readout .bb-finish-pads, .bb-finish-readout .bb-finish-money { grid-row: auto; grid-column: auto; gap: 5px; }
.bb-finish-readout .bb-finish-pads .bb-score-value, .bb-finish-readout .bb-finish-score { font-size: clamp(24px, 6vw, 36px); white-space: nowrap; }
.bb-finish-readout .bb-finish-pads, .bb-finish-readout .bb-finish-pads .bb-score-value, .bb-finish-readout .bb-finish-pads .bb-lcd-note { color: #5ee06a; }
.bb-finish-readout .bb-finish-pads[data-met="false"], .bb-finish-readout .bb-finish-pads[data-met="false"] .bb-score-value, .bb-finish-readout .bb-finish-pads[data-met="false"] .bb-lcd-note { color: #ffc46a; }
.bb-finish-readout .bb-finish-money, .bb-finish-readout .bb-finish-score, .bb-finish-readout .bb-finish-target { color: #ffc46a; }
.bb-finish-readout .bb-finish-score[data-met="true"], .bb-finish-readout .bb-finish-score[data-met="true"] + .bb-finish-target { color: #5ee06a; }
/* Its keys are the level screen's row (.bb-intro-keys): four silver keys in four places. The black cards keep the black
   keys: Continue alone, or Challenge beside a quiet Continue on the Dare. */
.bb-finish-dialog[data-keys="1"] .bb-finish-keys { grid-template-columns: 1fr; }
.bb-finish-keys .bb-dialog-action { min-height: 44px; min-width: 0; }
.bb-finish-keys .bb-transport-key[data-tone="teal"] { color: inherit; }
/* Set cleared: the four songs you played, by name, each with its stars, big enough to read as the set's record. */
.bb-finish-stage-levels { display: grid; gap: 12px; margin-top: 22px; }
.bb-finish-stage-level { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: baseline; gap: 10px; width: 100%; margin: 0; padding: 6px 8px; border: 0; border-radius: 6px; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.bb-finish-stage-level:hover, .bb-finish-stage-level:focus-visible { background: #1a2024; outline: none; }
.bb-finish-stage-level:active { background: #222a2f; }
.bb-finish-stage-levels { margin-left: -8px; margin-right: -8px; }
.bb-finish-stage-track { color: #5c6b74; font: 600 12px/1 var(--bb-readout); }
.bb-finish-stage-song { overflow: hidden; color: #e8eff3; font-size: 17px; line-height: 1.2; font-weight: 600; letter-spacing: -.2px; text-overflow: ellipsis; white-space: nowrap; }
.bb-finish-stage-stars { color: #ffc46a; font-size: 18px; line-height: 1; letter-spacing: .12em; white-space: nowrap; }
.bb-finish-stage .bb-finish-black-body { padding-bottom: 24px; }
/* The Dare (round three, V1): the number, the song, the target, a teal rule, one plain line; Challenge a friend wide beside
   a quiet Continue. The lead key is teal, the card's own colour (amber is the chase colour and fought it). */
.bb-finish-dare .bb-finish-black-body { padding: 24px 18px 22px; }
.bb-finish-dare-num { color: #3fe0e0; font: 700 56px/1 var(--bb-readout); letter-spacing: -2.5px; font-variant-numeric: tabular-nums; }
.bb-finish-dare .bb-finish-dare-song { margin-top: 6px; font-size: 22px; line-height: 1.1; letter-spacing: -.5px; }
.bb-finish-dare-note { margin-top: 8px; }
.bb-finish-dare-rule { height: 1px; margin: 20px 0 16px; background: linear-gradient(90deg, #3fe0e0, #3fe0e033 60%, transparent); }
.bb-finish-dare-ask { color: #cfd6da; font-size: 14px; line-height: 1.5; }
.bb-finish-dialog[data-keys="2"] .bb-finish-keys { grid-template-columns: 1.9fr 1fr; gap: 10px; }
.bb-finish-keys .bb-dialog-action { min-height: 48px; padding-inline: 8px; font-size: 13px; white-space: nowrap; }
.bb-finish-keys button[data-tone="teal"] { --coin-rgb: 63, 224, 224; color: #6ff0f0; }
/* The closed board (Mike, 2026-10-25: "fireworks and shit"): the sixteen pads lit gold and breathing, three stars, the
   score, and three bursts of sparks going off over the card in turn, forever. */
.bb-finish-closed .bb-finish-black-body { position: relative; overflow: hidden; padding: 24px 18px 22px; }
.bb-finish-closed-hero { position: relative; z-index: 1; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 16px; align-items: center; }
.bb-finish-closed-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; width: 112px; padding: 7px; border-radius: 5px; background: #07090a; box-shadow: inset 0 0 0 1px #23282c, 0 0 22px #ffc46a55; }
.bb-finish-closed-board i { display: block; aspect-ratio: 1; border-radius: 3px; background: #b8862f; box-shadow: 0 0 0 1px #ffc46a, 0 0 6px #ffc46a88; animation: bb-closed-breathe 2.4s ease-in-out infinite; animation-delay: calc((var(--bb-k, 0)) * .06s); }
.bb-finish-closed-board i:nth-child(1) { --bb-k: 0; } .bb-finish-closed-board i:nth-child(2), .bb-finish-closed-board i:nth-child(5) { --bb-k: 1; }
.bb-finish-closed-board i:nth-child(3), .bb-finish-closed-board i:nth-child(6), .bb-finish-closed-board i:nth-child(9) { --bb-k: 2; }
.bb-finish-closed-board i:nth-child(4), .bb-finish-closed-board i:nth-child(7), .bb-finish-closed-board i:nth-child(10), .bb-finish-closed-board i:nth-child(13) { --bb-k: 3; }
.bb-finish-closed-board i:nth-child(8), .bb-finish-closed-board i:nth-child(11), .bb-finish-closed-board i:nth-child(14) { --bb-k: 4; }
.bb-finish-closed-board i:nth-child(12), .bb-finish-closed-board i:nth-child(15) { --bb-k: 5; } .bb-finish-closed-board i:nth-child(16) { --bb-k: 6; }
@keyframes bb-closed-breathe { 0%, 100% { background: #b8862f; box-shadow: 0 0 0 1px #ffc46a, 0 0 6px #ffc46a88; } 50% { background: #ffe3a8; box-shadow: 0 0 0 1px #fff6dc, 0 0 14px #ffc46a; } }
.bb-finish-closed .bb-finish-closed-title { font-size: 28px; letter-spacing: -.8px; }
.bb-finish-closed-stars { margin-top: 6px; color: #ffc46a; font-size: 26px; line-height: 1; letter-spacing: .12em; text-shadow: 0 0 10px #ffc46a99; }
.bb-finish-closed-score { margin-top: 6px; color: #e8eff3; font: 700 17px/1.3 var(--bb-readout); }
.bb-finish-closed-ask { position: relative; z-index: 1; margin-top: 18px; padding-top: 16px; border-top: 1px solid #ffc46a33; color: #cfd6da; font-size: 14px; line-height: 1.5; }
.bb-finish-fireworks { position: absolute; inset: 0; pointer-events: none; }
.bb-firework { position: absolute; left: var(--x); top: var(--y); width: 0; height: 0; }
.bb-firework i { position: absolute; left: 0; top: -1.5px; width: 12px; height: 3px; border-radius: 2px; background: linear-gradient(90deg, transparent, var(--c)); box-shadow: 0 0 8px var(--c); opacity: 0; transform-origin: 0 50%; transform: rotate(var(--a)) translateX(0); animation: bb-firework 4s ease-out infinite; animation-delay: var(--d); }
@keyframes bb-firework {
  0% { opacity: 0; transform: rotate(var(--a)) translateX(0) scaleX(1.6); }
  3% { opacity: 1; }
  26% { opacity: 1; transform: rotate(var(--a)) translateX(var(--r)) scaleX(1); }
  40%, 100% { opacity: 0; transform: rotate(var(--a)) translateX(calc(var(--r) + 18px)) scaleX(.2); }
}
@media (prefers-reduced-motion: reduce) { .bb-finish-closed-board i { animation: none; } .bb-firework i { animation: none; opacity: .7; transform: rotate(var(--a)) translateX(var(--r)); } }
/* The stars card: once, at the first full kit, the third star explained with the board it asks for. */
.bb-finish-stars .bb-finish-black-body { padding: 22px 18px 20px; }
.bb-finish-stars .bb-finish-stars-title { font-size: 28px; letter-spacing: -.8px; }
.bb-finish-stars-big { margin-left: 4px; color: #ffc46a; font-size: 22px; font-weight: 400; letter-spacing: .08em; vertical-align: 2px; }
.bb-finish-stars-hero { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 16px; align-items: center; margin-top: 18px; }
.bb-finish-stars-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; width: 112px; padding: 7px; border-radius: 5px; background: #07090a; box-shadow: inset 0 0 0 1px #23282c, 0 0 18px #3fe0e033; }
.bb-finish-stars-board i { display: block; aspect-ratio: 1; border-radius: 3px; background: #1f6e6e; box-shadow: 0 0 0 1px #3fe0e0, 0 0 4px #3fe0e055; }
.bb-finish-stars-three { color: #ffc46a; font-size: 24px; line-height: 1; letter-spacing: .1em; }
.bb-finish-stars-three span { color: #e8eff3; font-size: 18px; font-weight: 700; letter-spacing: -.3px; vertical-align: 2px; }
.bb-finish-stars-line { margin-top: 5px; color: #e8eff3; font-size: 16px; line-height: 1.4; }
.bb-finish-dialog[data-card="stars"] .bb-finish-keys { grid-template-columns: 1.2fr 1fr; }
/* The board on the stars card shows what the third star is (Mike, 2026-10-25: "a little animation of the last one dropping
   in and filling and glowing, and the spiraling lights we do when you win; give somebody something to shoot for"): fifteen
   pads lit, the last one, top right since a board fills from the floor up, drops in and flares, then the win's light runs round the board in a spiral from it, the whole
   board glows once, and it starts again. Five seconds a round. */
.bb-finish-stars-board i:nth-child(4) { --bb-k: 0; }
.bb-finish-stars-board i:nth-child(8) { --bb-k: 1; }
.bb-finish-stars-board i:nth-child(12) { --bb-k: 2; }
.bb-finish-stars-board i:nth-child(16) { --bb-k: 3; }
.bb-finish-stars-board i:nth-child(15) { --bb-k: 4; }
.bb-finish-stars-board i:nth-child(14) { --bb-k: 5; }
.bb-finish-stars-board i:nth-child(13) { --bb-k: 6; }
.bb-finish-stars-board i:nth-child(9) { --bb-k: 7; }
.bb-finish-stars-board i:nth-child(5) { --bb-k: 8; }
.bb-finish-stars-board i:nth-child(1) { --bb-k: 9; }
.bb-finish-stars-board i:nth-child(2) { --bb-k: 10; }
.bb-finish-stars-board i:nth-child(3) { --bb-k: 11; }
.bb-finish-stars-board i:nth-child(7) { --bb-k: 12; }
.bb-finish-stars-board i:nth-child(11) { --bb-k: 13; }
.bb-finish-stars-board i:nth-child(10) { --bb-k: 14; }
.bb-finish-stars-board i:nth-child(6) { --bb-k: 15; }
.bb-finish-stars-board i { animation: bb-stars-sweep 5s linear infinite; animation-delay: calc(var(--bb-k) * .08s); }
.bb-finish-stars-board i:nth-child(4) { animation: bb-stars-last 5s ease-out infinite, bb-stars-sweep 5s linear infinite; animation-delay: 0s, 0s; }
@keyframes bb-stars-last {
  0%, 12% { background: #1c2327; box-shadow: inset 0 0 0 1px #2a3338; transform: translateY(-14px) scale(.85); opacity: 0; }
  14% { opacity: 1; }
  18% { background: #bff7f7; box-shadow: 0 0 0 1px #ffffff, 0 0 18px #9cf2f2, 0 0 36px #3fe0e0; transform: translateY(0) scale(1.12); opacity: 1; }
  26%, 90% { background: #1f6e6e; box-shadow: 0 0 0 1px #3fe0e0, 0 0 4px #3fe0e055; transform: none; opacity: 1; }
  100% { background: #1c2327; box-shadow: inset 0 0 0 1px #2a3338; transform: translateY(-14px) scale(.85); opacity: 0; }
}
@keyframes bb-stars-sweep {
  0%, 40% { background: #1f6e6e; box-shadow: 0 0 0 1px #3fe0e0, 0 0 4px #3fe0e055; }
  42% { background: #bff7f7; box-shadow: 0 0 0 1px #ffffff, 0 0 12px #9cf2f2; }
  47%, 70% { background: #1f6e6e; box-shadow: 0 0 0 1px #3fe0e0, 0 0 4px #3fe0e055; }
  74% { background: #3fe0e0; box-shadow: 0 0 0 1px #9cf2f2, 0 0 14px #3fe0e0; }
  80%, 100% { background: #1f6e6e; box-shadow: 0 0 0 1px #3fe0e0, 0 0 4px #3fe0e055; }
}
@media (prefers-reduced-motion: reduce) { .bb-finish-stars-board i, .bb-finish-stars-board i:nth-child(4) { animation: none; } }
/* Fade to Grey (Set 17; Mike, 2026-10-27): a tile that has sat three drops, the lane's back slots, and at 17.4 every tile,
   keeps its glyph and loses its color. */
.bb-token[data-grey="true"], .bb-machine[data-grey="true"] .bb-token { --coin-rgb: 150, 158, 166; --pop-rgb: 150, 158, 166; --symbol-rgb: 150, 158, 166; }
